There's a new visitor in your logs: the AI agent, acting on someone's behalf. Right now it "uses" your site by reading the DOM and guessing which button does what. WebMCP replaces the guessing with a contract — your page declares structured tools an in-browser agent can call directly.
It's a draft W3C standard (Google + Microsoft) that shipped as an early preview in Chrome 146. Here's how to add it, step by step, with copy-paste code.
The mental model (read this first)
A WebMCP tool is three things:
- a name
- a description the agent reads to decide when to call it
- an inputSchema describing its parameters
When the agent calls it, your code runs in the user's own tab, with their session and permissions, and returns a result. There are two ways to declare a tool — start with declarative.
Step 1 — Declarative: expose a form (the easy win)
If the action is already a <form> (search, subscribe, log in), add two attributes to the form and one to each input. It keeps working for humans; the browser synthesizes a tool from it.
<form
tool-name="search-products"
tool-description="Search the product catalog by keyword"
action="/search" method="get"
>
<input
name="query"
tool-param-description="Keywords to search for, e.g. 'running shoes'"
required
/>
<button type="submit">Search</button>
</form>
Because the tool lives in your HTML, it survives your build step and is trivial to verify. Make this your default: every important form gets tool-name and tool-description.
Step 2 — Imperative: register a tool in JS (for everything else)
For logic a form can't express, register a tool with JavaScript. Feature-detect first so non-WebMCP browsers are unaffected. The current entry point is document.modelContext (older previews used navigator.modelContext):
const mc = document.modelContext || navigator.modelContext;
if (mc) {
mc.registerTool({
name: "add-to-cart",
description: "Add a product to the cart by SKU.",
inputSchema: {
type: "object",
properties: {
sku: { type: "string", description: "Product SKU, e.g. 'SHOE-42'" },
quantity: { type: "integer", description: "How many to add" }
},
required: ["sku"],
additionalProperties: false
},
async execute({ sku, quantity = 1 }) {
const result = await addToCart(sku, quantity); // your existing app logic
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
});
}
The key move: execute calls the same function your UI already calls. You're exposing logic you already have, not building a second integration.
Step 3 — Return the right shape
Whatever execute does, return the MCP content-block shape so the agent gets a usable result:
return { content: [{ type: "text", text: JSON.stringify(data) }] };
Step 4 — Verify it
Two things worth knowing:
- Declarative tools are easy to confirm — they're right there in your HTML.
- Imperative tools aren't — they only exist after your JS runs, and in a minified bundle you can't eyeball them. Any static "is my site ready" check confirms your form-based tools but sees only a code reference for JS-registered ones. That's the nature of static analysis, not a flaw in your site.
Quick loop I use:
Generate correct snippets (both formats, with the feature-detection shim):
https://toolhq.dev/tool/webmcp-generator/Check your page — paste HTML or scan your URL; it also flags forms you could expose and hands you the attributes to add:
https://toolhq.dev/tool/webmcp-checker/For a true end-to-end test, open the page in a WebMCP-capable browser (Chrome 146+ with the flag) and have its agent call the tool.
Deeper walkthrough:
https://toolhq.dev/learn/make-your-website-webmcp-ready/
Best practices
- Name tools in kebab-case; write the description like you're briefing a teammate.
- Describe every parameter — vague inputs cause wrong calls.
- Least privilege: only expose actions the user could already perform; the tool runs with their session.
- Guard destructive actions (delete/pay/send) with confirmation; treat tool input as untrusted.
- Keep the human UI working — WebMCP augments your page, never replaces it.
Where WebMCP fits
Three layers, not one:
-
llms.txt→ tells an assistant what your site is and points at key pages. -
robots.txt→ decides who may crawl. - WebMCP → declares what an agent can do once it's there.
Search optimized your site for crawlers; this optimizes it for actors.
Wrap-up
WebMCP is early — the spec (especially the declarative attribute names) can still shift, so feature-detect and keep your human UI intact. But it's cheap to adopt: a couple of attributes on forms you already have, plus a thin registerTool wrapper around logic you already wrote.
Ten minutes. Start with one form.
Have you made anything agent-ready yet, or hit a rough edge in the spec? Compare notes in the comments.
Top comments (3)
The three-layer framing is useful. We added llms.txt plus a dedicated AI welcome guide, but discovery still is not execution. WebMCP is the missing action contract. I would add endpoint-level idempotency and rate limits to the checklist, because an in-browser confirmation does not prevent duplicate calls after an agent retries a request.
For icon-only controls, do you derive the tool description from the same accessible name as the UI? That could prevent WebMCP and ARIA semantics drifting apart.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.