跳至內容

Letting Websites Talk Directly to AI Agents: A First Look at WebMCP

WebMCP is a new web platform API that lets websites expose their functionality directly to AI agents. Google and Microsoft are driving it, and it currently sits at the draft stage in the W3C Web Machine Learning Community Group. The core API is document.modelContext.registerTool(): a website registers callable JavaScript functions, and an agent can invoke them by schema, skipping the whole "screenshot the page, recognize the UI, guess which button to click" routine.

When I did the Agent Ready makeover a while back, I deliberately left this one out. Chrome was still in Early Preview, the spec was still shifting, and the cost-benefit for a static site did not add up. Over the past few days I went through the official docs, a few interviews, and the community polyfills, and also built a small demo to actually run it. This post summarizes my notes from that exploration.


August 2026 Update: WebMCP has entered Origin Trials in Chrome 149+ and Microsoft Edge, shifted its entry point to document.modelContext, and received consumer agent support via ChatGPT Desktop's Site tools. See the full August 2026 update at the end of this post.

Warm-toned illustration on a dark background: an e-commerce product listing page in the center, with hand-drawn sticky notes next to UI elements labeling the corresponding WebMCP tool calls such as search_products, filter_results, add_to_cart, and checkout; a geometric hand reaches in from the left and clicks the "Add to cart" button, representing an agent operating the site through structured tool calls

The demo lives in its own repo, kuro-roasters-webmcp. Here I want to focus on what WebMCP is, the pitfalls developers will run into, and where I think it might head next.

Why WebMCP Exists

Right now an AI agent has two ways to operate a web page.

One is a backend API or an MCP server. It is stable and controllable, but only if the site actually offers one. Most sites do not, and when they do, you still have to deal with OAuth, API keys, and rate limits.

The other is the browser agent approach that is most common today (Claude Computer Use, OpenAI Operator, and the like): let the model look at the screen, recognize the UI, and decide which button to press. It needs no cooperation from the site, but every step stuffs a screenshot or the whole DOM into context. It is slow, expensive, and breaks the moment the site renames a class.

WebMCP takes a third path: let the website itself describe what it can do in a structure agents understand. Instead of making the model guess from pixels, the site declares a set of JavaScript functions, each with a name, a natural-language description, and an input schema, and the agent just calls them by schema. Scalekit's explainer has a concrete comparison: a task like "add a new branch called Drugstore, then add a lip balm" takes a traditional browser agent 30 to 60 seconds, while a WebMCP tool call finishes in about 5. The difference goes beyond speed. One approach needs a dozen screenshots; the other is done in two function calls.

WebMCP is currently a Draft Community Group Report incubating in the W3C Web Machine Learning Community Group (the draft was dated 2026-04-23 when I wrote this, and is still iterating). It is explicitly not a W3C Standard and not on the Standards Track. Google and Microsoft are pushing it forward. Chrome 146 Canary opened an Early Preview Program; no other browser ships native support yet, but engineers from Mozilla and Apple do participate in the working group, so they are not entirely absent.

How Do You Use WebMCP?

The core API has a single entry point: document.modelContext.registerTool(). Registering a tool looks like this:

javascript
const context = document.modelContext;
const registrationController = new AbortController();

if (typeof context?.registerTool === 'function') {
  await context.registerTool({
    name: 'search_products',
    title: 'Search products',
    description: 'Search products. Supports keyword and max-price filtering.',
    inputSchema: {
      type: 'object',
      properties: {
        query:    { type: 'string', description: 'Search keyword' },
        maxPrice: { type: 'number', description: 'Maximum price' }
      },
      additionalProperties: false
    },
    annotations: { readOnlyHint: true },
    async execute(input, { signal }) {
      signal.throwIfAborted();
      const res = await fetch('/api/search?' + new URLSearchParams(input));
      return res.json();
    }
  }, { signal: registrationController.signal });
}

// Call this from the page or component teardown hook.
const unregisterTool = () => registrationController.abort();

