A hands-on reference implementation of WebMCP, the proposed web standard that lets a page expose structured, schema-described tools to AI agents running in the browser (for example, Gemini in Chrome). The repository exercises both API surfaces (imperative and declarative), demonstrates the full testing pyramid recommended by Chrome's evals guidance, and documents the practical constraints you will hit before adopting WebMCP in a production codebase.
Everything here is plain static HTML and JavaScript. There is no build step, no framework, and no server-side code, so each demo is readable end to end in a single file pair.
- Engineers evaluating WebMCP for a real product and needing working code beyond the documentation snippets.
- Teams deciding between the imperative and declarative APIs and wanting a side-by-side comparison of the trade-offs.
- Anyone designing a testing strategy for agent-invoked tools, from deterministic unit tests up to model-driven evals.
| Demo | API style | What it demonstrates |
|---|---|---|
demos/pizza-imperative |
Imperative (document.modelContext.registerTool) |
A multi-tool transactional flow, toolchange events, and invoking tools manually with executeTool for isolated testing |
demos/support-declarative |
Declarative (toolname / tooldescription HTML attributes) |
Turning a plain <form> into a tool with zero JavaScript, toolparamdescription, toolautosubmit, the SubmitEvent.agentInvoked / respondWith flow, and the :tool-form-active / :tool-submit-active CSS hooks |
demos/car-search-imperative |
Imperative | A richer JSON Schema (enums, numbers, booleans) driving a client-side filter, modeled on the documented "vehicle search" use case |
demos/payment-confirmation-imperative |
Imperative | Human-in-the-loop approval: a destructive tool whose execute promise stays pending until the user approves, rejects, or the request times out, next to a read-only tool (readOnlyHint) that resolves immediately |
evals |
n/a | Eval fixtures in the expectedCall format, plus a deterministic (non-LLM) test harness for the tool logic itself |
Every imperative demo includes an on-page event log: each tool invocation — agent-driven or via the manual harness — is recorded with its arguments, result or error, and duration, and controls the agent touched are briefly highlighted. The demos share this instrumentation through demos/shared.js.
Serve the repository root with any static server. Do not open the files via file://; that origin breaks module loading and some browser features.
npx serve .
# or: python3 -m http.server 8080Then open a demo, for example http://localhost:3000/demos/pizza-imperative/.
WebMCP is experimental (Chrome 149 or later at the time of writing):
- Enable
chrome://flags/#enable-webmcp-testingand relaunch. This is the local-development path; no origin trial token is needed. For production origins, Chrome offers an origin trial instead. document.modelContextis the entry point.navigator.modelContextwas the original prototype location but stopped being available in Chrome 150; this repository targetsdocument.modelContextexclusively.- Exercising the tools with a real agent requires a WebMCP-aware agent surface (for example, Gemini in Chrome, or the Model Context Tool Inspector extension). Without one, every demo includes a "Simulate agent call" panel that invokes
document.modelContext.executeTool(...)directly, so you can see exactly the payloads an agent would send and receive.
These are the scenarios from Chrome's use-cases documentation that the demos map to:
- Structured search and filtering. The car-search demo models an agent translating a natural-language request ("a used electric SUV under 30k") into a single schema-validated tool call, rather than the agent scraping the DOM and driving filter widgets click by click. The same pattern applies to product catalogs, apartment listings, and hotel search.
- Multi-step transactional flows. The pizza demo shows an agent composing several small tools (
set_pizza_size,add_topping,place_order) to complete an order, with page state and UI staying in sync after every call. This is the shape of most cart, booking, and configuration flows. - Form completion. The support-form demo shows the lowest-cost adoption path: annotating an existing
<form>with a handful of attributes so the browser derives a JSON Schema from the markup. Chrome's docs note that well-deployed autofill can lower form abandonment by roughly 75 percent; declarative WebMCP is the agent-era extension of that same investment. - Human-in-the-loop for destructive actions. The payment demo shows the pattern any production deployment needs before letting an agent spend money or delete data: the tool validates its arguments, then holds its
executepromise open while the page displays an approval card. The agent receives a success result only after explicit user approval, and a structured error on rejection or timeout. The demo also enforces a per-call amount cap in the tool itself rather than trusting the schema alone.
- Declarative is the right default for anything that is already a form. It requires no JavaScript, degrades to a normal form when WebMCP is unavailable, and the schema is derived from markup you already maintain. Its ceiling is low: one tool per form, parameters limited to what form controls can express, and no dynamic registration.
- Imperative is required when tools need arbitrary JSON Schemas, computed results returned to the agent, dynamic registration and removal (for example, tools that only exist after login), or behavior not tied to a form submit. The cost is that you now own schema definitions and execution logic as code, and they can drift from the UI if not tested.
A useful production posture: declarative for forms, imperative for everything else, and deterministic tests (see below) guarding the imperative tool logic.
Things to know before building on WebMCP, drawn from the docs and confirmed in these demos:
- Experimental API surface. Chrome 149+ behind a flag or origin trial. The entry point has already moved once (
navigator.modelContexttodocument.modelContext), and further breaking changes should be expected. Do not ship load-bearing functionality on it; treat it as progressive enhancement and always feature-detect ("modelContext" in document), as every demo here does. - No headless mode. A visible tab or webview must be open; tools cannot run in a background or server context. This directly constrains eval automation (see the cost section).
- Origin isolation is mandatory. WebMCP only works in origin-isolated documents. Setting
Origin-Agent-Cluster: ?0(or relying ondocument.domain) disables it. - Permissions Policy. Both APIs are gated by the
toolsPermissions Policy, which defaults toself. Cross-origin iframes need an explicitallow="tools"attribute. agentInvokedcannot be spoofed. Pages cannot synthesize a submit event withagentInvoked = true. The declarative demo approximates the agent path by programmatically filling and submitting the form, and is explicit about the difference.- Implementation rough edges. Some Chrome builds return
inputSchemaandexecuteToolresults as JSON strings rather than parsed objects. The demos normalize both (seeparsedSchema/parsedResultindemos/shared.js); expect to do the same until the implementation settles. - Discoverability is per-page. An agent only sees a page's tools once the page is loaded in a tab. There is no cross-site registry; WebMCP is not a replacement for a server-side MCP server when you need tools available outside a browsing session.
- Security model is your responsibility above the protocol. Tool descriptions and results flow into a model's context, so the usual agent risks apply: never trust tool arguments (validate server-side exactly as you would a form POST), and mark destructive tools appropriately (see the
readOnlyHintannotation onplace_order).
The evals doc describes three tiers. This repository implements all three, and they have very different costs:
-
Isolated tool testing — free, interactive. The "Simulate agent call" panel in each demo calls
executeTooldirectly against one tool. Use this while developing a tool's schema and execute function. -
Deterministic tests — free, CI-friendly.
evals/tool-logic.test.mjsruns plain Node assertions with no browser and no LLM. The pure tool logic lives in modules (demos/*/logic.js,demos/car-search-imperative/search.js) that both the browser demos and the tests import, so the tests exercise exactly the code that ships and cannot drift from it:npm testThe same command runs in CI on every push (
.github/workflows/ci.yml). This tier should carry most of your regression coverage, because it is the only tier that is fast, free, and deterministic. -
Probabilistic evals — costs real model tokens and requires a real browser.
evals/cases/*.jsonholds fixtures in theexpectedCallshape (user message in, expected tool call out). Because WebMCP has no headless mode, running them means driving an actual tab with an agent runtime; Chrome's experimentalwebmcp-toolsevals-cli exists for this, and the related inspector extension defaults togemini-3-flash-preview, which requires your own API key and is billed at that model's standard rates. Budget accordingly: these are non-deterministic, so meaningful signal requires multiple runs per case. Keep this tier small and high-value (tool selection and argument extraction), not a substitute for tier 2.
There is no cost to WebMCP itself: it is a browser API, and this repository has zero dependencies and no services to pay for. Costs only appear when you put a model in the loop.
demos/
pizza-imperative/ imperative API, multi-tool flow, manual harness
logic.js pure tool logic (shared with tier-2 tests)
support-declarative/ declarative API, HTML-only tool, agentInvoked flow
car-search-imperative/ imperative API, richer JSON Schema
search.js pure tool logic (shared with tier-2 tests)
payment-confirmation-imperative/ human-in-the-loop approval for a destructive tool
logic.js pure validation logic (shared with tier-2 tests)
shared.js feature detection, event log, quirk normalization
shared.css shared demo styling
evals/
cases/ expectedCall-style eval fixtures (tier 3)
tool-logic.test.mjs deterministic unit tests (tier 2)
README.md how the eval tiers map to this repo
.github/workflows/ci.yml runs the tier-2 tests on every push
index.html demo index with WebMCP feature detection
package.json no dependencies; provides npm test / npm start