Browser Shopping Tools
Use this guide to let an agent shop alongside a human in the same live storefront and session. The 0.2.0 target adds browser WebMCP tools, optional ChatGPT annotations, and server-owned review/dispatch. It does not add a public mutation API or a hosted MCP server. Check Compatibility And Publication before choosing a package version.
WebMCP invokes commerce through host-wired Actions. Annotations supply selection context, suggested prompts, and reversible previews; they do not invoke commerce. Public JSON-LD, llms.txt, and the public-only agent manifest remain separate discovery surfaces.
Wire one durable backend
Section titled “Wire one durable backend”The recommended built-in backend path starts with Production Backend Wiring. Copy lib/mika-api.ts, actions/index.ts, actions/mika.ts, and components/MikaStorefront.astro from the package templates, alongside the normal storefront components. The copied createMikaHost(backend) function composes the ordinary API and storefront options from the same dependencies. For this path, replace its empty api and undefined storefrontOptions scaffold exports:
// src/lib/mika-api.ts — retain the copied createMikaHost function.// backend is your real MikaBackendDependencies implementation, not a Mika export.import { backend } from "./mika-backend";
export const { api, storefrontOptions, storefront } = createMikaHost(backend);For this built-in backend example, the host implements ./mika-backend: durable repositories, provider registry, notification delivery, defaults, ID generation, clock, and cryptographic hash. The source example src/templates/astro/examples/astro-storefront.md shows the full dependency composition. Do not substitute test fixture memory collections, fake providers, seeded identities, or fixed clocks for production dependencies.
Additional repository requirements are:
repositories.ops.findStorefrontRunand atomicupdateStorefrontRunfor durable dispatch/recovery. Reuse this store; do not add another ledger.repositories.catalog.listItemsandfindPriceByIdfor discovery and reviewed plan changes.repositories.accountneeds onlyfindCustomerById,findCustomerByUserId, andfindCustomerByEmailHash, with optionallistLicensesByCustomer; it does not require a full account repository. Identity resolution still applies with account tools disabled.- A server session driver shared across requests and instances for identity, pending reviews, and opaque pagination cursors.
Actions and page module graphs must share authoritative state and durable run storage. With the built-in backend, retain backend in storefront options for mutation-boundary review checks. Alternatively, omit backend and supply custom captureReview with host API overrides that enforce ctx.storefrontReview at the actual mutation boundary. Capturing terms without enforcing them when writing is insufficient. Both paths require durable dispatch/recovery storage; neither requires a second ledger.
Register the protected Actions
Section titled “Register the protected Actions”import { createMikaActions } from "./mika";import { createMikaStorefrontActions } from "@bnomei/emdash-mika/storefront-actions";import { api, storefrontOptions } from "../lib/mika-api";
export const server = { mika: createMikaActions({ api }), mikaStorefront: createMikaStorefrontActions(storefrontOptions),};This registers describe, invoke, and confirm as session-bound JSON Astro Actions, not public EmDash plugin routes. Keep Astro origin checks enabled. Add host rate limits and authorization through each factory’s guard; use operationPolicy for rules shared across operation surfaces. The storefront factory additionally requires the exact same-origin Origin and rejects cross-site fetch metadata.
confirm is called only by the visible review UI and must never be registered as an agent tool. Optional humanConfirmation(ctx, reviewId, intent) adds host policy. Neither the endpoint nor a trusted click proves humanity: page scripts already have the user’s authority. Hosts needing stronger approval must supply their own policy.
/storefront and /storefront-actions are server-only. /webmcp, /annotations, and /storefront-ui are browser entrypoints; do not import backend repositories or credentials into client scripts. See Storefront Browser Tools for the exact allowlist and schemas.
Opt in per page
Section titled “Opt in per page”The copied MikaKumoPage.astro defaults both integrations off. storefront enables both; explicit flags override it independently:
<!-- Both integrations --><MikaKumoPage title="Product" storefront> <ProductPurchase sellables={sellables} /></MikaKumoPage>
<!-- Annotations only: no storefront commerce transport required --><MikaKumoPage title="Product" annotations webmcp={false}> <ProductPurchase sellables={sellables} /></MikaKumoPage>
<!-- WebMCP only --><MikaKumoPage title="Product" webmcp annotations={false}> <ProductPurchase sellables={sellables} /></MikaKumoPage>These are alternative page configurations, not three shells to render together. In a custom layout, placing <MikaStorefront /> directly enables both by default; enabled={false} disables both. Its webmcp and annotations props remain independent.
Keep product, cart, and account views request-rendered (prerender = false) and uncached. Custom layouts need uniquely keyed data-mika-storefront-view regions containing the authoritative SSR state. The shared runtime refreshes those regions and cart badges after forms/tools, then refreshes enabled tools and annotations. Unrelated dirty visible controls retain their drafts; submitted controls and hidden authority use fresh server values. Keep the mount outside replaceable regions.
Eligible same-origin cart/wishlist POST forms are enhanced; checkout and authentication retain native navigation. Without JavaScript, forms remain native. Missing browser APIs leave ordinary controls usable. Registration uses asynchronous document.modelContext.registerTool, not navigator.modelContext; lifecycle cleanup covers navigation and Astro swaps.
Enable account tools deliberately
Section titled “Enable account tools deliberately”Account tools default off. Set accountTools: { enabled: true } in the shared storefront options only after wiring authentication, notifications, and account authorization. Pass those same options to both createMikaStorefront and createMikaStorefrontActions if using both surfaces. Configure contentUrl to match real host product routes; optional search must paginate at the source and return no more than the requested limit.
Enabled account tools include account reads, export/status/download, deletion, portal handoff, subscription cancel/change/renew, invoice, download handoff, and sign-in handoff. Discovery and dispatch check the resolved customer identity. signIn.request is only available without a customer identity: it accepts no email and sends no email, instead opening the normal /account sign-in flow after review.
For built-in export attachments, install mikaAccountExportIntegration from /astro with the same live API and operation policy as the plugin. EmDash 0.22 otherwise JSON-wraps the response. Follow Account Export Downloads for the exact HTTP route, session requirements, and token confirmation flow; neither artifact bytes nor the server-only deliverAccountExport capability belong in tool output.
Agents receive safe projections, not raw account records, license keys, bearer download URLs, or provider redirect URLs. Review details and handoff links belong to human UI, not tool output.
Preserve review and recovery semantics
Section titled “Preserve review and recovery semantics”Cart/wishlist mutations use durable request IDs without a separate consequential review. Checkout start/cancel and consequential account tools first return requires_confirmation; no consequential operation has dispatched yet. The UI shows server-derived terms and a separate confirmation control. Confirmation rechecks identity, authorization, expiry, input, and authoritative state. Changed terms require a new review, not silent approval of the changed purchase.
After a known operation, a handoff displays a continuation link for the human; it does not automatically navigate or prove payment completion. Use provider-backed checkout status. Cancel/Escape dismisses review locally only; it does not cancel the server intent.
For an unknown outcome, do not repeat the mutation with a new ID. Recover using the same UUID v4 request ID, normalized input, and session/identity scope. Completed safe results survive TTL; missing or malformed saved results remain uncertain. The durable store never reclaims unfinished dispatch merely because its TTL expired. Retain tombstones indefinitely and reconcile effects out of band with the recovery reference. A provider effect and local persistence are not an exactly-once transaction; backend provider exceptions and failed audits can still represent unknown effects. Sensitive handoff URLs are not persisted for replay; recover the known outcome before beginning a new human continuation flow.
The browser runtime does not automatically retry. Known outcomes remain known if UI refresh fails (uiRefreshFailed); both initial and post-confirmation responses retain review/handoff UI with a stale-display warning. Terminal confirmation outcomes provide status guidance and Close rather than inviting repeat confirmation. Unknown mutation outcomes are uncertain. See response and recovery contracts.
Keep annotation previews separate from purchases
Section titled “Keep annotation previews separate from purchases”The annotation runtime uses document.oai.annotation, oai-annotation-container, oai-annotatable, and validated optional oai-annotation-metadata. For custom targets, mark a visible region and a suggested-prompt button:
<section data-mika-annotation="Product comparison"> <p>Compare the visible product options.</p> <button type="button" hidden data-mika-annotation-prompt="Help me compare these options."> Ask about these options </button></section>The mount feature-detects request and registerControls. A prompt opens an editable annotation with initialComment; accepted: true does not mean a message was sent. Unsupported/denied APIs leave ordinary editing intact.
Product preview controls render a separate “Annotation preview only — not applied to purchase” status. They never change submitted controls, ordinary drafts, or the cart. preview, preview-original, and reset are reversible local rendering events, never permission to save or dispatch commerce. Custom bindings must be synchronous and reversible, with no requests or synthetic mutation events. Human edits supersede previews; refresh/dispose cleans registrations. Only share intentional, bounded metadata; a secret-key filter cannot determine whether a value is private.
Verify in the intended browser
Section titled “Verify in the intended browser”The source integration work verified native Chrome 155 tool registration, cart mutation, review, handoff, and account tool exposure. Native ChatGPT annotation compatibility has not been verified. Do not treat a mock annotation API or feature detection as proof of compatibility.
ChatGPT currently requires JavaScript registration in the top-level page: declarative HTML tools and iframe tools are unsupported. Ordinary forms still work as forms, not declarative WebMCP tools. Browser availability and workspace policies are separate from Mika configuration.
Before publishing a host integration:
- Verify both, WebMCP-only, annotations-only, unsupported-browser, and JavaScript-disabled configurations.
- Interleave human variant/quantity edits and agent cart changes; ensure visible SSR state agrees and unrelated drafts survive.
- Check same-origin/session enforcement, account-tool opt-in, stale reviews, expiry, concurrent changes, and double confirmation.
- Simulate lost responses and completion persistence failures; verify same-ID recovery and no second dispatch after uncertainty.
- Check that agent outputs and metadata omit private URLs, tokens, and raw account data.
- In the native ChatGPT browser, test prompt acceptance/denial, preview/original/reset, human edits during preview, navigation cleanup, and no cart write during any annotation event.
Use the Fixture Testbed for resettable scenarios, not as proof of production provider behavior.
Migrate ordinary Action callers
Section titled “Migrate ordinary Action callers”The 0.2.0 target changes ordinary actions.mika.* results to preserve the Mika envelope inside Astro’s Action result. Check result.error, then result.data.ok; read the success DTO from result.data.data. A truthy result.data can be a commerce failure. The separate mikaStorefront Actions return descriptions or MikaStorefrontResponse inside Astro’s envelope, without that additional Mika wrapper. See Astro Actions.
External References
Section titled “External References”Source Anchors
Section titled “Source Anchors”../emdash-mika/src/templates/astro/examples/astro-storefront.md../emdash-mika/src/templates/astro/lib/mika-api.ts../emdash-mika/src/templates/astro/actions/index.ts../emdash-mika/src/templates/astro/components/MikaStorefront.astro../emdash-mika/src/templates/astro/components/MikaKumoPage.astro../emdash-mika/src/storefront.ts../emdash-mika/src/storefront-actions.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-runs.ts