# Cart Profile Tools

> Create, edit, preview, publish and restore cart profiles through the Storefront MCP.

Canonical URL: https://c22dfdd27662:3005/tools/cart

> **MCP v3 routing:** The operation names on this page are retained as action-contract references. Do not call them as top-level tools. Pass the former name to `lexsis_discover`, then call the returned router and action with the documented parameters inside `args`.

The Storefront MCP provides cart inspection, draft design/promotion editing, previews,
profile lifecycle management and audit history. Publishing and live resolution
changes require explicit approval. See [Cart Lifecycle Through MCP](/guides/cart-lifecycle)
for the full create → edit → preview → publish → restore workflow.

See [Preview and Verify Cart Edits](/guides/cart-edit-loop) for compact
responses, dry runs, incremental CSS and six signed sample-cart URLs.

Call `get_cart_profile` before assigning or editing a cart.

Open <AppLink to="cart">Storefront → Cart</AppLink> for profile creation,
visual previews, publishing, rollback, defaults, assignments, and history.

`lexsis_cart.capabilities` also returns the renderer-managed
`lx:cart:add-items` command contract. Search for **custom cart**, **batch
add**, **multiple products**, or **custom interaction** to find its limits,
event directions, transaction policy, errors, and lifecycle-safe example.

It also lists the frozen cart snapshot schema, eight additional shopper
commands, condition fields/operators, and declarative text binding paths.

## Custom module state and visibility

For the full snapshot schema, all condition fields, command limits, error
handling and worked source examples, see [Custom Cart Modules](/guides/custom-cart-modules).

Custom module scripts read `lifecycle.cart` or subscribe with
`lifecycle.onCart((cart, previous) => ...)`. Subscriptions receive settled
changes at most once per frame and unsubscribe when the module unmounts.
Money contains decimal `amount`, `currencyCode`, and integer `minor`, using the
same confirmed merchandise basis as the built-in summary. Currency is never
guessed; use the subscription while the initial cart loads.

Add `visible_when` to an upsert to show a custom or optional built-in module
only on a populated cart:

```json
{
  "operation": "upsert",
  "module_id": "payment-options",
  "visible_when": {
    "op": "AND",
    "clauses": [{"field": "cart.item_count", "op": "gt", "value": 0}]
  }
}
```

Use the existing module ID returned by `cart.get`. Omit `source` when changing
only an existing module's condition. Pass `visible_when: null` to clear it.
Use `cart.has_variant_id` with `neq` and the actual variant GID to hide an
upsell once it is in the cart. Hidden modules leave the accessibility tree
and reappear as cart state changes. Required cart modules cannot be hidden.

For simple gift text, no script is needed:

```html
<!-- section: gift-message -->
<section>
  <p data-lx-if="rewards.next.remaining.minor &gt; 0">
    Only <strong data-lx-text="rewards.next.remaining"></strong> to go.
  </p>
</section>
```

The compiler rejects unknown expression paths and functions. Membership
predicates such as `reward.unlocked('REWARD_ID')`, comparisons, parentheses,
`!`, `&&`, and `||` are supported. Money text uses the real currency.
Customer conditions require host session context; collection conditions
require available Shopify metadata.

### Shopper commands

All commands dispatch from `section` as bubbling events with a unique
`requestId`. Read exact payloads and limits in `lexsis_cart.capabilities.commands`.

| Event suffix after `lx:cart:` | Payload after `requestId` |
|---|---|
| `update-line` | `lineId`, `quantity` (0–99) |
| `remove-line` | `lineId` |
| `apply-code`, `remove-code` | `code` |
| `set-attributes` | `attributes` string record |
| `set-note` | `note` (empty clears) |
| `choose-gift` | Existing `rewardId`, eligible `variantId` |
| `swap-variant` | `lineId`, `variantId`, optional `quantity` |

Responses use the original event plus `:pending`, `:accepted`, `:confirmed`,
`:success`, or `:error`, and return to the originating module. Success means
settlement completed. Identical request replay does not mutate twice; changed
payloads using the same ID fail. Attribute writes preserve existing attribution.
An AJAX swap can report `partial_swap`; review the accepted cart contents before
trying another action. Shopper commands do not create or edit promotions.

## get_cart_profile

Inspect a profile draft or resolve the effective cart for a page.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `page_id` | UUID | No | Resolve the cart a shopper receives on this page |
| `cart_profile_id` | UUID | No | Fetch one editable profile |
| `store_id` | UUID | No | Optional store hint for a multi-store account |
| `include_available_profiles` | boolean | No | Include a compact profile list, defaults to `true` |
| `fields` | string[] | No | Explicit profile fields or dotted paths; unknown fields fail |
| `response` | `summary` or `full` | No | Compact metadata by default; full source is opt-in |
| `include_history` | boolean | No | Include lifecycle audit history |

