# WebMCP: Making Your Site Usable by AI Agents

Published: July 13, 2026
Tags: ai, webdev, javascript, mcp

WebMCP lets a web page expose typed tools that browser AI agents call directly, no DOM scraping. What it is, how to try it in Chrome, and the caveats.

A while back I wrote about [configuring this site for AI discoverability](/blog/configuring-my-site-for-ai-discoverability): raw Markdown mirrors, `llms.txt`, an AI stance in `robots.txt`. All of that is about making a site *readable*. An agent fetches your prose cheaply, summarizes it, and moves on. The agent reads, but it can't do anything on your page.

WebMCP is the other half. Instead of handing an agent your text to read, you hand it a set of typed functions to call: search your posts, filter a product list, submit a form. The page declares what it can do, and an AI agent running in the browser invokes those actions directly.

It's early and experimental, Chromium-only behind a flag, and the spec changes almost weekly. But it's the first serious attempt at answering "how do I make my site *usable* by agents, not just legible to them," so it's worth understanding now.

## What is WebMCP?

WebMCP is a proposed web API that lets a page expose structured **tools** to an AI agent: JavaScript functions, each with a natural-language description and a JSON Schema for its parameters, that an in-browser agent can discover and call. In [Chrome's own words](https://developer.chrome.com/docs/ai/webmcp), it's "a proposed web standard to help you build and expose structured tools for AI agents."

The name is the giveaway. It's [Model Context Protocol](https://modelcontextprotocol.io) adapted for the browser. A normal MCP server is a separate backend process that advertises tools to a model over a protocol. WebMCP takes that same tool model, name, description, input schema, and a result payload, and moves it into the page itself. Your page becomes, in effect, a client-side MCP server. The tools are defined *and* executed in your own JavaScript context, reusing the session the user is already logged into. No extra backend, no separate auth handshake.

## Why not just let the agent read the page?

Because reading a page to *act* on it is expensive and fragile. Today an agent that wants to click a button downloads your DOM, maybe screenshots the page, infers which element is the "add to cart" control, and computes where to click. That burns tokens, adds latency, and breaks the moment a layout shifts or an ad loads late. You're asking a model to reverse-engineer an interface that was built for eyes and a mouse.

WebMCP replaces that guesswork with an explicit contract. The page says "here is a `search_posts` tool, it takes a `tag`, here's what it returns," and the agent calls it like a function. It's the difference between scraping a form and being handed its API.

It also fills a gap the discoverability tools don't:

| Mechanism | What it gives an agent | Direction |
| --- | --- | --- |
| `llms.txt` / Markdown mirrors | Your content, cheaply | Read-only |
| Remote MCP server | Tools, via a separate backend + its own auth | Actionable, off-page |
| **WebMCP** | Tools, in the page, on the user's session | Actionable, in-page |

`llms.txt` tells an agent what you've written. A remote MCP server exposes actions but lives on its own infrastructure with its own OAuth story. WebMCP is the in-page option: actions that run in the tab the user already has open, with the auth they already have.

## What does the API look like?

You register tools on `document.modelContext`. Each call defines one tool with four keys: a `name`, a `description` the agent reads to decide when to use it, an `inputSchema` (JSON Schema) for the arguments, and an async `execute` function that does the work.

```js
await document.modelContext.registerTool({
  name: "search_posts",
  description: "Search blog posts by tag. Returns matching titles and URLs.",
  inputSchema: {
    type: "object",
    properties: {
      tag: { type: "string", description: "A single lowercase tag, e.g. 'webdev'" },
    },
    required: ["tag"],
  },
  execute: async ({ tag }) => {
    const posts = await findPostsByTag(tag);
    return `Found ${posts.length} posts tagged ${tag}.`;
  },
});
```

That example returns a plain string, which the [imperative API docs](https://developer.chrome.com/docs/ai/webmcp/imperative-api) accept. The spec actually types the return as `any`, so a string is fine, but if you want to match what a regular MCP tool sends back you'd return a [`{ content: [{ type: "text", text: "…" }] }` object](https://modelcontextprotocol.io/specification/2025-06-18/server/tools). WebMCP mirrors that convention without enforcing it. Tools are registered one at a time, and a `toolchange` event fires whenever the set changes, so other frames can react when you add or remove a tool.

One trap if you follow older tutorials: the entry point used to be `navigator.modelContext`. That's **deprecated as of Chrome 150** in favor of `document.modelContext`. Write the new one; a lot of early write-ups still show the old surface.

There's also a declarative flavor where you annotate existing `<form>` elements and Chrome exposes them as tools automatically. Chrome documents it, but the spec itself hasn't defined it yet, its section is literally marked as a TODO pointing back to the explainer. Treat that one as a preview of a preview.

## How do you try it today?

In Chrome, behind an origin trial. Per the [Chromium "Intent to Experiment"](https://groups.google.com/a/chromium.org/g/blink-dev/c/gmYffo5WOE8/m/OJxuQRP3AAAJ), WebMCP runs as an origin trial from **Chrome 149 through 156**, on desktop first, plus Android and WebView. You can [sign up for the trial](https://developer.chrome.com/blog/ai-webmcp-origin-trial) to enable it for real users, or flip it on locally at `chrome://flags/#enable-webmcp-testing`.

The cross-browser picture is the honest catch. In the same filing, Mozilla (Gecko) and WebKit are both recorded as **"No signal."** And despite Microsoft co-authoring the spec, there's no shipping Edge support I could find. So right now this is a Chromium-only experiment. Don't build anything on it expecting Firefox or Safari to follow soon; they haven't said they will.

## Is WebMCP an actual standard?

Not yet, and it's worth being precise about that. WebMCP is incubated in the [W3C Web Machine Learning Community Group](https://webmachinelearning.github.io/webmcp/), and the current draft is dated **July 10, 2026**. A Community Group Report is explicitly *not* a W3C Standard and not on the Recommendation track. It's a proposal with momentum, edited by engineers from Google and Microsoft, but it can still change shape or stall.

The origin story is a good reminder of how early this is. WebMCP grew out of **MCP-B**, a browser extension built by [Alex Nahas](https://github.com/MiguelsPizza). His insight was that the browser already solves the hard part of agent auth, you're logged in, so run MCP *inside* the browser and inherit that session instead of standing up a server and an OAuth flow for every tool. That extension work converged into the W3C proposal and got renamed WebMCP. He built MCP-B in January 2025, and the draft is dated July 2026, so we're about eighteen months out from a browser extension becoming a draft spec. Worth keeping in mind before you plan a roadmap around it.

## What about security?

This is the part I'd read before shipping anything, because handing a language model a set of callable actions on your users' authenticated sessions is exactly as dangerous as it sounds. The [security model](https://developer.chrome.com/docs/ai/webmcp/secure-tools) gives you a few knobs:

- **`exposedTo`** allowlists which origins can see a tool. Tools aren't exposed cross-origin by default.
- **`untrustedContentHint`** flags a tool's output as untrusted (user-generated or external) so the agent treats it with more suspicion, a prompt-injection signal.
- **`readOnlyHint`** marks a tool that doesn't change state, so an agent can skip a confirmation prompt. The docs still warn that even a read can leak information, so only expose to origins you trust.
- **`requestUserInteraction()`** lets a tool pause mid-execution to ask the user to confirm.

But the docs are refreshingly blunt about the ceiling here: prompt injection is not solved. They acknowledge that repeatable prompt-injection attacks have succeeded against state-of-the-art models, and that because models are probabilistic, safety can't be guaranteed inside the model itself. Origin gating, the hints, and human confirmation are defense in depth, not a guarantee. If you expose a destructive action as a tool, assume it can be tricked into firing.

## Should you add it to your site?

Not to production, not yet. It's Chromium-only, gated behind an origin trial that expires at Chrome 156, the declarative API isn't specified, no other engine has signaled support, and the spec is moving week to week. Anything you build today you'll be rewriting.

But it's worth prototyping, and worth understanding, because the shape of it is right. My [discoverability post](/blog/configuring-my-site-for-ai-discoverability) was about the read side: serve agents a version of your content they can consume cheaply. WebMCP is the write side. It's the first proposal that treats agent interaction as an explicit, typed contract the page controls, rather than something agents reverse-engineer from your DOM. Same instinct that made [the QUERY method](/blog/the-new-http-query-method) satisfying: the platform finally growing a real name for something we'd been faking.

Maybe it ships as WebMCP, maybe the standards process files it down into something else. Either way, sites are going to start declaring what agents can do, not just what they can read. I'd rather figure that out while it's a flag in Chrome than after it's a default everywhere.
