WebMCP: Turning Your Website Into Tools for AI Agents
Posted on: 10/12/2026 1:13:36 AM
Table of contents
- 1. Three ways agents "use" the web today — and why none is good enough
- 2. What WebMCP is, and where it stands
- 3. Architecture: the browser is the mediator
- 4. Declarative API: turn existing forms into tools
- 5. Imperative API: tools for complex SPAs
- 6. WebMCP and MCP: not an either-or
- 7. Designing tools agents use correctly
- 8. Security: tools run with the user's privileges
- 9. The UX angle: agent and user share one screen
- 10. Testing and tooling
- 11. Should you adopt it now? A four-step roadmap
- Conclusion
Watch an AI agent try to book an appointment on your website. It takes a screenshot, guesses which box is "Date", clicks the promo banner by mistake, scrolls, takes another screenshot, reads the accessibility tree, types the date in the wrong format, and waits for the page to complain. Thirty seconds and tens of thousands of tokens for something a person does in five. And the moment your frontend team renames a CSS class, that whole "skill" falls apart.
WebMCP proposes a very different approach: instead of letting the agent guess the interface, the website declares what it allows to be done — as tools with a name, a natural-language description and a JSON Schema for parameters. An agent running in the browser simply calls book_appointment({ date: "2026-10-20", slot: "09:00" }), the website runs it with its own JavaScript and the user's existing session, and the UI updates right in front of them. This article takes WebMCP apart as of October 2026: how it works, the declarative and imperative APIs, how it differs from MCP, the security model, how to design tools agents actually use correctly, and whether you should adopt it now.
1. Three ways agents "use" the web today — and why none is good enough
To see what WebMCP solves, look at the three paths agents currently take to interact with a website:
- Looking at pixels (computer use / vision): take a screenshot, have a multimodal model guess coordinates, click. The most general option, but slow, expensive in image tokens, and very sensitive to layout changes, pop-ups and A/B tests.
- Reading the DOM or accessibility tree: frameworks like Playwright MCP or Browser Use turn the page into structured text so the model can pick elements. Faster, but still reverse-engineering a UI designed for human eyes: the agent has to work out that three inputs and a "Continue" button mean "book an appointment".
- Calling a backend API (MCP server, REST): precise and stable, but detached from the user's browser session. You need separate OAuth, a separate server to maintain, and the user can't see what is happening.
WebMCP is a fourth path: it keeps the browser context (cookies, logged-in session, cart state, the current page) like the first two, but with a clear contract like the third. The website becomes its own "MCP server", living inside the tab, with no new backend.
flowchart LR A["AI agent in the browser"] A --> V["Pixels
screenshot then click"] A --> D["DOM / accessibility tree
guess which element to use"] A --> M["Backend MCP server
separate OAuth, outside the tab"] A --> W["WebMCP
call tools the page declares"] V --> R1["Slow, breaks on layout changes"] D --> R2["Faster but still guessing intent"] M --> R3["Stable but loses session context"] W --> R4["Schema-backed, uses existing session,
user sees the UI update"] style A fill:#2c3e50,stroke:#fff,color:#fff style W fill:#e94560,stroke:#fff,color:#fff style R4 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style V fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style D fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style M fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
2. What WebMCP is, and where it stands
WebMCP (Web Model Context Protocol) is a proposed web standard developed in the W3C Web Machine Learning Community Group, with editors from Microsoft and Google. To be clear up front: it is a Draft Community Group Report, not a W3C Recommendation, and not on the standards track. It carries the MCP name because it borrows MCP's core concepts (tools, descriptions, input schemas), but it does not use MCP's JSON-RPC protocol layer — the browser sits in the middle as the mediator.
navigator.modelContext polyfill for early experiments.navigator.modelContext to document.modelContext, because tools belong to a specific document. Chrome 150 deprecates the old name.debugging annotation arrives and window.ontoolactivated is dropped (156).Set the right expectations
As of October 2026, the number of real websites registering tools is close to zero, and no mainstream agent calls WebMCP at scale — Google says Gemini in Chrome will be the first consumer. Firefox and Safari take part in the discussions but have not committed to a timeline. Treat WebMCP as something to experiment with and prepare for, not a feature your users already have.
3. Architecture: the browser is the mediator
The key point of WebMCP is that the page never talks to the model directly. The page registers tools on document.modelContext; the agent (Gemini in Chrome, an extension, or an external agent driving the browser) asks the browser for the list of tools, picks one, and the browser calls the page's execute function. The result travels back the same way.
sequenceDiagram
actor U as User
participant AG as Agent (e.g. Gemini in Chrome)
participant BR as Browser
participant P as Web page (open tab)
P->>BR: registerTool(name, description, inputSchema, execute)
U->>AG: "Book a check-up next Monday morning"
AG->>BR: getTools()
BR-->>AG: Tool list + JSON Schema
AG->>U: "Monday 19 Oct, 9am or 10am?"
U->>AG: "9am"
AG->>BR: executeTool(book_appointment, {date, slot})
BR->>BR: Check permissions policy, annotations, confirm if needed
BR->>P: Call execute() with existing cookies and session
P->>P: Call internal API, update UI in front of the user
P-->>BR: { content: [...] } or an error message
BR-->>AG: Tool result
AG-->>U: "Booked for 9am Monday 19 Oct"
This architecture has three important consequences:
- Tools are ephemeral and tab-bound. They exist only while the page is open. Navigate away or close the tab and the agent loses access. There is no background "server".
- Tools run with the user's privileges.
executeruns inside the page, sharing cookies, tokens and app state. No separate OAuth for the agent — but it also means a manipulated agent can do anything the user can (see section 8). - The user can see it. Because tools act on the visible UI, users can watch what the agent does and cancel mid-way. That is a big trust difference compared with headless agents working against a backend.
4. Declarative API: turn existing forms into tools
The fastest way to become "agent-ready" is to add a few attributes to forms you already have. The browser derives the JSON Schema from the form fields.
<form toolname="book_appointment"
tooldescription="Book a clinic appointment. Returns a booking code."
action="/appointments" method="post">
<label for="dept">Department</label>
<select id="dept" name="department" required
toolparamdescription="Medical department; determines which doctor is assigned.">
<option value="general">General medicine</option>
<option value="pediatrics">Pediatrics</option>
<option value="dermatology">Dermatology</option>
</select>
<label for="date">Date</label>
<input id="date" name="date" type="date" required
toolparamdescription="Appointment date, format YYYY-MM-DD.">
<label for="note">Symptoms</label>
<textarea id="note" name="note"></textarea>
<button type="submit">Book</button>
</form>
The key attributes:
toolnameandtooldescriptionon the<form>: identify the tool. Removing either one unregisters it.toolparamdescriptionon each field: describes the parameter. If omitted, the browser falls back to the<label>text, thenaria-description— one more reason to write properly labelled, accessible forms.toolautosubmit: when present, the form submits as soon as the agent fills it. When absent, the agent only pre-fills and the user must click the button — the safe default for anything with consequences.
On the JavaScript side, the page can tell that a submission came from an agent and return a structured result instead of navigating away:
form.addEventListener('submit', (e) => {
if (!e.agentInvoked) return; // a human clicked: keep the old behaviour
e.preventDefault();
e.respondWith((async () => {
const res = await fetch('/api/appointments', {
method: 'POST',
body: new FormData(form)
});
if (!res.ok) return 'That slot is full. Call again with a different date.';
const { code, time } = await res.json();
return `Booked for ${time}, code ${code}.`;
})());
});
The page also receives toolactivated (once the agent has pre-filled the fields) and toolcancel (when the user cancels or the form is reset) on document.modelContext — handy for showing an "Assistant is filling this in for you" hint, or for logging.
5. Imperative API: tools for complex SPAs
Not every action is a form. Filtering a list, adding to the cart, switching tabs, reading order status — these need the programmatic API:
// Works with both the old and new drafts
const mc = document.modelContext ?? navigator.modelContext;
if (mc) {
const controller = new AbortController();
await mc.registerTool({
name: 'search_orders',
description: 'Search the current user\'s orders within a date range. ' +
'Returns order number, shipping status and current location.',
inputSchema: {
type: 'object',
properties: {
from: { type: 'string', description: 'Start date, YYYY-MM-DD' },
to: { type: 'string', description: 'End date, YYYY-MM-DD' },
status: { type: 'string', enum: ['pending', 'shipping', 'delivered', 'returned'] }
},
required: ['from', 'to']
},
annotations: { readOnlyHint: true },
execute: async ({ from, to, status }, { signal }) => {
const orders = await api.orders.search({ from, to, status }, { signal });
store.setOrderFilter({ from, to, status }); // the UI follows along
if (orders.length === 0)
return 'No orders found. Try widening the date range or dropping the status filter.';
return { content: [{ type: 'text', text: JSON.stringify(orders.slice(0, 20)) }] };
}
}, { signal: controller.signal });
// When leaving this route:
// controller.abort();
}
Things worth noting:
- Unregister with an
AbortSignal. Pass{ signal }as the second argument and callabort()when the component unmounts. Since Chrome 153, unregistering does not break calls already in flight. executereceives its ownsignalso you can cancelfetchcalls when the agent or user stops mid-way.- Tools should follow application state. The cart page offers
update_quantityandapply_coupon; only the checkout page offersplace_order. Agents listen for thetoolchangeevent to learn the list has changed. Registering per route is a natural way to limit what an agent can do at each step. - Tools in cross-origin iframes are disabled by default through the
toolspermissions policy (defaultself); to enable them, declare<iframe allow="tools">.
In React, Vue or Angular, the common pattern is to wrap registration in a hook or composable: register on mount, abort on unmount, and have execute call straight into your existing store or actions instead of new logic. A tool then becomes just another entry point into the same business logic your buttons already use.
6. WebMCP and MCP: not an either-or
A common misconception is that WebMCP will replace MCP servers. In practice they serve different contexts and are designed to be used side by side:
| Criterion | MCP (server) | WebMCP |
|---|---|---|
| Purpose | Make data and actions available to agents anywhere, anytime | Make an open web page ready for agent interaction |
| Lifecycle | Persistent, runs as a server or daemon | Ephemeral, tab-bound |
| Protocol | JSON-RPC over stdio / Streamable HTTP, SDKs in many languages | Browser API (JS or HTML attributes), browser mediates |
| Concepts | Tools, resources, prompts, sampling... | Tools only (borrowed from MCP), no resources |
| Auth | OAuth or a dedicated mechanism for the agent | Reuses the user's logged-in browser session |
| UI | Headless, invisible to the user | Lives in the DOM; users can watch and cancel |
| Discovery | Register the server with each agent client | The agent must visit the page to learn its tools |
| Best for | Background tasks, system integration, shared data | Acting on a live UI: filters, forms, carts, configuration |
flowchart TB
subgraph Client["User side"]
AG["Agent in the browser"]
TAB["Website tab
WebMCP tools"]
AG -->|"executeTool"| TAB
end
subgraph Backend["System side"]
API["Business API / domain services"]
MCPS["MCP server"]
MCPS --> API
end
DESK["Desktop / IDE / background workflow agent"] -->|"JSON-RPC"| MCPS
TAB -->|"fetch with user session"| API
style AG fill:#2c3e50,stroke:#fff,color:#fff
style TAB fill:#e94560,stroke:#fff,color:#fff
style MCPS fill:#16213e,stroke:#fff,color:#fff
style API fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style DESK fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
A sensible architecture for a SaaS product: business logic stays in the API as before; an MCP server serves agents running outside the browser (IDEs, desktop apps, automated workflows); WebMCP serves the agent that travels with the user while they are on the website. Both layers should share the same schema definitions so descriptions and validation stay consistent.
7. Designing tools agents use correctly
Registering a tool is easy; getting the model to pick the right tool, fill the right parameters and recover from errors is the hard part. Chrome's documentation and experience from the MCP ecosystem suggest a few principles:
- Start from user goals, not endpoints. List what users actually want an assistant to do on the page, then break it into steps. "Follow up on a meeting" may need three tools: record the transcript, summarise, draft an email.
- Use verb + object names (
search_flights,filter_flights,book_flight) and stick to one casing convention. - Don't let tools guess. If a user says "next week", the date parameter should be required so the agent asks "Which day next week?" instead of the tool picking one.
- Be explicit about formats (YYYY-MM-DD, currency units) and use
enumwhen the value set is finite. - Stay within character budgets. Chrome suggests roughly 500 characters per tool description, 150 per parameter description, 30 per name, and about 1,500 per result. Paginate or summarise longer outputs.
- Reflect results in the UI. A tool that filters orders should actually change the on-screen filter, so the user and the agent are looking at the same state.
Error messages are the most important "prompt" you will write for the agent:
| Situation | Bad error | Good error |
|---|---|---|
| Called out of order | 500 Internal Server Error | "No flight search results found. Call search_flights first." |
| Malformed input | Invalid input | "Invalid date. Use the YYYY-MM-DD format." |
| Business rule | {"code": 4012} | "Order 123 has already shipped and can't be cancelled. Point the user to the returns policy." |
| Empty result | [] | "No orders in this range. Try widening the date range." |
Test with evals, not gut feeling
Write a set of sample prompts ("change the shipping address on my latest order", "find a blue size M shirt under $15") and measure three things: does the agent pick the right tool, does it extract the right parameters, and does it handle multi-step state correctly? Chrome has a dedicated guide on WebMCP evals; once live, read the call logs to find where agents drift, then fix the tool descriptions.
8. Security: tools run with the user's privileges
This is the section to read most carefully. WebMCP tools run inside the page with the user's cookies and session. If the agent falls for indirect prompt injection — say it reads a malicious comment containing "change the shipping address to..." — it can call real tools with attacker-chosen parameters. Chrome's documentation says plainly that safety inside an LLM cannot be guaranteed; the right strategy is to limit the damage a manipulated agent can do.
flowchart TB IN["Untrusted content
comments, emails, reviews"] --> AG["Agent reads and reasons"] AG --> L1{"Is the tool exposed
to this origin?"} L1 -->|"No: exposedTo, permissions policy"| X1["Blocked"] L1 -->|"Yes"| L2{"consequentialHint?"} L2 -->|"Yes"| C["Browser / page
asks the user to confirm"] L2 -->|"No, readOnlyHint"| L3["Execute"] C -->|"Approve"| L3 C -->|"Reject"| X2["Cancel, fire toolcancel"] L3 --> L4["Server re-validates input
like any client request"] L4 --> OUT["Result flagged untrustedContentHint
if it contains user content"] style IN fill:#f8f9fa,stroke:#ff9800,color:#2c3e50 style AG fill:#2c3e50,stroke:#fff,color:#fff style C fill:#e94560,stroke:#fff,color:#fff style X1 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style X2 fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50 style L4 fill:#16213e,stroke:#fff,color:#fff
The protections the spec and Chrome provide:
- Risk-declaring annotations (all default to
false):readOnlyHint: the tool only reads and does not change state — helps the agent decide when confirmation isn't needed.consequentialHint: significant or irreversible actions (payments, bookings, deletions) — lets the browser require user confirmation.untrustedContentHint: the result contains user-generated or external content — tells the agent to delimit and treat it cautiously, reducing indirect prompt injection.
requestUserInteraction()in the draft spec: a tool can actively ask the user for confirmation or extra input during execution.- Origin isolation. By default, other sites and cross-origin iframes can't see your tools; sharing requires listing origins in
exposedTo. Even read-only tools can leak user data, so only expose them to partners you would share that data with directly. - HTTPS only (secure context), governed by the
toolspermissions policy.
What annotations won't do for you
Annotations are hints to the agent and browser, not a security boundary. Your server must still check authorisation and validate input as it would for any browser request. Expose the narrowest tools possible (update_shipping_address for an order in progress, not a generic update_profile), require confirmation for every side effect, and log every call with an "agent-initiated" flag. Also remember that browser extensions with access to the page can call tools via content scripts — and could already manipulate the page without WebMCP.
9. The UX angle: agent and user share one screen
WebMCP is interesting for UX people because it enables a co-pilot model: the agent acts, the user watches and can step in. For that to feel trustworthy, the interface must show what is happening.
Chrome adds two pseudo-classes: :tool-form-active for a form the agent is driving and :tool-submit-active for its submit button. By default the browser draws a dashed outline; you can restyle it to match your design system:
form:tool-form-active {
outline: 2px dashed var(--brand-accent);
outline-offset: 6px;
}
form:tool-form-active::before {
content: "Your AI assistant is filling this in — review before sending";
display: block;
font-size: .875rem;
margin-bottom: .5rem;
}
button:tool-submit-active {
box-shadow: 0 0 0 3px var(--brand-accent);
}
UX principles worth adopting:
- Default to no auto-submit for consequential actions: the agent fills, the user clicks. That is the cheapest human-in-the-loop you can get.
- Leave a trail. When a tool filters a list or switches a tab, update the UI and URL as if the user had done it, so they can carry on from there.
- Make cancelling easy. Listen for
toolcancelto restore state and never leave a form half-filled. - A form that works for people works for agents. Clear labels,
required, the righttype, specific error messages — things accessibility has demanded for years now directly make agents more accurate.
10. Testing and tooling
- Enable the flag
chrome://flags/#enable-webmcp-testingto use WebMCP in development without an origin trial token. - Model Context Tool Inspector: an extension that lists a page's tools, lets you run them, and uses natural-language prompts to check whether an agent calls the right one (bring your own Gemini API key).
- On-device model: enable
chrome://flags/#prompt-api-tool-useto test with the Prompt API without an API key. - DevTools lets you debug tools and inspect how the browser parses the JSON Schema — especially useful for declarative tools.
- Lighthouse includes audits for registered tools, form coverage and schema validity; pages without WebMCP get "Not Applicable".
- The MCP-B polyfill provides the API in browsers without native support, convenient for testing with extension-based agents.
11. Should you adopt it now? A four-step roadmap
For most product teams the sensible answer is: prepare now, enable gradually, measure carefully. Most of the preparation pays off even if WebMCP never becomes a standard.
flowchart LR S1["Step 1
Clean up forms and business actions
labels, schemas, clear errors"] --> S2["Step 2
Declarative on 2-3 low-impact forms
no auto-submit"] S2 --> S3["Step 3
Imperative read-only tools
readOnlyHint, per route"] S3 --> S4["Step 4
Write tools with confirmation
consequentialHint, logs, evals"] style S1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style S2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style S3 fill:#2c3e50,stroke:#fff,color:#fff style S4 fill:#e94560,stroke:#fff,color:#fff
- Clean up first. Consolidate business actions into clear functions (book, search orders, add to cart) with validation schemas shared by the UI, the API and your MCP server. Properly labelled forms, meaningful errors.
- Declarative on a few safe forms: search, filters, newsletter sign-up. Never use
toolautosubmiton anything that costs money. - Imperative read-only tools: order lookup, plan comparison, stock checks. Register per route, mark
readOnlyHint, adduntrustedContentHintif you return reviews or comments. - Write tools last, with
consequentialHint, user confirmation, full logging, server-side rate limits and an eval suite running in CI.
Always feature-detect (document.modelContext ?? navigator.modelContext) and treat WebMCP as progressive enhancement: in browsers without support the site still works normally; agents simply fall back to pixels or DOM reading.
Conclusion
For more than twenty years we have optimised websites for two kinds of "reader": humans and search engine bots. WebMCP is the first attempt at the web-standards level to add a third — AI agents — without forcing them to pretend to be human. The core idea is simple: instead of letting the agent guess the interface, tell it what the page can do, with a clear contract and clear security boundaries.
The standard is young: no engine beyond Chromium has committed, the API just moved to document, and no mainstream agent really uses it yet. But the preparation — business actions as schema-backed functions, well-labelled forms, error messages that help the reader recover, confirmation for every important action — makes your product better today, for users and agents alike. A small experiment for your next sprint: take your search form, add toolname and tooldescription, flip the flag, and watch how an agent uses it.
References
- Chrome for Developers — WebMCP: Get started
- Chrome for Developers — WebMCP Declarative API
- Chrome for Developers — WebMCP Imperative API
- Chrome for Developers — WebMCP versus MCP
- Chrome for Developers — Build effective tools
- Chrome for Developers — WebMCP tool security
- W3C Community Group — WebMCP Technical Notes
- Spronta — The State of WebMCP: July 2026
- OpenHermit — navigator.modelContext: Chrome's WebMCP API and What It Means for Agent-Ready Websites
- Ivan Turković — How to Make Your Website Agent-Ready With WebMCP
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.