Pass either `page_id` or `cart_profile_id`, not both. Passing only `store_id`
lists the available profiles.

### Resolve a page

```json
{
  "name": "get_cart_profile",
  "arguments": {
    "page_id": "7f58258d-cb47-433c-9e35-672cafdffde2"
  }
}
```

The response includes `effective_profile.resolution_source`:

- `page`
- `campaign`
- `default`
- `legacy`
- `none`

The effective profile is the published snapshot used by the renderer. An
individually fetched profile is the editable draft and includes
`has_unpublished_changes`.

## set_cart_profile

Assign a published profile to one page. A new assignment replaces the previous
page assignment.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `page_id` | UUID | Yes | Target page |
| `cart_profile_id` | UUID or `null` | Yes | Published profile, or `null` to remove the page assignment |

```json
{
  "name": "set_cart_profile",
  "arguments": {
    "page_id": "7f58258d-cb47-433c-9e35-672cafdffde2",
    "cart_profile_id": "6b36d37e-065d-409e-b15a-66dd76934a31"
  }
}
```

To restore campaign, store-default, or legacy fallback resolution:

```json
{
  "name": "set_cart_profile",
  "arguments": {
    "page_id": "7f58258d-cb47-433c-9e35-672cafdffde2",
    "cart_profile_id": null
  }
}
```

The tool returns the effective profile after the assignment change. Lexsis
then refreshes the affected published Shopify page automatically.

## edit_cart

Apply a partial update to a profile draft. The tool never publishes.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `cart_profile_id` | UUID | Yes | Profile to edit |
| `store_id` | UUID | No | Optional store hint |
| `patch` | object | Yes | Partial cart configuration |
| `change_note` | string | No | Short reason for the draft change |
| `expected_version` | positive integer | Yes | Profile version returned by `get_cart_profile` |
| `dry_run` | boolean | No | Compile and preview without saving a version |
| `response` | `summary` or `full` | No | Summary by default; full profile is opt-in |
| `page_id` | UUID | No | Page on which to preview the edit |

The patch accepts:

| Field | Purpose |
|---|---|
| `cart_mode` | Drawer, sheet, modal, fullscreen, or page presentation |
| `composition_ops` | Ordered `upsert`, `move`, `set_enabled`, or custom-module `remove` operations |
| `custom_css` | Replaces all custom CSS and named blocks; `null` clears it |
| `css_ops` | Ordered named block upsert/remove; cannot accompany `custom_css` |
| `design_patch` | Typed `CartDesignSpecV1` JSON Merge Patch |

The MCP edits presentation through these fields. Use
`lexsis_drafts.cart_promotions_edit` for promotion, payment and cart-rule
configuration.

`design_patch` merges recursively into the existing `design_spec`. Passing
`null` for an optional design field removes that override so the selected
preset or parent value applies again.

### Payment logos and rendering warnings

Payment logos can be configured through
`design_patch.modules.payment_options`:

```json
{
  "placement": "inside_checkout",
  "providers": ["upi", "visa", "mastercard"],
  "variant": "icons_with_labels",
  "alignment": "left"
}
```

Placement accepts `inside_checkout`, `below_checkout`, or `hidden`. Provider
order is preserved. Disabling the payment module hides its logos at either
placement. These settings change their display; they do not activate payment
methods at checkout.

Presentation edits regenerate the rendered cart. Disabled optional modules
stay hidden, including offers beside individual cart items. `shell.title`
controls both the visible title and the dialog's accessible label.

`cart_edit` returns `warnings` with `path`, `code: "setting_not_rendered"`,
and a message for stored settings that do not render. Responsive overrides
currently support tokens, drawer width, and module/state CSS. Set structural
options such as title, variant, icons, and payment placement at the base level.
The legacy gift `lock_quantity` setting and unused `warning` color token are
deprecated; confirmed gifts always follow reward quantity rules.

### Apply a visual preset and responsive overrides

