WebMCP: Turning Your Website Into Tools for AI Agents

Posted on: 10/12/2026 1:13:36 AM

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.

149–156Chrome versions covered by the WebMCP origin trial announced at Google I/O, May 2026
2ways to declare tools: Declarative (HTML attributes on forms) and Imperative (JavaScript)
~1,500characters — the recommended budget for each tool result returned to the agent
0public commitments from Firefox and Safari as of October 2026

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
Figure 1: Four ways an agent interacts with a website, and the trade-offs of each

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.

2025
Microsoft and Google publish the WebMCP explainer; the MCP-B community ships a navigator.modelContext polyfill for early experiments.
February 2026
Chrome opens an early preview of WebMCP behind a flag in Canary.
May 2026
Google I/O announces an origin trial from Chrome 149 to 156. Official WebMCP docs land on developer.chrome.com; Lighthouse adds WebMCP-related audits.
June 2026
Edge 147 ships experimental support behind a flag.
July 2026
The 21 July draft moves the API from navigator.modelContext to document.modelContext, because tools belong to a specific document. Chrome 150 deprecates the old name.
Chrome 153–156
Unregistering a tool no longer breaks in-flight calls (153); JSON-stringified input arguments are deprecated (155); a 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"
Figure 2: The lifecycle of a WebMCP tool call, from the user's request to the UI update

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. execute runs 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:

  • toolname and tooldescription on the <form>: identify the tool. Removing either one unregisters it.
  • toolparamdescription on each field: describes the parameter. If omitted, the browser falls back to the <label> text, then aria-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 call abort() when the component unmounts. Since Chrome 153, unregistering does not break calls already in flight.
  • execute receives its own signal so you can cancel fetch calls when the agent or user stops mid-way.
  • Tools should follow application state. The cart page offers update_quantity and apply_coupon; only the checkout page offers place_order. Agents listen for the toolchange event 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 tools permissions policy (default self); 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:

CriterionMCP (server)WebMCP
PurposeMake data and actions available to agents anywhere, anytimeMake an open web page ready for agent interaction
LifecyclePersistent, runs as a server or daemonEphemeral, tab-bound
ProtocolJSON-RPC over stdio / Streamable HTTP, SDKs in many languagesBrowser API (JS or HTML attributes), browser mediates
ConceptsTools, resources, prompts, sampling...Tools only (borrowed from MCP), no resources
AuthOAuth or a dedicated mechanism for the agentReuses the user's logged-in browser session
UIHeadless, invisible to the userLives in the DOM; users can watch and cancel
DiscoveryRegister the server with each agent clientThe agent must visit the page to learn its tools
Best forBackground tasks, system integration, shared dataActing 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
Figure 3: The MCP server and WebMCP sit on the same business API — two doors for two contexts

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 enum when 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:

SituationBad errorGood error
Called out of order500 Internal Server Error"No flight search results found. Call search_flights first."
Malformed inputInvalid 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
Figure 4: Layers of defence around a WebMCP tool call

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 tools permissions 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 toolcancel to restore state and never leave a form half-filled.
  • A form that works for people works for agents. Clear labels, required, the right type, 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-testing to 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-use to 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
Figure 5: A WebMCP adoption roadmap from low to high risk
  1. 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.
  2. Declarative on a few safe forms: search, filters, newsletter sign-up. Never use toolautosubmit on anything that costs money.
  3. Imperative read-only tools: order lookup, plan comparison, stock checks. Register per route, mark readOnlyHint, add untrustedContentHint if you return reviews or comments.
  4. 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.