What it is, and why WebMCP (the use case)

In-browser AI agents act on websites by guessing at the DOM — brittle selectors, scraping, silent failures, no way to verify what happened. WebMCP fixes that at the platform level: the page declares tools, the agent calls them. But it only matters if sites actually implement it, and hand-wiring agent tools for every page of every site does not scale.

Cartwright is an open-source (MIT) engine that scaffolds complete stores — design system, database, cart, checkout, admin, i18n — with one command. During the challenge window we made the engine WebMCP-native: webshop templates now scaffold with the surface ON (npx create-cartwright my-shop --template coffee → a store whose every page registers typed, page-contextual tools via document.modelContext the moment an agent-capable browser opens it). Not one agent-ready site — a generator of them. Shopify has shown storefront-scale WebMCP for its hosted platform; this is the same idea for the self-hosted, open-source world: the whole engine is MIT, and every scaffold owns its stack.

WebMCP leverage — the depth of the surface

13 tools on the demo shop, three registration styles, one shared core:

  • Site-wide (imperative): search_products (reduced, bounded rows — no checkout endpoints or internal fields ever enter the browser surface), get_cart (verification-rich returns: line handles, stock ceilings, structured money), same-origin navigate.
  • Contextual, per route — progressive disclosure (the shape proposed in the tool-collections discussion, webmcp#255): an agent only ever sees the collection that makes sense on the page it is on. The product page registers add_current_product_to_cart with the page's live variants as natural-language enum values ("Whole beans, 250 g" — not internal ids), prices and stock in the descriptor. The cart page registers update_cart_item_quantity / remove_cart_item (line ids enumerated in the schema) / go_to_checkout. The catalogue page registers list_visible_products (exactly what the human sees, zero network) and filter_products (schema server-derived from the live categories).
  • Declarative forms: search, contact and newsletter carry toolname / tooldescription / toolparamdescription attributes — the form itself is the tool, answered via SubmitEvent.respondWith (preventDefault-first). toolautosubmit only on read-only search; the human confirms communication. Agent activity is visible: :tool-form-active styling highlights the form the agent is filling.
  • Design-pack tools: the homepage brew calculator is also calculate_brew_ratio — the same math module serves the human's widget and the agent's tool, so the two can never disagree. Any design pack can ship tools (DesignPack.webMcpToolBindings); the safety tests aggregate them all.

The engine also answers the frameworks meta-issue (webmcp#199) in shipped, test-pinned code: execute() reads state freshly at execution time (never captured props); effect dependencies are serialized props, so re-renders never thrash the registry; every mount owns an AbortSignal and cleanup runs before the next route's registrations (abort-then-register, the current-spec-safe order — our e2e fake rejects duplicate names, and a soft-navigation spec proves the swap inside one document); one rejecting registration cannot drop the tools after it.

One governed tool registry, two transports: the same operations back the in-page WebMCP tools for browsing agents and a server-side MCP endpoint (/api/mcp) for headless agents — same session, same state in the browser; same scopes and audit trail on the server. Beneath the callable layer sits the readable and discoverable one: llms.txt with capability truth, JSON-LD on every citable page, content negotiation (the homepage serves markdown to agents that ask), an ARD catalog, OpenAPI — and on the payable side the engine carries protocol-pluralistic commerce rails (ACP endpoints and UCP identity-linking on the same engine), so the merchant doesn't have to bet. Readable, discoverable, callable, payable — one engine.

Execution — a complete product, tested like one

This is a production Next.js 16 App Router application deployed on Vercel: a real store with a live database, working cart and checkout, an admin, and i18n — not a mock. The WebMCP layer is held to the same bar:

  • ~100 unit tests across the registrar, the per-route mounts, the declarative forms, the pack tool, and the safety moat (below).
  • A Playwright e2e suite installs a faithful fake document.modelContext (current spec semantics: duplicate-name rejection, pre-aborted-signal rejection) and walks home → catalogue → product → cart → checkout, including one real add-to-cart round-trip against the database and a soft-navigation leg that proves route teardown inside one document.
  • Evaluation-driven, with the Chrome team's own harness: we ran GoogleChromeLabs/webmcp-tools' webmcp-evals (smoke mode) against the live demo — 12/12 steps green across brew-ratio, cart reads, catalogue listing/filtering, search, a real add-to-cart and the checkout handoff (runner [email protected], repo commit d39eae4, 2026-08-27). The run's three genuine findings (a prefetch loop on the empty-cart page, non-canonical URLs in two tool returns, Danish parameter names on an English store) were fixed the same day — the eval harness caught real bugs, which is the point of running it.
  • Byte-identity discipline: with the flag off, no registrar mounts, no tool* attribute renders, the HTML is byte-identical — pinned by tests.
  • The live surface is smoke-monitored: the store's deployment gate asserts /en/webmcp-check and the pack tool on every run.

Potential impact — who this is for

Every small store that will never hand-write agent tools. The economics only work at the engine level: implement WebMCP once, correctly, with tests — and every scaffolded site inherits it. The demo is a real storefront a judge can buy from (demo checkout), running the exact code a scaffold produces. And the scaffold proves it in a first minute: npx create-cartwright myshop --template coffee && cd myshop && npm run dev seeds the demo's own nine-product catalogue — images, stock, attributes, and real variants on one product (an agent must handle both shapes) — so the tools have something true to act on with no config and no API keys. The same engine runs a second live vertical (eyewear, different design pack, Danish locale) — the surface is engine-level, not bespoke to one demo.

Creativity & ambition — the line we drew

The collaboration model is explicit: the agent gets the errands, the human keeps the decisions.

  • Every cart mutation returns the fresh cart, so the agent verifies instead of assumes; an ambiguous mid-request failure says so and routes the agent through get_cart before any retry.
  • Communication forms never autosubmit — the agent fills, the human sees the copper glow on the form, the human confirms.
  • The purchase is deliberately out of the agent's hands: a test-enforced safety moat holds the entire browser surface — across the layout, every per-route mount, and every registered design pack — to the cart/catalogue tool families. No order-placing tool exists in the browser. go_to_checkout opens the page; the checkout form is annotated for no one. Graceful handoff, in the commerce-protocol sense: the agent escorts the human to the door it cannot open.
  • /en/webmcp-check documents the shop's own agent surface — generated from the same constants the moat test verifies, so the page structurally cannot lie — with an intent-first explorer ("I want to… → the tool + a copyable example") and the Chrome team's polyfill as fallback so it demonstrates in any browser.
  • And the agent-experience layer is measured, not asserted: every change to the store's agent surface (WebMCP, MCP, llms.txt, discovery files) ships through tests + live smoke, and the store publishes machine-readable pricing, auth and capability truth alongside the tools.

Prior work vs. this submission (per the rules)

The engine — storefront, cart, checkout, admin, server-side MCP/REST APIs — pre-exists the challenge, as does a four-tool WebMCP experiment from June 2026 (v0.25.0: global search/add/get-cart/navigate behind a flag). The entire WebMCP surface submitted here was designed and built inside the submission window (Aug 27 – Sep 3, 2026): the shared registration core, contextual per-route tools, declarative form annotations, catalogue tools with server-derived schemas, design-pack tools with registry-aggregated safety tests, the moat, the e2e suite, the eval run, /webmcp-check, and the WebMCP-native scaffold default.

Public, timestamped evidence (the repo publishes snapshot syncs + release tags):

  • Tag boundary: v0.49.0 (Aug 24, pre-window baseline) → v0.50.0 / v0.51.0 (Aug 27, in-window release).
  • Pre-window snapshot: https://github.com/Teloz1870/cartwright-template/tree/v0.49.0
  • In-window snapshot and dated GitHub Release: https://github.com/Teloz1870/cartwright-template/tree/v0.51.0 and https://github.com/Teloz1870/cartwright-template/releases/tag/v0.51.0
  • CHANGELOG.md's dated v0.50.x/v0.51.0 blocks and docs/webmcp.md's day-by-day timeline. (The public mirror intentionally publishes each sanitised snapshot as a root commit, so GitHub's compare view has no common ancestor; the two tagged trees above are the auditable boundary.)
  • npm: create-cartwright 2.7.15, 2.8.0 and 2.8.1 were all published Aug 27 (public registry timestamps).
  • Sep 3 (deadline day, in-window): engine v0.54.0 — the coffee scaffold now seeds the live demo's nine-product catalogue with variants, and the README leads with "Try the agent tools in 60 seconds" — published to npm the same day.

(OpenAI's challenge brief: "build a new agent-native web app, or add WebMCP support to an app you already run" — this is deliberately the second shape, documented.)

Built With

Share this project:

Updates

Submission history