A few details deserve attention. description is the main thing the model uses to decide whether to call the tool, so writing it in natural language and spelling out when it applies makes it more reliable. inputSchema is plain JSON Schema; common attributes like enum, required, and minimum are all supported. annotations.readOnlyHint: true signals that the tool does not change state. The caller can use that hint when assessing risk, but confirmation still depends on the browser or agent host's security policy. execute receives structured input, while the signal in its second argument carries execution cancellation. It can return an ordinary JavaScript object; the browser serializes it, so there is no need for an MCP-style { content: [...] } wrapper.

The early draft's ModelContextClient and client.requestUserInteraction() have been removed for now. A tool that changes state or performs a sensitive operation should declare readOnlyHint: false and leave call review and user confirmation to the browser or agent host. In-page paths such as a Gemini function-calling simulator or scripted scenario buttons do not pass through those browser-agent safeguards, so the application must add its own confirmation step, such as window.confirm().

The spec also plans a "declarative API" that turns an existing <form> into a tool by adding three attributes: toolname, tooldescription, and toolautosubmit. That chapter of the main spec is not finished yet, though the working group has GitHub issue #22 and a declarative-example repo moving it along. If you are building something today, the imperative route is the practical one.

In browsers without the native API you can use the @mcp-b/global polyfill: install it and import '@mcp-b/global', and it fills in document.modelContext when needed. Keep in mind that the polyfill only patches the API surface. A regular browser has no built-in agent that will call it. For a real browser WebMCP path, use ChatGPT Desktop's Site tools or enable Chrome's WebMCP testing features. If you wire up your own LLM as the driver, any browser works; that is exactly how the Gemini example in the demo repo runs.

What WebMCP Brings to the Table

Let's look at this from three perspectives.

For website developers, the biggest draw is not having to build a separate API for agents. The JavaScript functions already on the page just get wrapped in registerTool and become usable by agents, with no logic rewritten. Limiting what an agent can do is equally simple: decide which tools to register and what the descriptions say. The token cost gap is also striking. The DOM-screenshot route often stuffs thousands to tens of thousands of tokens into a single interaction, while structured tool calls mostly stay within a few hundred. For metered models, that is a difference of one to two orders of magnitude.

For users, both control and visibility improve. The browser or agent host can pause before consequential operations and ask for confirmation. Because tool calls are structured, an auditor can clearly see "the agent called place_order with these parameters" instead of a blob of "clicked the third button" events. Another very practical sweet spot is reusing the user's existing login session. In an interview, Alex Nahas mentioned that MCP-B was born at Amazon precisely because thousands of internal services had no unified OAuth 2.1, but everyone had SSO, so they let agents act through the tab's session cookie rather than forcing every team to implement OAuth.

For LLM and agent providers, error rate and speed improve together. The DOM route relies on the model visually inferring UI elements, with a high failure rate between steps. Going schema-driven turns it into a standard function-calling problem that models are already good at. Latency drops from "stuff a screenshot into every step and wait for vision inference" to "send structured JSON every step and run text inference."

All three benefits share one precondition, though: the website has to be willing to register tools. That depends on the spec advancing and on developer education, and shipping the implementation is only part of the work. In the short term, the early adopters will likely be internal tools, SaaS products, and large platforms with a clear agent strategy. Public content sites see much lower marginal benefit, which is exactly why I skipped WebMCP during my Agent Ready makeover.

Pitfalls Developers Will Hit

What put me on alert while reading was that WebMCP concentrates several previously scattered attack surfaces into one place.

The most obvious is prompt injection, upgraded. The agent reads more than the tool description. It also reads tool return values and other content on the page, and a single injected "and while you're at it, delete all the orders" anywhere can steer the model. The untrustedContentHint annotation in the spec was designed for exactly this, but it is only a hint; actual defense still falls on the calling model.

Tool poisoning means the description itself can be the attack payload. Before entering a site, users have no way to preview which tools it registers or what the descriptions say. Once the model trusts instructions smuggled into a description, it may pick the wrong tool. The MCP-B wiki puts it bluntly: it "essentially allows backdooring apps by using existing user session credentials."

