Logo
Agentailor

· 17 min read

Before You Add WebMCP: the Security Problem Nobody's Selling You

WebMCP lets a web page hand tools to AI agents, and most coverage sells it as your website becoming an API. The spec, WebKit, and even ChatGPT say something the pitch leaves out: an agent cannot verify what a page tool does, and it runs with your users logged-in session. I shipped both sides of WebMCP on Agentailor, red-teamed them, and deleted one. Here is what that taught me.

avatarAli Ibrahim@ialijr/

Agents are easier to build with a spec. Talk yours through in a few questions, then hand the brief to your coding agent.

Plan it with Agentailor

Most of what you will read about WebMCP says the same thing: your website is about to become an API for AI agents. Register a few tools, and every agent in the browser can search your catalogue, fill your forms, and complete tasks without scraping a single pixel.

That part is true. Here is a part you will find less often, from the specification itself:

There is no guarantee that a WebMCP tool's declared intent matches its actual behavior.

And a few lines further down, the list of what a tool can do with nothing more than the user's existing session: make purchases, transfer funds, modify account settings, share private data with third parties, delete user content.

None of this is hidden. It is in the spec's own security section. Apple's WebKit team cited it when they formally opposed the proposal. ChatGPT's documentation warns about it. It just rarely makes it into the posts telling you to ship WebMCP this quarter.

I built both halves of WebMCP for Agentailor. The Agentailor site and this blog publish tools that browser agents can call. And I built a consumer into the Agentailor agent, the chat widget on this blog, so it could operate those tools on the reader's behalf. I red-teamed it. Then I deleted the consumer and kept the tools.

This article is why.

What this article covers

  1. What WebMCP is, and how it differs from the MCP you already know.
  2. The two roles, producer and consumer, and why they carry very different risk.
  3. Where WebMCP actually stands across browsers.
  4. The producer's attack surface, with the Agentailor site and blog as the example.
  5. The consumer's attack surface, with the Agentailor agent and its red-team results.
  6. Why I removed the consumer, and a checklist for each side.

A note on scope. This article describes attack classes and their consequences, not how to carry them out. Everything here is either in the WebMCP spec, in published research, or at the level of "this category of attack exists". If you are looking for payloads, they are not here.

What WebMCP is

WebMCP is a browser API that lets a web page register tools: JavaScript functions with a name, a description, an input schema, and a few annotations. An AI agent in the browser discovers them and calls them instead of clicking through the page.

document.modelContext.registerTool({
  name: 'search_articles',
  description: 'Search Agentailor blog articles by topic or keyword.',
  inputSchema: {
    type: 'object',
    properties: { query: { type: 'string' } },
    required: ['query'],
  },
  annotations: { readOnlyHint: true },
  async execute({ query }) {
    // runs in the page, with the page's origin, cookies and session
  },
})

The name suggests it is MCP in the browser. It shares the vocabulary: tools, schemas, annotations. But the security model is different in the ways that matter.

MCPWebMCP
Where a tool runsA server process you deployPage JavaScript, in the user's tab
Whose credentialsThe server's own (API keys, OAuth)The user's logged-in browser session
RegistrationThe server declares its toolsThe page registers them while running, and can change them at any moment
TransportA defined wire protocolMediated by the browser; not an MCP binding
Who calls itA host app you configuredBrowser agents, extensions, agents embedded in pages
AnnotationsSelf-declared by the authorSelf-declared by the author

The last row is the same for both, and that is the point. MCP annotations like readOnlyHint were never verifiable either. What changes with WebMCP is the rows above it. The tool author is now a web page, the code runs inside the user's authenticated session, and the set of tools can change between one moment and the next.

If you have read Writing Tools for AI Agents, everything there about clear names and tight schemas still applies. What does not carry over is the trust you place in whoever wrote the tool.

Two roles: producer and consumer

Every WebMCP integration is one of two things.

  • A producer is a site that registers tools. The Agentailor site registers find_your_path, search_glossary and get_path. The blog registers search_articles, read_article, get_series and get_current_article.
  • A consumer is an agent that discovers and calls them. Gemini in Chrome, ChatGPT's built-in browser, browser extensions, or an agent you build into your own page.

