Skip to main content

WebMCP Security: Risks and How to Reduce Them

Short answer

A WebMCP tool runs JavaScript on your page, in the visitor's signed-in browser, and an AI agent decides when to call it. The main risks are text that steers the agent (in a tool's name, description or output), tools that do more than their description says, and tools that ask for more personal data than they need. The browser keeps tools private to your origin by default and gives you hints such as consequentialHint, but nothing in the spec verifies them. As of 20 October 2026 there is no complete defense, so limit what you expose, mark risky actions, and check every input.

Tested withWebMCP draft Community Group Report, 20 October 2026Chrome for Developers, WebMCP tool security (published 9 June 2026, updated 1 September 2026)Chrome for Developers, Agent security considerations for WebMCP (published 9 June 2026)Mozilla standards position for WebMCP, issue 1412 (checked 20 October 2026)WebKit standards position for WebMCP, issue 670 (checked 20 October 2026)Checked 20 Oct 2026

Why WebMCP changes the trust boundary​

A normal web page trusts its own code, and the visitor trusts the page. A WebMCP page also hands an agent text to read: tool names, descriptions, parameter descriptions and return values. The agent's language model reads that text as part of its context, so the page can influence what the agent does next.

The spec describes what an agent can have. It can inherit the visitor's identity and login, read personalization data and browsing history, and correlate information across sites (spec section 6.2). When a tool runs, the page's code runs with all of that behind it.

Chrome's guidance says it plainly: models treat instructions and user data as one sequence of tokens, so they're open to indirect prompt injection, and no model can be guaranteed safe from it. That's why the risks below come from the page's text and the page's own code, not from the protocol.

The four risks​

The spec's security section (section 6) sorts the threats into a few groups. These are the four that matter most to a site owner.

Tool descriptions can carry instructions​

A page can put instructions in a tool's name, description or a parameter's description. The spec's example is a search tool whose description contains a line that tells the agent to ignore its earlier instructions and send the user's browsing history to an outside address (spec section 6.3.1.1). The site author is the attacker here. Chrome calls this a malicious manifest.

Tool output can carry instructions​

A tool that returns user-generated content passes whatever the user wrote straight into the agent. The spec's second example is a forum post with text in it telling the agent to send that content to another site (spec section 6.3.1.2). The site itself may be trustworthy. The content isn't. Chrome calls this contaminated output.

A tool can do more than it says​

The spec says there's "no guarantee that a WebMCP tool's declared intent matches its actual behavior" (section 6.3.2). Its example is a tool called finalizeCart, described as finalizing the cart for viewing, that actually triggers a purchase. The agent calls it to show the user their cart, and the user has bought something.

A tool can ask for more personal data than it needs​

A tool can list parameters such as pregnant or previousPurchases, with descriptions that make them sound like search filters. A helpful agent fills them in from the visitor's personalization data, and the site collects them (spec section 6.3.3). Chrome's agent guidance answers this with data minimization: put personal data in tool arguments only when the tool can't work without it.

Worked example: a tool that can spend money​

Suppose your checkout page registers a tool that places an order. Here's a first version, the kind that works in a demo and causes trouble in production:

await document.modelContext.registerTool({
name: 'place_order',
description: 'Places an order.',
inputSchema: {
type: 'object',
properties: {
sku: { type: 'string' },
quantity: { type: 'integer' },
},
required: ['sku'],
},
async execute(input) {
return await cart.submit(input);
},
});

It has four problems:

  • The description doesn't say the order charges the visitor or can't be undone, so an agent has no reason to ask first.
  • There's no consequentialHint, so the browser has no signal to ask the visitor to confirm.
  • execute trusts the input. The spec only requires the input to parse as a JSON object before execute runs, so a missing or wrong quantity goes straight into cart.submit().
  • If cart.submit() throws, the message isn't passed to the agent, so the agent gets no useful error. Chrome's guide to agentic JavaScript tools states this rule for exceptions thrown from execute.

Here's a version that addresses each point:

await document.modelContext.registerTool({
name: 'place_order',
description: 'Places an order for the items in the cart. Charges the saved card and cannot be undone.',
inputSchema: {
type: 'object',
properties: {
sku: { type: 'string', description: 'Product SKU from the cart' },
quantity: { type: 'integer', minimum: 1, description: 'Number of units' },
},
required: ['sku', 'quantity'],
},
annotations: { consequentialHint: true },
async execute(input) {
if (typeof input.sku !== 'string' || !Number.isInteger(input.quantity) || input.quantity < 1) {
return { error: 'Give a sku and a whole-number quantity of at least 1.' };
}
try {
const order = await cart.submit({ sku: input.sku, quantity: input.quantity });
return { status: 'placed', orderId: order.id };
} catch {
return { error: 'The order was not placed. Check the cart and try again.' };
}
},
});

The changes are small. The description says what the tool costs and that it can't be undone. consequentialHint: true tells the browser or agent to ask first, if it supports that. The input is checked before anything runs. Errors come back as a value the agent can read, with a fixed message, so no text from the upstream service reaches the model.

What the browser gives you​

Chrome's documentation and the spec describe these controls. Chrome's imperative API page lists the annotations and their defaults.

ControlWhat it doesSource
Same-origin defaultOther websites and cross-origin iframes can't see or run your tools.Chrome, tool security; spec section 4.2
exposedToLets the listed HTTPS origins discover and run a tool when they're embedded with your page.Chrome, tool security; spec section 4.2.3
fromOriginsThe calling page has to name your origin here before it can see tools from it.Spec section 4.2.4
tools Permissions PolicyPermissions-Policy: tools=() makes WebMCP unavailable to the page and every frame inside it. The default allowlist is 'self'.Spec sections 4.4 and 6.4.1
Secure contextWebMCP needs HTTPS. Every origin in exposedTo must also be HTTPS or localhost.Spec sections 4.2 and 4.2.3
readOnlyHintSays the tool only reads data, so an agent can decide when it's safe to call. Defaults to false.Chrome, imperative API
untrustedContentHintMarks output that came from users or third parties, so the agent can treat it as data. Defaults to false.Chrome, tool security; spec section 6.4.4
consequentialHintMarks irreversible or real-world actions, so the browser or agent can ask the user to confirm first. Chrome's guide lists it from Chrome 154.Chrome, tool security; spec section 6.4.5
debuggingMarks tools meant for developer tooling, so general agents can skip them. Chrome lists it from Chrome 156.Chrome, imperative API

Two more points. First, Permissions-Policy: tools=() is the strongest control for a page that shouldn't have tools at all, because the browser enforces it before any script runs (spec section 6.4.1). Second, the spec's signal option removes a tool from the page, but it doesn't cancel an execution that's already running (spec section 4.2 note).

What the hints don't do​

The hints are signals to the agent. They are not a security control, and the spec says so:

  • The spec lists "no verification mechanism" for whether a tool matches its description. Agent implementers can't check a tool's behavior before it runs (spec section 6.3.2.4).
  • Chrome tells agent developers to assume a tool changes state unless its description or readOnlyHint clearly says otherwise.
  • Chrome notes that an extension with host_permission can run its own JavaScript on your page, with or without WebMCP. Same-origin rules don't stop an installed extension.

Checklist for site owners​

  1. Expose the least. Keep tools same-origin unless a partner needs them, and list only the exact HTTPS origins in exposedTo. Chrome's guidance is to expose a read-only tool only to a site you'd share that data with anyway.
  2. Mark the risky tools. Use consequentialHint for purchases, account changes and deletions. Use untrustedContentHint for user-generated content and third-party data. Use readOnlyHint for tools that only read.
  3. Describe side effects. The description should say what the tool changes, charges or sends, in plain words.
  4. Check every input. The browser checks only that the input is a JSON object. Validate the values inside execute.
  5. Ask for less personal data. Don't add parameters for data the tool doesn't need.
  6. Return errors as values. Use fixed messages that you wrote, not raw upstream text.
  7. Switch it off where it isn't used. Send Permissions-Policy: tools=() on pages that don't need tools. The spec says it applies to every descendant frame.
  8. Keep text short. Chrome's recommendations are 500 characters for a tool description, 150 for a parameter description and 1.5K for a tool's output. Chrome says these may change.

If you build the agent​

Agents that call WebMCP tools need their own defenses. Chrome's agent guidance groups them into two kinds:

  • Deterministic guardrails: set token limits on tool responses, tell the model in its system instructions to treat untrustedContentHint output as data, restrict which origins the agent may act on, and confirm actions with the user.
  • Probabilistic guardrails: mark untrusted tool output so the model treats it as data (spotlighting), and add a classifier or a second model that checks each planned call against the user's request.