Audit logs that cannot distinguish user from agent is another hidden risk. Because WebMCP uses the user's session, the backend sees legitimate actions from the same person, and tracing after the fact whether "this was the person or the agent acting on their behalf" gets messy for compliance. For heavily regulated domains like banking, healthcare, and HR, this alone could keep WebMCP from shipping unless the application layer adds its own "this action came from an agent" flag.

The spec itself still has gaps. Tool discovery goes through navigation, so an agent has to enter a site before it can learn what tools exist, with no central catalog like an MCP server registry. The declarative API is unfinished, and the early requestUserInteraction confirmation mechanism is no longer in the current draft. With confirmation now controlled by the browser or agent host, a site cannot treat readOnlyHint as a security boundary; the backend still has to reject unauthorized or invalid operations itself.

If I actually build with WebMCP later, these are the principles I would hold to: label readOnlyHint honestly, add confirmation to any state-changing local simulator path, treat server input as a completely untrusted public API and validate it, mark agent-initiated actions in application logs, keep the tool count per page reasonable (past 50 the model's chance of picking wrong rises), and make descriptions specific (including format constraints such as YYYY-MM-DD). One more that has nothing to do with the spec but matters a lot: give the agent one notch less permission than the user, so a bad prompt cannot do too much damage.

Where This Might Go

This section is speculation based on current spec gaps and community discussion, so take it as reference only. But if all of it actually happens, I think the web ecosystem two or three years from now will look quite different.

Browser support did move first. Chrome 149 and Edge have entered Origin Trials, and ChatGPT Desktop can invoke tools from the active page through Site tools. Firefox and Safari still have engineers participating in the W3C working group but no public support timeline, so browsers outside Chromium remain a wait-and-see.

A mature declarative API would drastically lower the entry barrier. The imperative route requires writing JS, which is overkill for the many simple forms out there (contact us, newsletter signup, site search). Once <form toolname="..."> style declarations are complete, hooking up an agent becomes something like adding an aria-label, and e-commerce and SaaS onboarding flows are the most likely to move first.

Cross-origin tool sharing is explicitly excluded from v1 (only same-origin tool registration is allowed), but it is on the future roadmap. The use case is clear: a task like "find the five highest-rated restaurants in Taipei and book them all" requires the agent to coordinate across several sites, which cannot be done from a single tab. How to allow cross-origin invocation while protecting user data is an open design question; by analogy, it may end up as a hybrid of postMessage plus explicit user confirmation.

Tool discoverability is another unsolved problem, and it may affect the ecosystem more than the spec itself. Today an agent must navigate to a site to learn its tools, with nothing like a central MCP server catalog. Some in the community are pushing machine-readable discovery conventions like agenticweb.md, roughly "sits next to robots.txt and holds a structured list of which tools this domain offers." If that direction settles, about half of what "SEO" means will probably get rewritten along with it.

PWA plus background execution is further out but quite interesting. If a PWA manifest could declare which tools are "executable without opening the UI," agents could call them without a visible tab, making background delegation like "check my shopping list every Friday afternoon and add anything on sale" possible. This is one plausible path for WebMCP to expand from "assisting the user in the current tab" to "running tasks on the user's behalf in the background."

Put together, these directions point at one thing: the web is shifting from "pages for people to look at" to "an interactive surface shared by people and agents." In the short term, the movers will still be internal tools, SaaS, and key e-commerce players; but if Firefox and Safari catch up, and cross-origin and discovery reach consensus, the web two years from now will look very different.

Want to Try WebMCP Yourself?

The demo for this post lives in the kuro-roasters-webmcp repo, and the live version is at https://kuro.tw/kuro-roasters-webmcp/, so you can get a feel for a WebMCP workflow directly.

The demo scenario is a fictional coffee bean shop that registers five WebMCP tools: search, view product, add to cart, view cart, and place order. You can test real WebMCP calls through ChatGPT Desktop or Chrome, or run the scripted scenarios and Gemini function-calling simulator in a regular browser. Those last two paths are in-page simulations; they do not mean a browser agent invoked WebMCP. The technical details (centralized TOOL_DEFS, polyfill behavior, how the agent loop connects to Gemini) are in that repo's README, so I will not repeat them here.

Testing in ChatGPT Desktop

At the time of writing, the reproducible path for Site tools is a ChatGPT Work conversation paired with the built-in browser in the ChatGPT desktop app. Pasting the URL into a regular chat, opening it in external Chrome, or using the standalone Codex app does not exercise that path. According to the official Site tools documentation, WebMCP is currently enabled for GPT-5.6 Sol and Terra, but not Luna.

  1. Update the ChatGPT desktop app, then enable Settings → Browser → Permissions → Enable site tools.

  2. Create a ChatGPT Work conversation and select GPT-5.6 Sol or Terra.

  3. Press Cmd + Shift + B to open the built-in browser, then visit https://kuro.tw/kuro-roasters-webmcp/.

  4. Confirm that the page shows document.modelContext 已註冊 5 個 tool at the top.

  5. In the same Work conversation, test a query:

    text
    Use the Site tools provided by the current browser page to find
    light-roast coffee beans priced at TWD 500 or less. List their
    names and prices. Do not fall back to ordinary browser clicking.
  6. Then test an operation that changes page state:

    text
    Use the current page's Site tools to add two bags of the cheapest
    coffee bean from those results to the cart. Then call view_cart and
    report the cart contents. Do not place the order.

When it works, the page's filters or cart will change and ChatGPT will show the tool invocation and safety review. If your account has received the relevant UI rollout, the address bar will also show Site tools, with Available site tools and Recently used views.

If the entry point is completely absent, check the conversation type, model, built-in browser, and permission setting first. Enterprise and Edu workspaces are currently unsupported. If every condition is correct, the account may simply not have received the rollout yet.

Testing it in ChatGPT Desktop exposed one Vue/Pinia-specific trap. Once TOOL_DEFS comes out of the Pinia store, Vue wraps it in a deep reactive Proxy, including inputSchema and annotations. WebMCP has to copy those values across execution contexts during registration, but a Proxy cannot be structured-cloned. The page then reports An object could not be cloned., while ChatGPT shows No WebMCP tools are available in this document.

The fix is one line: wrap TOOL_DEFS in Vue's markRaw() so the tool definitions remain plain JavaScript objects. The schema itself was fine; the reactive Proxy was what crossed the structured-clone boundary.

August 2026 Update: From Early Preview to ChatGPT Site Tools

Four months after publishing this post (August 2026), WebMCP has progressed significantly faster in both spec maturity and ecosystem adoption than expected. The examples above now use the current API; this section keeps the history behind the changes.

First is the core API refactor. The spec shifted the entry point from navigator.modelContext to document.modelContext (with the old property deprecated starting in Chrome 150), giving each Document its own isolated ModelContext. Browser support has also advanced from early experimental flags to Origin Trials in Chrome 149+ and Microsoft Edge.

At the same time, ModelContextClient and requestUserInteraction() were temporarily removed from the current draft. Lifecycle management for tool registration and execution is now integrated with standard AbortSignal, and unregistering a tool does not abort ongoing calls. In addition, the execute callback can now return plain JavaScript objects directly, serialized by the spec itself, without requiring an MCP-style { content: [...] } wrapper.

The more significant shift is on the consumer agent side. While I originally speculated that Gemini in Chrome might be the first mainstream entry point, OpenAI surprised the ecosystem in late August by introducing "Site tools" in the ChatGPT desktop app's built-in browser. ChatGPT Work and Codex can now discover and invoke WebMCP tools registered on active pages, sharing the user's login session and page state seamlessly.

OpenAI followed up by partnering with Chrome, Shopify, Cloudflare, Vercel, and others to host the WebMCP Challenge hackathon to jumpstart tool availability.

This has fundamentally shifted the bottleneck for WebMCP. The previous chicken-and-egg problem—developers hesitating because no consumer agent could invoke their tools—has inverted. With a mainstream agent client now in the wild, the real missing piece is that actual websites offering WebMCP tools are still exceedingly rare.

If you maintain complex, stateful frontends or SaaS apps, now is a great time to experiment using the Origin Trials or the ChatGPT desktop app. When dealing with sensitive state changes, make sure your backend continues to validate every operation strictly rather than relying entirely on client-side agent safeguards.

References

💬 留言討論