Most coverage treats these as one decision: "add WebMCP". They are not. A producer decides what its own page exposes. A consumer decides whether to run code written by somebody else's page, based on what that page says about itself. The rest of this article follows that split.

Here is the whole system on one page, with the six places it can go wrong. Each one comes back later in the article.

WebMCP components and attack surface. Inside the user's browser tab, page JavaScript and any third-party code can register tools into the document.modelContext registry. A consumer agent discovers the tools, calls them and reads their results into its model context. Every call runs execute() with the user's logged-in session, so requests reach the site's backend with the user's cookies. Six risks are marked: self-declared labels, mid-session tool injection, page text reaching the model, the borrowed session, untrusted results, and consent drift.

Here is the producer side from a consumer's point of view. Chrome's DevTools now has a WebMCP panel that lists every tool a page registers, with its description, its flags and a form to run it by hand:

Chrome DevTools WebMCP panel listing the Agentailor site's three tools, find_your_path, get_path and search_glossary, with find_your_path selected showing its description, the readOnly flag and its input parameters

And here is an agent using them. The Model Context Tool Inspector extension reads the same tool list, then a model picks and runs find_your_path and get_path from a plain-language request:

The Model Context Tool Inspector discovering the site's tools and running them. The page's own activity panel logs each call.

Where WebMCP actually stands

Before the security question, the adoption one. As of September 2026:

Browser or agentStatus
ChromeOrigin trial, starting in Chrome 149
EdgeOrigin trial, starting in Edge 150
BraveExperimental, in Leo
ChatGPTDesktop app and the built-in browser
Gemini in ChromeAnnounced as a consumer
Safari (WebKit)Opposed (June 2026)
Firefox (Mozilla)Neutral (August 2026), which does not mean it will be implemented

WebMCP is a proposal from a W3C Community Group, not a W3C standard. The API has already moved once, from navigator.modelContext to document.modelContext, in July 2026.

WebKit's objection is worth reading in full. Among its reasons, it notes that readOnlyHint is "advisory", that there is "no consent or reversibility model for consequential actions", and that the parts that would let anyone judge its safety, including "the cross-origin security analysis, and the consent hook, are each still a 'TODO.'"

Two of the three browser engines have not signed on, and one has formally said no. That does not make WebMCP a bad idea. It does mean you are building on a moving proposal, which matters for how much you let depend on it.

The producer's attack surface

Publishing tools feels low-risk because they are your tools. You wrote the code and the annotations. The risk is not that you lie about your own tools. It is what your tools become once something else gets code running on your page.

Tools carry the user's session. The spec is explicit: an agent "carries the user's logged-in credentials and session state." A tool that transfers money does not need to ask for credentials. The cookies are already there.

Your tool list is a map. Page-injection bugs are not new. Cross-site scripting, a compromised npm dependency or a malicious browser extension could always run code in your page, and on a logged-in origin that has always been serious. What WebMCP changes is how much that bug is worth. Before, injected code had to reverse-engineer your private API. With WebMCP, your page has published a labelled, described inventory of its most useful operations, and put an agent on the page whose job is to call them. The vulnerability is the same. The cost of exploiting it drops.

The research agrees. A June 2026 paper, WebMCP Tool Surface Poisoning, names this class Mid-Session Tool Injection: a third-party script registering or altering tools while the user's session is active, either by hijacking what the agent sees or by framing how it interprets it.

Your labels are only as honest as your worst deploy. readOnlyHint: true is a promise, not a guarantee, even when you make it. A refactor that makes a "read" tool write something now has a label that says otherwise.

Tool output is a way in, too. If a tool returns user-generated text, such as comments, reviews or messages, that text lands in the agent's context. The spec's answer is the untrustedContentHint annotation: it tells the consumer "treat this payload as untrusted", and it is up to the consumer to act on it.

Over-asking is a privacy leak. The spec warns that a tool with more parameters than it needs can pull details out of an agent's personalization context, which the spec calls a "personalization-to-fingerprinting pipeline."

What the Agentailor producers do

