There is a decent chance an AI agent visited your site this week and had to guess its way around. It read your DOM, found something button-shaped, and clicked it. Then you shipped a redesign and it broke.

WebMCP is the W3C draft that fixes this. A page declares what it can do — typed tools with JSON Schemas, registered on document.modelContext — and agents call those instead of hunting for selectors.

It is not speculative any more. Chrome runs a public origin trial from 149 through 156, with a WebMCP panel in DevTools. It is default-on across Shopify storefronts and Cloudflare-fronted sites. The getter moved from Navigator to Document in May and Chrome 150 deprecated the old alias — the kind of churn you only get on a standard people are actually shipping.

The two sides

Here is the thing that took me a while to see clearly. WebMCP is not one capability. It is two, and they are almost unrelated in practice.

The provider side is publishing: your page registers tools so something else can drive it. This is what Shopify is doing. It is what you do when you add toolname to a form.

The consumer side is calling: an agent discovers the tools on a page and invokes them. This is what Chrome’s built-in agent does.

Now look at who is building what:

WhoTheir sideWhat they can’t do
Shopify, Cloudflare, any site adding <form toolname>providernothing calls those tools from inside the page
Chrome’s built-in agentconsumeryou can’t build on it, or ship it
MCP-B (@mcp-b/*)provider plumbing — polyfill, transports, relayit has no agent of its own
CopilotKit, Stagehand, Vercel AI SDKneithernot on the standard at all

Everyone picked a side. Which is reasonable — they are different problems, and the standard is young.

But the interesting position is both, in the same page. An agent that can call the tools the page already publishes, and publish its own so outside agents can drive the page. That is the thing nobody had, so we built it.

Consuming: the page’s tools become the agent’s tools

If you are running an agent in the browser, the page’s WebMCP registry is the highest-quality context you will ever get. It is not a DOM snapshot you have to interpret. It is a list of operations with names, descriptions, and argument schemas, written by the people who built the page.

In Dornick, adopting them is the default:

const dornick = new Dornick({ config, transport });
dornick.attach(form);

// The page registered "add_to_cart".
// The model now has a tool called webmcp__add_to_cart, and calls it directly.

Two design decisions turned out to matter more than expected.

Trust is a property of the channel, not a claim. A tool descriptor can carry annotations, including hints about whether its output is trustworthy. We ignore that for anything adopted from the page or a remote server, and mark it untrusted regardless. The page is not adversarial in the normal case, but “the page says its own output is safe” is not evidence, and the failure mode — prompt injection straight into the model — is bad enough to warrant the paranoia. Adopted output gets fenced in <untrusted-tool-output> and screened before it reaches the model.

Side effects need a human. A read-only tool can run freely. A tool that charges a card cannot. Consequential tools from external origins are denied unless the integrator supplies a confirmation handler. Deny-by-default is annoying exactly once, which is the right number of times.

Publishing: the config you already wrote is the schema

The provider side is where the effort usually is. Every tool means a name, a description, a JSON Schema, and an execute callback. For a form with twelve fields that is a lot of typing to describe something you already described.

Except — you did already describe it. If you configured an agent to collect an email with a validation pattern and a set of allowed values, that is a JSON Schema in all but syntax:

new Dornick({ config, transport, webmcp: { publish: true } });

// dornick_set_email, dornick_set_plan, … one per configured dataPoint,
// each schema derived from the type / pattern / allowedValues already in your config,
// plus the read-only workspace verbs and dornick_ask.

That is not a better polyfill. It is a different starting point. Because the runtime already knows what your form is for, the tool surface falls out of the configuration rather than being authored a second time.

Writes route through the same virtual filesystem the agent uses, so existing validation hooks and access-control rules apply to an outside agent exactly as they do to the in-page one. The provider surface is not a back door.

The part everyone should say out loud

We ship a polyfill. It implements document.modelContext in every modern browser, in about 7 KB, and it never clobbers — if the browser has a native implementation, or another vendor’s polyfill got there first, it stands down and uses theirs.

Here is what it does not do, and what no polyfill can:

EnvironmentWho can see your tools
Chrome 149+, origin trial or flagthe browser’s own agent, extensions, DevTools, page script
Anything else (polyfilled)extensions, CDP/Playwright drivers, the MCP relay, page script

Native document.modelContext is a browser IPC surface. A polyfill is ordinary page JavaScript. Chrome’s built-in agent talks to the browser, not to your page’s variables, so it will never see polyfilled tools no matter how conformant they are.

This distinction is easy to blur in marketing copy and I would rather not. What a polyfill buys you is one authoring API that works everywhere today, plus reach from extensions and automation drivers. What it does not buy is browser-agent reach. If that is your goal, get an origin-trial token; the polyfill is what covers everything else in the meantime.

Where this goes

The bet underneath all of this is that the web gets a second audience. Pages have been designed for humans, then retrofitted for crawlers. Agents are a third reader, and unlike crawlers they want to act, not just read.

You can serve that reader by leaving your DOM as a puzzle and hoping the model is clever enough. Or you can tell it what your page does.

WebMCP is the standard for telling it. Both sides of it are worth building.


Dornick is MIT and runs entirely in the browser. Make your page agent-ready · WebMCP, both sides · How we compare to MCP-B

FAQ

What is WebMCP?

WebMCP is a W3C draft that lets a web page expose typed, callable tools to AI agents through document.modelContext. Chrome runs a public origin trial through version 156, and it is default-on across Shopify storefronts and Cloudflare-fronted sites.

How is WebMCP different from MCP?

MCP connects an agent to external systems across a network boundary — a CRM, a knowledge base, a desktop client. WebMCP is how a page exposes its own functionality to agents running in the browser. No network hop; the tools are the page's own JavaScript.

Can a polyfill make my tools visible to Chrome's built-in agent?

No. Native document.modelContext is a browser IPC surface; a polyfill is ordinary page JavaScript. A polyfill reaches page script, extensions, and CDP/Playwright drivers. For the browser's own agent you need an origin-trial token or the chrome://flags switch.