Skip to main content
@commercengine/ai exposes Commerce Engine storefront capabilities to browser AI agents through WebMCP. It works through the Storefront SDK and your existing checkout/navigation integrations rather than asking an agent to infer your UI or click DOM elements.
Add @commercengine/checkout only if you use Hosted Checkout and want drawer tools such as open_cart, open_checkout, or open_login.

The safety boundary

The package intentionally lets an agent prepare a purchase but never complete one. open_checkout hands control back to the shopper. Commerce Engine deliberately provides no payment or order-placement tool.

Quick start

Register from client code:
site and routes should come from the same defineCommerceSeoConfig() declaration used by @commercengine/seo. That keeps crawler-visible and agent-visible URLs identical.
Registration returns null when WebMCP is not available. That is an expected capability check, not a storefront error. In development, diagnostics prove the registration code actually ran.

Capability gating

Tools appear only when the capability behind them exists. Which adds up to a predictable tool count: The storefront’s browser session client is used automatically by default, discovered through its clientStorefront() or session() accessor. A storefront exposing neither registers the four catalog tools and nothing else — indistinguishable from a bug, so check the accessor name first when cart tools are missing. Tool registration does not wait for anonymous-session bootstrap: session/cart tools can be declared immediately and return a retryable not-ready result until the session becomes usable. Set session: null when you intentionally want a catalog-only agent surface.

Add Hosted Checkout and navigation

Hosted Checkout contributes presentation only. Cart reads and mutations use the authenticated Commerce Engine Storefront SDK and return the resulting cart directly.
The checkout bridge never creates a session or owns cart truth. open_cart opens the existing drawer; the drawer then refetches so it reflects any mutation the agent made through the SDK.

Cart semantics

Cart tools operate on the shopper’s real Commerce Engine cart, including anonymous shoppers.

Add versus set

  • add_to_cart adds its quantity to the current quantity. It can take up to 10 items in one invocation.
  • set_cart_item_quantity sets the absolute paid quantity for an existing cart line.
  • setting quantity to 0 removes the line
  • remove_from_cart is the explicit removal form
Do not treat add_to_cart as an absolute setter. Repeating an add can create duplicate quantity.

Ordering constraints

Cart and catalog output include:
  • minOrderQuantity
  • maxOrderQuantity
  • incrementalQuantity
Existing cart lines are checked against their constraints before mutation. New lines rely on Commerce Engine’s server validation because the cart does not yet contain the line’s constraints.

Promotional free items

A promotion can add units the shopper never selected. Agent-facing cart lines distinguish: A free-only line cannot be removed or reduced, matching the Hosted Checkout UI.

Result shapes

Tools return plain values. Do not JSON-stringify them yourself; the user agent serializes the result.
Failures distinguish retryable infrastructure conditions from terminal application conditions:
A successful cart mutation returns a safe projection of the resulting cart. The cart projection is an allow-list, so future Storefront API fields such as addresses, metadata, or payment state cannot accidentally become agent-visible.

Two signals, doing different jobs

Claiming a tool set is not atomic — it is a sequence of awaited registerTool calls. Two overlapping registrations against one model context would race for the same names and one would throw InvalidStateError: Duplicate tool name. React Strict Mode makes this the ordinary case rather than an edge one: it mounts, cleans up, and mounts again back to back on every dev mount. Registrations against one model context are therefore serialized — a second call waits for the first to settle, including any rollback. Cancellation rejects with CommerceWebMcpAbortError, which callers are expected to swallow — it means “you asked for this”. Without config.signal, cleanup can run before the controller exists and the stale pass stays live after its component has gone away.

Resolving CMS slugs in both directions

routes.product runs in the browser during tool execution, so a resolver that performs a CMS lookup would need that credential on the client. The portable shape is a pure function over route data the browser already has:
resolveProductRoute matters as much as product. An agent reads a CMS page slug off the page and calls get_product("knee-pain-relief-oil"); without the inbound mapping that goes straight to a catalog which has never heard of it. The two directions have opposite fallback rules, and conflating them is the common mistake: Returning null from an inbound resolver fails the tool with route_not_found before any catalog request rather than passing an unmapped slug to a catalog that has never heard of it.
Do not ship a complete 100,000-product manifest to the browser merely to resolve a search page. This package performs no route-data transport and imposes no API, batching, or cache policy — at that size, back the same hooks with a storefront-owned point cache that loads only the result set.

Cancellation is not rollback

WebMCP execution may supply an AbortSignal, and the package honors it at asynchronous boundaries. Commerce Engine SDK requests do not currently accept that signal, so once a cart request is in flight, cancellation cannot undo a server-side mutation. If the mutation response already confirms the new cart, the package returns that truth instead of pretending the change never happened. This prevents an agent from retrying an already-applied add and duplicating quantity.

Anonymous and authenticated sessions

A shopper does not need to sign in to use cart tools. An anonymous Commerce Engine session has a user identity sufficient for cart ownership. Signing in adds customer capabilities such as orders, saved addresses, loyalty, and customer pricing. The AI package does not authenticate the shopper itself:
  • open_login only opens your login UI
  • get_session_state reports whether the current session is anonymous/logged in and whether login UI is available
  • no tool accepts a password or OTP
  • session state does not expose who the shopper is
A guest cart survives sign-in in the same browser session because the session token evolves in place. Do not promise cart merge across a different device or browser. Navigation is same-origin by default. External destinations require explicit opt-in:
Permission is not inferred from arbitrary catalog or CMS content. This prevents attacker-controlled product content from becoming an open redirect through an agent tool.

Catalog and CMS content is untrusted

Catalog/search and optional policy/FAQ results are marked as untrusted content for the agent. Tool descriptions remain package-authored and are never interpolated from CMS output. Free-form input is bounded, tool schemas reject extra properties, and sensitive cart/customer fields are not forwarded by default.

Agent tools and security

Full tool inventory, diagnostics, WebMCP availability, extension modules, and the security model.

Development diagnostics

Always wire diagnostics during development:
Typical outcomes include:
No diagnostic line at all usually means your mount code never executed. Typecheck, build, and lint cannot detect dead registration code.

Build tools without the DOM

Use createCommerceAiTools(config) when you want to inspect or test the exact tool set without browser registration:
Tools are composed from plain modules. extraModules appends custom capabilities, while modules replaces the default module list entirely. Duplicate tool names fail during composition.

Key exports

Framework setup

Registration is always client-side. The correct mount point differs by framework:

React / Vite

Register from app bootstrap or a root effect.

Next.js

Use a root Client Component.

TanStack Start

Use a root client initializer.

Astro

Register on astro:page-load and handle View Transitions.

SvelteKit

Register in root onMount.