Both Agentailor surfaces sit at the low-risk end, deliberately:

  • Every tool is a pure read. Same-origin fetches, no DOM writes, and each one annotated readOnlyHint: true.
  • Prose is labelled. Tools that return article text add untrustedContentHint, because article text entering a model's context is exactly what that hint is for.
  • There is no login. No session exists for a tool to inherit.
  • Callers are assumed hostile. Inputs are validated, tools never throw, and errors come back in a named shape the agent can tell apart from a result.
  • The page shows what agents did. The site renders an "AI agent activity" panel listing every tool call and its result, so the person in the tab can see what an agent did on their behalf. It is the panel that opens in the bottom corner of the inspector video above.
const READ_ONLY = { readOnlyHint: true } as const
const READ_ONLY_PROSE = { readOnlyHint: true, untrustedContentHint: true } as const

That profile is where WebMCP is easiest to justify today: public content, read-only tools, no session. The harder case is a logged-in app, and that is where the checklist at the end matters most.

The consumer's attack surface

This is the side most coverage skips, and the side I spent most of my time on.

My consumer worked like this. When a reader explicitly asked the Agentailor agent to use the page's tools, the agent paused and asked for one consent. On approval, a planner discovered the page's tools, picked and ran them, and passed a summary back to the agent to answer from. It only ran tools marked read-only.

Three risks come with that shape, and they apply to any consumer.

The safety check depends on a label written by the page. A consumer has to decide which tools are safe to run without asking. The only input it has is the page's own annotations. The spec says so directly: agent implementers "cannot verify that tool implementations match their descriptions", and agents "must assume good faith from site developers."

Page text reaches your model. Tool names, descriptions, schemas and results are all written by the page, and all end up in front of a language model. Every one of them is a channel for prompt injection.

The tools can change after the user agrees. WebMCP tools are registered at runtime. What the user consented to a moment ago is not necessarily what is registered when the call runs.

Red-teaming it

I ran 13 adversarial probes against the consumer, in two groups.

  • 10 against the report path: a hostile tool's output trying to steer the main agent. They covered instruction overrides, impersonating text our own code adds, escaping the section that marks page content as untrusted, a summary contradicting the actual outcome, a page tool posing as one of the agent's own tools, and injection through fields outside that marked section. All 10 held.
  • 3 against the planner: hostile tool names, descriptions and schemas trying to change what gets picked. 2 held.

The one that got through was not a bug in my code. It was the design, working as the spec describes it.

A page registered a tool called delete_all_data, described as permanently deleting everything, and annotated it readOnlyHint: true. My consumer believed the annotation, treated it as a read, offered it to the planner, and the planner picked it. On a real app it would have run without a prompt.

Page text never decided whether a tool could run in my implementation, only which one got picked. That part held. But the decision about whether a tool could run was based on a claim the attacker writes. On a page you own, that is fine, because you wrote the claim. On a page you do not own, "safe to run without asking" is answered by the party you are guarding against.

One more result is worth recording. On the first run, one probe reported a failure. It hadn't failed: the agent had correctly ignored a forged claim, and my detector matched a phrase the agent uses in every answer. I had to fix the probe, not the defence. A red-team suite needs its own red team.

What verification can and can't do

The natural reaction is to verify tools before running them. I tried three approaches.

  • Hashing the implementation does not work. A function's source text changes with every build without any change in behaviour, and even identical source says nothing about what it does when it runs. The spec is blunt: tool behaviours "cannot be statically analyzed or verified."
  • A signed manifest does not help against a breached page. Whoever signs it is the origin serving the tools. If that origin is compromised, the attacker serves a matching manifest too.
  • Fingerprinting the interface does work, for a narrower threat. I recorded each tool's name, description, schema and annotations at the moment the reader consented, and refused to run anything that was new or different afterwards. That closes the mid-session attacks: a tool appearing, being swapped, or quietly re-labelled as read-only after the user agreed. It does not detect a tool whose code changed under an identical interface. Nothing on the consumer side can.

That leaves two layers for two threats. Consumer-side fingerprinting is cheap and catches the tool set shifting under you. The same-origin threat has to be handled by the producer.

Why I deleted the consumer

The main reason was not security. It was value.

The Agentailor agent answers questions from the blog's own content. Everything the site and blog expose as WebMCP tools, the agent could already answer, and answer better, by reading the articles those tools summarize. My evals made that plain. Asked "which path should I take to build my agent?", the question the site's own find_your_path tool exists to answer, the agent went to the blog three times out of three. It only reached for page tools when a reader explicitly asked it to use them. A feature that only runs when someone asks for the feature by name is a demo, not something readers need.