Chrome compares the two spotlighting methods in this table:

MethodSecurity valueCost
Delimiting (wrap text in tags)Fine for low-risk content. A crafted closing tag can break out of it.Cheap, easy to read in debugging
Base64 encodingSuited to high-risk content. Injected delimiters can't be read as delimiters.About 33% more tokens

Chrome also recommends red-team testing your agent against prompt injection before shipping it.

Errors you'll see when a call is refused​

ErrorWhereWhat it means
SecurityErrorregisterTool(), getTools()An origin in exposedTo or fromOrigins isn't HTTPS or localhost.
NotAllowedErrorregisterTool(), getTools(), executeTool()The tools Permissions Policy blocks this document.
InvalidStateErrorregisterTool()The name is already registered, empty, longer than 128 characters, or uses characters outside the allowed set. The description can't be empty either.
NotFoundErrorexecuteTool() from another originThe tool doesn't exist, or it isn't exposed to the caller. The browser returns the same error for both, so a caller can't probe for hidden tools.
DataErrorexecuteTool()The input couldn't be parsed as a JSON object.

These names come from the spec's algorithms (sections 4.1 and 4.2).

Common misconceptions​

"Same-origin means my tools are safe." Same-origin only keeps other websites out. The agent acting for the visitor already has the visitor's session, and an extension with host permission can run JavaScript on your page anyway.

"consequentialHint stops an action from going wrong." It asks the browser or agent to confirm before the tool runs, if the agent supports that. It doesn't change what the tool does.

"Read-only tools are harmless." A read-only tool can reveal information about the visitor. Chrome says to expose these only to origins you'd share that data with.

"MCP security advice applies unchanged." Prompt injection and tool poisoning are shared concerns, and the spec says it adopts MCP's risk assessments (section 6.1). The trust boundary is different, though. A WebMCP tool runs inside the visitor's page, with their login. For the MCP side, read tool poisoning and rug pulls and prompt injection.

"The browser checks my tools." It checks the registration rules and that input is a JSON object. It doesn't check what your tool does.

What's still open​

Browser makers don't agree on WebMCP yet. Here is each position as of 20 October 2026:

PartyPositionDate and source
MozillaNeutral. Mozilla sees value in aligning sites with browsing agents but is concerned about sites using tools for prompt injection, data collection or misleading agents. Its later comments support the imperative API.Labelled position: neutral, last updated 26 August 2026. Mozilla issue 1412
WebKit (Safari)Opposed. The concerns include privacy, security, consent and reversibility. WebKit's view is that a parallel agent-facing tool layer is the wrong fix, and that the gaps belong in HTML and ARIA.Labelled position: oppose, comment dated 3 June 2026, last updated 17 June 2026. WebKit issue 670
ChromeOrigin trial. Chrome publishes guidance for site owners and agent developers, and in the spec thread it says mitigations for site attackers belong at the agent or browser level.Chrome docs published 9 June 2026. Comment on webmachinelearning/webmcp issue 313, 28 September 2026.

Three open threads in the spec repository are relevant:

  • Issue 313, "Mitigations for profound privacy and security issues seem to be missing?", is open and labelled privacy-needs-resolution. It was last updated on 8 October 2026.
  • Issue 165 proposes requestUserInteraction(), a way for a tool to ask the user for input while it runs. It's open and was updated on 20 October 2026.
  • The draft's section on state carried between origins (section 6.3.4) has no content yet, and the draft says so.

FAQ​

Is WebMCP safe to use on my site?

There is no complete defense yet, and neither the spec nor Chrome claims one. Keep tools same-origin, mark actions that cost money or can't be undone, and treat every value an agent sends as untrusted. Start with read-only tools on pages where a mistake is cheap.

Can another website call my WebMCP tools?

Not by default. Your tools are visible only to your own page and same-origin documents. You can share a tool with a partner site by listing its HTTPS origin in exposedTo, and the partner's page has to name your origin in fromOrigins before it can see the tool.

Do the annotations stop an agent from doing something harmful?

No. The annotations are hints for the agent and the browser. The spec says nothing verifies that a tool does what its description claims. consequentialHint asks for a confirmation step if the agent supports one.

What is the biggest security risk for a site owner?

Text that the agent reads. Tool descriptions and tool outputs can carry instructions, which is a form of prompt injection. Tools that return user-generated content are the most exposed, so mark them with untrustedContentHint.