Table of Contents

Create a sofa product with MCP

This tutorial demonstrates the safe MCP call sequence for creating and refining a sofa product. It focuses on workflow and transfer-document structure; use asset searches and canonical exports from your own tenant instead of copying identifiers from another library.

1. Select the target library

Call list_product_contexts with a tenant or library name search. Pass the selected tenantId and libraryId to select_product_context, and retain the returned contextHandle until its expiresAt time.

2. Find sofa assets

Call list_static_assets with type set to the required asset type and a narrow search such as seat, leg, or sofa. Keep each returned transferReference object intact:

{
  "$ref": "00000000-0000-0000-0000-000000000000",
  "fileName": "seat.gltf"
}

The placeholder above represents the shape only. Use the actual reference returned for the selected tenant. Do not use an asset from another tenant and do not reduce the object to a bare GUID.

3. Create one atomic root

Submit one top-level Product and its inline child definitions in a single create_product call. This tested minimal structure creates a sofa root and seat part together:

{
  "Sofa": {
    "_type": "Product",
    "DisplayName": { "en-US": "Modular sofa" },
    "Child": { "$ref": "#/Seat" }
  },
  "Seat": {
    "_type": "Part",
    "DisplayName": { "en-US": "Seat module" },
    "ThreeDModelAsset": {
      "$ref": "REPLACE_WITH_RETURNED_REVISION_ID",
      "fileName": "seat.gltf"
    }
  }
}

Replace the asset reference with the exact transferReference returned by list_static_assets. The create result returns the root permanent ID, all created item IDs, the canonical document, and its revision.

Nested creation is atomic: a semantically invalid child or asset reference prevents the whole create operation. Creating every child in a separate call is unnecessary.

4. Inspect the canonical result

Call get_product_hierarchy with the created root ID. Then call get_product_document; use this canonical document as the basis for edits. It shows the property names and shapes accepted for the actual product item types in your library.

5. Review a routine edit

Change the sofa display name with a patch:

[
  {
    "op": "replace",
    "path": "/Sofa/DisplayName/en-US",
    "value": "Fine modular sofa"
  }
]

Pass the operations to review_product_patch. Read its differences and hazards. If review succeeds, pass the exact same operations and returned review token to apply_product_patch.

For seat dimensions, connector offsets, and leg placement, preserve array order as X, Y, Z and use meters for spatial offsets unless the specific property documentation says otherwise. Follow the right-handed coordinate system. Prefer values exported from a known-good product over guessed property names or nested shapes.

6. Add more modules safely

To add seat variants, structures, slots, or legs:

  1. Export a known-good item of the required type when one exists.
  2. Search required assets in the selected tenant.
  3. Add definitions and local $ref links with a reviewed patch.
  4. Preserve all existing child references unless removal is intentional.
  5. Apply only the reviewed operations and token.

Use full replacement only when intentionally replacing the complete authored document. If review reports destructive hazards, verify every path before acknowledging them.

7. Validate the product

Call validate_product after applying changes. This regenerates development preview data.

  • Resolve diagnostics with severity Error; these make valid false.
  • Review Warning diagnostics, especially missing thumbnail/model assets and outdated asset revisions.
  • Use affectedProductItemId, location, property fields, and remediation to target corrections.
  • Inspect idsToRefresh because integrity checks can update authored items.

Finally, call get_product_hierarchy again to confirm the intended sofa structure.

If apply returns revision_conflict, another writer advanced the library. Fetch the document again, rebuild and review the patch against fresh state, and apply the new token. A previous success must not be inferred from a failed concurrent call.