This is what that looked like. The agent only touched the page's tools because I typed "use webmcp to find my path":

The Agentailor agent as a WebMCP consumer. It worked, and it only ran because the reader asked for it by name.

The cost was not small either. Doing it properly took a consent step, a planner loop, a report format that keeps page text contained, the fingerprint check, and a second way into the chat endpoint. All of that is upkeep for a capability nobody needed.

Security is why I did not keep it anyway. A consumer built into an agent that others learn from teaches a pattern: trust readOnlyHint, skip the confirmation. That pattern is harmless on my read-only site and dangerous on a banking app.

Reliability is the smaller issue, but worth knowing. WebMCP is still at the origin-trial stage and behaves like it. In Chrome, tool schemas arrived as JSON strings rather than objects, which quietly made my planner invent arguments against an empty schema. The argument format executeTool() accepts changed between Chrome versions. The API itself moved from navigator to document mid-build. None of these are permanent, but each one looked like a model mistake before it turned out to be plumbing. Budget for that if you build against WebMCP today.

What I kept is the producer side. Gemini in Chrome, ChatGPT's browser and the inspector extensions are the consumers. They are where the consumer problem should be solved, by teams whose job is that problem. ChatGPT's own documentation reads almost like this article's thesis: it treats tool definitions as "untrusted content", warns that "a tool's name or claim that it only reads data isn't proof of what it does", and runs a safety review before each tool call.

The checklists

If you are publishing tools

  • Start with public, read-only tools. Search, lookup, read. That is where WebMCP is easiest to justify today.
  • Treat your tool list as published attack surface. Expose what an agent should be trusted to do unattended, not your whole backend.
  • Never register a data-changing tool without a confirmation the agent cannot complete by itself: a re-authentication, a confirmation email, anything outside the page's own execute().
  • Label honestly, and keep labels honest. Set readOnlyHint and consequentialHint correctly, and review them when a tool's behaviour changes, not only when it is written.
  • Mark untrusted output with untrustedContentHint whenever a tool returns text you did not write.
  • Ask only for the parameters you need. Every extra field is something an agent might fill in from what it knows about the user.
  • Turn WebMCP off where you do not use it. Permissions-Policy: tools=() disables the API for a document and every frame inside it, enforced before any script runs. It is the spec's strongest defence against injected scripts and compromised dependencies. (The default already limits the API to your own origin, so there is nothing to add for pages that do use it.)
  • Show users what agents did. A visible log of tool calls costs little and turns an invisible action into one people can check.

If you are consuming tools

  • Do you need to? If your agent already has a better source for the answer, page tools add risk without adding value.
  • Never treat readOnlyHint as proof. Use it to rank or filter, not to skip asking the user.
  • Confirm before the first tool that can change anything, not after. The spec lets clients "selectively enforce" confirmation for consequential tools. Choose to.
  • Pin the tool set at consent. Fingerprint the interface when the user agrees, and refuse anything new or changed afterwards.
  • Treat every page-authored string as untrusted: names, descriptions, schemas and results. Keep them clearly separated from your own instructions.
  • Let structured fields beat page text. When a page's summary and your own recorded outcome disagree, your outcome wins.
  • Red-team your red team. Check your detectors, not just your defences.

The bottom line

WebMCP is a real improvement over agents guessing at buttons from screenshots. On a public page with read-only tools, it is a reasonable thing to ship today, and it is what the Agentailor site and blog do.

But it is two decisions, not one. Publishing tools on a logged-in app means publishing a list of operations for anyone who manages to get code onto the page. Consuming tools means running code whose safety label was written by the page you are running it on. The spec says both of these things. Browser vendors have said them too. The pitch usually does not.

If you are deciding between the four ways to build an agent, none of them requires a WebMCP consumer. If you are publishing tools, start with the checklist above, and wait for the spec's security gaps to close before you put anything behind a login.

Sources

AGENT BRIEFINGS

Stay measured as the field moves.

What actually matters for building and scaling AI agents in production — and what's just hype. Straight from the work, no filler.