```json
{
  "name": "edit_cart",
  "arguments": {
    "cart_profile_id": "6b36d37e-065d-409e-b15a-66dd76934a31",
    "expected_version": 8,
    "change_note": "Use the softer campaign cart treatment",
    "patch": {
      "design_patch": {
        "schema_version": 1,
        "preset_id": "soft-beauty",
        "tokens": {
          "colors": {
            "accent": "#8F4A5B",
            "surface_alt": "#FBF7F5"
          },
          "spacing": {
            "density": "spacious"
          }
        },
        "modules": {
          "cart_lines": {
            "variant": "editorial",
            "image_aspect": "portrait",
            "free_gift": {
              "variant": "gift_card",
              "badge_text": "A gift for you"
            }
          }
        },
        "responsive": {
          "mobile": {
            "tokens": {
              "spacing": {
                "density": "comfortable"
              }
            },
            "shell": {
              "width": "standard"
            }
          }
        }
      }
    }
  }
}
```

Supported presets are `clean-default`, `soft-beauty`, and
`compact-utility`. Design patches can control colors, typography, shape,
spacing, shell layout, cart lines, rewards, shipping, coupons, offers, order
summary, checkout, payment options, and the empty state.

### Update design and free shipping

```json
{
  "name": "edit_cart",
  "arguments": {
    "cart_profile_id": "6b36d37e-065d-409e-b15a-66dd76934a31",
    "expected_version": 8,
    "change_note": "Use the campaign cart treatment",
    "patch": {
      "cart_mode": "drawer-right",
      "commerce_config": {
        "free_shipping_threshold": 7500,
        "free_shipping_celebration": true,
        "cart_style": {
          "width": "440px",
          "responsive": {
            "mobile": "bottom-sheet"
          },
          "line_spacing": "comfortable"
        }
      }
    }
  }
}
```

Money thresholds use the store currency and are expressed in minor units. For
example, `7500` is 75.00 for a two-decimal currency.

### Configure an offer

```json
{
  "name": "edit_cart",
  "arguments": {
    "cart_profile_id": "6b36d37e-065d-409e-b15a-66dd76934a31",
    "expected_version": 8,
    "patch": {
      "commerce_config": {
        "offer_slots": [
          {
            "id": "pairs-well",
            "placement": "after_line",
            "source": "shopify_recommendations",
            "recommendation_intent": "COMPLEMENTARY",
            "heading": "Pairs well with",
            "trigger_product_ids": [],
            "recommend_product_ids": [],
            "max_items": 1,
            "enabled": true
          }
        ]
      }
    }
  }
}
```

### Add scoped CSS

```json
{
  "name": "edit_cart",
  "arguments": {
    "cart_profile_id": "6b36d37e-065d-409e-b15a-66dd76934a31",
    "expected_version": 8,
    "patch": {
      "custom_css": "[data-part=\"panel\"] { border-radius: 0; }\n[data-part=\"checkout\"] { font-weight: 600; }"
    }
  }
}
```

The CSS is scoped to the profile's cart root. It cannot alter the rest of the
page. External imports, external URLs, script escapes, and unbalanced rules are
rejected.

## Publishing

`edit_cart` changes only the draft. The default response reports the new
`version`, `updated_fields`, `warnings`, `preview_url` and `readiness_summary`.
Use `response: "full"` for `profile`, `publish_required` and compiler metadata.
Review the preview
and explicitly approve `lexsis_live_ops.cart_publish` for the latest version.

If `expected_version` is stale, reload the profile and reapply the intended patch
to the latest draft rather than overwriting another edit.

Publishing a profile refreshes affected published Shopify pages. Rollback
creates a new draft; it requires a separate approved publish.

This boundary prevents an agent edit from changing a shopper-facing cart
without merchant review.

## Related

- [Cart Profiles](/guides/cart-v2)
- [Page Management](/tools/page-management)
- [Cart Islands](/islands/cart)

## Patch semantics and module IDs

`design_patch` is a JSON Merge Patch: omitted fields remain, null removes a
stored override, arrays replace arrays. Defaults can apply when an override
is removed. `custom_css` replaces all profile CSS, including named blocks;
null clears it. Use `css_ops` for incremental named-block edits.
A source-bearing composition `upsert` replaces the entire module source,
including CSS and script; omitting source preserves it.

Rendered `data-module` equals the layout ID, including `offer-<uuid>`.
`data-module-type` contains the layout type, for example `product_offer`.
Older generated sections use `data-module="product-offer"`; save/recompile a
draft to adopt the consistent ID before targeting it. Layout enabled flags
are authoritative and offer-slot flags are mirrored. Disabled modules are
absent; conditional modules use `hidden` while their predicate is false.
See [Cart Styling Hooks](/guides/cart-styling).

`lexsis_cart.promotions` accepts `{"cart_profile_id":"PROFILE_UUID"}` without
`store_id`. An explicit store remains an optional scope; store-only calls list
Shopify discounts without profile-specific readiness.
