Storefront Browser Tools
Use this when
Section titled “Use this when”Use this reference to connect visible storefront controls to session-authorized commerce tools and human review. This describes 0.2.0, the local source package version; it is not yet published.
Do not use this surface as a public JSON mutation API, a bundled MCP server, an authentication service, or a replacement for host-owned checkout pages. The host owns session/auth policy, protected action wiring, provider credentials, authoritative content, deployment, and reconciliation of uncertain effects.
Import boundaries
Section titled “Import boundaries”All subpaths below are under @bnomei/emdash-mika and appear in the target source export map.
/webmcp— browser:mountMikaWebMCP,mikaPageContext,mikaSelectVariant; options/mount types and storefront transport contracts. Usesdocument.modelContext, not a server MCP endpoint./annotations— browser:mountMikaAnnotations,validateMikaAnnotationMetadata, annotation control/spec/binding/options types. Does not invoke commerce or WebMCP actions./storefront-ui— browser:mountMikaStorefrontUI; shared authoritative SSR refresh, cart/wishlist form enhancement, tools, and annotations./storefront— server:createMikaStorefront,MIKA_STOREFRONT_REVIEW_SESSION_KEY,MikaStorefrontOptions,MikaStorefront, search-item and request/response/snapshot/tool types./storefront-actions— server/Astro:createMikaStorefrontActions,MikaStorefrontActionsOptions; JSONdescribe,invoke, and separateconfirmactions.
MikaStorefrontTransport supplies asynchronous describe(), invoke(request), and confirm(reviewId). The host injects this adapter into the browser runtime; executable server implementations do not cross the browser contract boundary.
Exact server tool allowlist
Section titled “Exact server tool allowlist”Names are case-sensitive. These are deliberately allowlisted, not discovered from every Mika operation. Below, R means read, W means mutation without separate review, and H means mutation requiring separate review. Every W/H request requires a UUID v4 requestId, outside input in the transport envelope. Fields marked ? are optional; — means empty input.
The registered WebMCP tool schema includes requestId alongside its other arguments; the browser adapter moves it out of those arguments into the transport envelope. Do not place it inside input when calling the transport directly.
| Tool | Mode | Input fields |
|---|---|---|
catalog.search |
R | query?, limit?, cursor? |
catalog.sellables |
R | collection, id, locale? |
stock.availability |
R | sellableId |
cart.get |
R | — |
cart.quote |
R | sellableId?, priceId?, quantity?, couponCode? |
cart.add |
W | sellableId, priceId?, quantity?, variantKey?, variantOptions? |
cart.update |
W | lineId, quantity |
cart.remove |
W | lineId |
cart.merge |
W | — |
cart.applyCoupon |
W | code |
cart.removeCoupon |
W | — |
wishlist.get |
R | — |
wishlist.add |
W | sellableId, priceId? |
wishlist.remove |
W | itemId |
wishlist.moveToCart |
W | itemId, quantity? |
wishlist.saveForLater |
W | lineId |
wishlist.merge |
W | — |
checkout.preview |
R | sellableId?, priceId?, quantity?, couponCode?, provider? |
checkout.start |
H | sellableId?, priceId?, quantity?, couponCode?, provider? |
checkout.status |
R | checkoutId |
checkout.cancel |
H | checkoutId |
Account tools: default off
Section titled “Account tools: default off”All tools in this table require accountTools: { enabled: true }. Discovery and dispatch also require a resolved customer identity, except signIn.request, which is available only without a customer identity. Enabling the toggle does not grant ownership of arbitrary records.
| Tool | Mode | Input fields |
|---|---|---|
account.get |
R | — |
account.export |
H | — |
account.exportStatus |
R | exportId |
account.exportDownload |
H | exportId |
account.delete |
H | — |
account.portal |
H | — |
subscription.cancel |
H | subscriptionId |
subscription.change |
H | subscriptionId, priceId |
subscription.renew |
H | subscriptionId |
order.invoice |
H | orderId |
download.handoff |
H | downloadId |
signIn.request |
H | — |
signIn.request sends no email and accepts no email field. After review it hands the human to /account to enter and review an email in the ordinary sign-in form.
Unknown input fields are rejected. Quantity is an integer from 1–1000; search limit is 1–50 (default 20). Strings must be nonblank, with maximum length 200 for query and 512 for other string fields. variantOptions permits at most 20 nonblank string entries, with keys/values bounded to 200 characters and prototype-related keys refused. Operation schemas apply additional validation.
Search cursors are opaque, session/identity/query-bound continuations. Continue even after an empty page if nextCursor exists; never manufacture a cursor. Host search hooks must paginate at the source and return at most the requested limit.
Browser-local tools and lifecycle
Section titled “Browser-local tools and lifecycle”The WebMCP mount adds two local tools, overriding any transport descriptions with the same names:
mika_page_context: empty input; bounded visible purchase labels, options, variant groups, and prices. It does not harvest hidden form payloads, tokens, or arbitrary datasets.mika_select_variant: requiredwidgetIdandsellableId, optionalpriceId. Selects a visible available variant within exactly one purchase widget; ambiguous matches are rejected. It changes visible controls, not the cart or checkout, and needs no durable request ID.
Tool annotations expose readOnlyHint, consequentialHint, and untrustedContentHint; all tools mark content untrusted. Server consequential hints correspond to H tools; local selection is non-read-only but non-consequential.
mountMikaWebMCP requires a refresh(response) callback that resolves only after visible UI reflects the response. The agent result follows that refresh; known server outcomes remain known if refresh fails, with uiRefreshFailed: true. Registration uses abort-signal lifetimes, suspends on page hide/Astro swap, resumes on page show/Astro load, and refreshes on mika:context-change. Dispose removes registrations, panels, and listeners. If document.modelContext.registerTool is absent, registration is skipped.
Response statuses and safe output
Section titled “Response statuses and safe output”MikaStorefrontResponse.status is distinct from the numeric HTTP-like result.status inside MikaApiResult.
| Status | Meaning |
|---|---|
completed |
Known successful operation or replay of its safe result. |
rejected |
Invalid, unauthorized, denied, conflicting, expired, or known unsuccessful request; a safe error result may be present. |
requires_confirmation |
Pending separate review; review contains an ID and safe summary. No consequential dispatch yet. |
handoff |
Human UI continuation, with a UI-only URL/label. Not proof of payment or fulfillment completion. |
in-progress |
Durable dispatch already claimed, or browser runtime currently busy. Do not start another mutation to recover it. |
uncertain |
Effects cannot safely be inferred; reconcile before any new intent. |
MikaStorefrontResponse is a discriminated union: requires_confirmation requires review and prohibits handoff; handoff requires handoff and prohibits review; all other statuses prohibit both. Responses may include safe result, state (cart/wishlist and optionally account), and recoveryReference.
The WebMCP agent projection returns status, result, state, and recoveryRef (renamed from recoveryReference), with optional uiRefreshFailed: true, never review IDs/summaries or handoff URLs. Before a server response is known, an uncertain browser mutation uses the original request ID as recoveryRef; server responses use the hashed durable recovery reference.
Nested structural allowlists strip unknown fields, provider redirects, download bearer URLs, and raw account DTOs. Account output includes safe owned order/subscription/entitlement/download information and license status/IDs, never license keys. Error messages are generic, field errors are allowlisted, and only recognized warning/error codes survive projection. Public product URLs in search are deliberately distinct from sensitive handoffs.
Server handoff URLs allow safe root-relative paths and credential-free external HTTPS URLs. Absolute same-origin HTTP/HTTPS URLs normalize to relative paths using the request origin. Whitespace, control characters, backslashes, credentials, protocol-relative URLs, and executable schemes are refused. The browser renders a continuation link rather than automatically navigating. Same-run replay preserves an existing human handoff in the browser. Handoff URLs are not persisted in safe results and cannot be reconstructed by replay; if the browser has lost the handoff, recover the known outcome before beginning a new human continuation flow.
Review, durable dispatch, and recovery
Section titled “Review, durable dispatch, and recovery”createMikaStorefront requires the API, account/catalog repositories, durable repositories.ops, a collision-resistant cryptographic hash, and a clock. Invocations require a server session/session ID. Identity hydration checks account bindings and rejects invalid or anonymized accounts. The default review TTL is ten minutes; the cap is eight unexpired pending session reviews. Completed reviews do not exhaust that cap.
The account port is limited to findCustomerById, findCustomerByUserId, findCustomerByEmailHash, and optional listLicensesByCustomer; a full account repository is not required.
H tools capture safe review terms and bind the request to authoritative state. Confirmation rechecks scope, expiry, tool availability, authorization, operation policy, terms, and state before approval; execution checks terms/state again. Boundary-sensitive operations support either the recommended built-in backend review capability or custom captureReview without backend, provided host API overrides enforce ctx.storefrontReview at the actual mutation boundary. A durable claim alone does not make provider/backend mutation atomic or close a review-to-write race.
The Astro action wrapper enforces an exact same-origin Origin, refuses cross-site fetch metadata, bounds serialized input to 16 KiB, and supports a host guard. humanConfirmation is optional additional host policy. Never register confirm as an agent tool. The browser review button requires a trusted click, but neither isTrusted, the endpoint, nor a confirmation response is cryptographic proof of humanity: page scripts already possess the user’s authority.
Cancel/Escape dismisses review locally only. It does not cancel the server intent; check its status before submitting another action. Confirmation or refresh failures must not trigger automatic repeat confirmation.
Both initial invocation and post-confirmation UI retain authoritative review/handoff controls if SSR refresh fails, with a stale-display warning. Terminal confirmation outcomes show status guidance and a Close control; a refresh failure does not turn a known operation into an unknown one.
For durable retry behavior:
- Use a fresh UUID v4 for a new intent. For lost-response recovery, reuse the same ID with the same normalized input and session/identity scope.
- The server derives an opaque hashed key from scope and the ID. Durable records bind that key to scope, operation, and input hashes; changed input conflicts. Pending review can be returned again while valid. Completed safe results replay even after TTL: expiry limits approval/new dispatch, not recovery of completed effects. Missing or malformed stored results remain
uncertain, never permission to dispatch again. - The runner atomically claims a prepared/approved run as
in-progressbefore dispatch. Store update callbacks must be pure because the atomic store may retry them. This is one-shot durable dispatch, not an exactly-once transaction across provider effects. - Dispatch exceptions, lost completion persistence, aborts, or process death may leave
uncertainor non-reclaimablein-progress. The browser does not automatically retry: abort after dispatch cannot prove that a write did not happen. An unknown mutation outcome reports uncertainty; an unknown read reports rejection. - Retain durable tombstones indefinitely, including after TTL. Never reclaim unfinished runs or reuse an expired key. Do not retry uncertainty with a new request ID. Reconcile effects out of band using the recovery reference before starting a genuinely new intent.
Built-in backend provider exceptions preserve unknown effects rather than implying rejection. Audited subscription/admin recovery also preserves uncertainty when an effect or its completion persistence may have failed; an audit marked failed is not proof that no effect occurred. Custom API overrides must preserve this distinction (effectOutcome: "unknown") instead of converting uncertain writes into ordinary retryable failures.
Use the existing durable run store; do not add a second browser or host ledger to infer whether a mutation happened.
Annotation-only mode and safe previews
Section titled “Annotation-only mode and safe previews”mountMikaStorefrontUI(root, transport?, { webmcp?, annotations? }) has separate toggles. WebMCP defaults on only when a transport is supplied; explicitly enabling it without a transport throws. Annotations default on unless annotations: false. Annotation-only mode can omit transport and set { webmcp: false, annotations: true }; it does not require server tool registration. The shared UI also enhances eligible same-origin cart/wishlist forms and refreshes keyed SSR views, preserving edited visible drafts but not hidden authority, passwords, or file controls. Hosted checkout/auth retain ordinary navigation.
Annotation targets use data-mika-annotation; prompt buttons use data-mika-annotation-prompt. The runtime emits oai-annotation-container, oai-annotatable, and validated optional oai-annotation-metadata, feature-detecting document.oai.annotation.registerControls and .request. Prompt requests require trusted clicks and never submit the enclosing form. Missing or denied APIs leave ordinary editing intact.
Default product controls use a separate preview map and status output: “Annotation preview only — not applied to purchase.” They never change submitted purchase controls or drafts. Human input clears previews; refresh/dispose cleans registrations and restores applicable preview state without overwriting intervening edits. Custom control bindings are host-owned; use them only for safe previews, not commerce dispatch. Supported control types are select, toggle, color, and range, with bounded validated specs. Metadata comes only from explicit caller data, is bounded to six scalar entries/2048 UTF-8 bytes, and rejects secret-like keys; this validation is not a substitute for host review of metadata values.
For custom bindings, use mountMikaAnnotations(root, { metadata, controls, productControls, onRequest }) and call its refresh() after DOM replacement or ordinary state changes. The shared storefront mount exposes boolean toggles, not these custom options; its metadata comes from explicit data-mika-annotation-metadata values.
Native Chrome 155 verification is reported by the source review thread. ChatGPT annotation compatibility has not been verified; the annotation adapter’s feature detection is not a compatibility guarantee.
Source Anchors
Section titled “Source Anchors”../emdash-mika/package.json../emdash-mika/src/storefront.ts../emdash-mika/src/storefront-actions.ts../emdash-mika/src/webmcp.ts../emdash-mika/src/annotations.ts../emdash-mika/src/storefront-ui.ts../emdash-mika/src/browser/contracts.ts../emdash-mika/src/browser/webmcp.ts../emdash-mika/src/browser/annotations.ts../emdash-mika/src/browser/storefront-ui.ts../emdash-mika/src/api/storefront/tools.ts../emdash-mika/src/api/storefront/projection.ts../emdash-mika/src/api/storefront/review.ts../emdash-mika/src/api/storefront-runs.ts../emdash-mika/src/api/backend/admin-audit.ts