WebMCP
The WebMCP security model, and the parts that are still open
Session-based access is a genuine improvement over handing an agent an API key. It is not a complete threat model, and the specification says so. What to rely on, what to gate, and what to tell a compliance officer.
Last reviewed 27 August 2026
The security story starts well. Tools run inside the page with the visitor's existing browser session, so nothing new is issued, nothing is stored in a model's context, and revocation is logging out. Compared with generating an API key and pasting it into an assistant's configuration, that is a real improvement in posture rather than a rhetorical one.
What the platform gives you
- Secure context only. The interface is exposed on
Windowunder[SecureContext]— no HTTPS, no tools. - Origin isolation. Tools are visible to same-origin documents unless you list other origins in
exposedTo. - Permissions policy. The
toolsfeature defaults to an allowlist of['self']; a cross-origin iframe needs an explicitallow="tools"and throwsNotAllowedErrorotherwise. - Same-origin credentials. Handlers fetch with
credentials: "same-origin", so the session travels and the token never does.
What it does not give you
The specification acknowledges a lethal trifecta: an agent that can read private data, process untrusted content and send information outward can be induced to chain those three into an exfiltration. Every one of those capabilities is individually reasonable. The combination is the problem, and it is not one a page can solve alone.
Annotations are advisory, not enforced. readOnlyHint and the destructive hints are declarations to the agent, not guarantees from the browser. Nothing prevents a tool marked read-only from writing. Your server-side authorisation is still the only thing that actually stops anything.
Prompt injection reaches further here
An agent reading a page treats what it finds as information. If your page renders content other people wrote — reviews, comments, listings, uploaded documents, support threads — then an instruction hidden in that content arrives in the same channel as your own tool descriptions.
This is why untrustedContentHint exists: it marks a tool whose return value contains material you did not author, so the agent can downgrade its trust in what comes back. Set it on every tool that surfaces user-generated content. It is the cheapest thing on this page and it is routinely skipped.
Read-only first is a strategy, not a compromise
The instinct in a regulated business is to refuse the whole category. The better move is to ship the read half, which carries most of the customer value and almost none of the risk.
const readOnly = { readOnlyHint: true };
register("find_my_policy", "List the policies on this account.", readOnly);
register("explain_coverage", "Explain whether a scenario is covered.", readOnly);
register("get_claim_status", "Return the status and next step for a claim.", readOnly);
register("list_required_documents", "List documents needed for a claim type.", readOnly);
register("locate_form", "Return the URL of the correct form for a topic.", readOnly);
// Anything that writes stops for a human first.
register("prefill_claim_draft",
"Prepare a claim draft for the user to review. Does not submit.",
{ readOnlyHint: false });Five tools, no write path, and a customer can ask their own assistant what their policy covers instead of navigating a portal. The draft tool prepares and stops; submission stays a deliberate human act in your UI.
Confirmation before consequential actions
The API is described as offering a way for a tool to pause execution and request explicit user interaction before continuing. Use it for anything that spends money, cancels something, sends a message on the user's behalf or cannot be undone.
The confirmation mechanism is documented in secondary sources under the name requestUserInteraction(), but it did not appear in the WebIDL we retrieved from the specification. Check the current draft before writing it into a design document or a compliance submission. The principle — a human confirms consequential actions in your own UI — holds regardless of what the call ends up being named, and you can implement it yourself with a modal and a promise if the platform hook is not where you need it.
A review checklist
- Every read tool carries
readOnlyHint: true. - Every tool returning user-generated content carries
untrustedContentHint: true. - Server-side authorisation is enforced independently of any annotation.
- No tool accepts a raw identifier the caller could enumerate — scope to the session instead.
- Anything consequential stops for explicit human confirmation in your UI.
- Tool descriptions contain no secrets, internal hostnames or ticket references.
- Rate limits apply per session, and return
Retry-Afterso an agent can back off rather than hammer. - Cross-origin iframes are given
allow="tools"deliberately, not by default.
Bring that list to the security review rather than waiting to be asked. In our experience the objection in a regulated environment is never this is unsafe — it is nobody has told us what this is. A read-only surface with an explicit threat model gets approved; a request to enable an experimental browser API does not.
Sources
Primary documents, checked on 27 August 2026
- webmachinelearning.github.io/webmcp — W3C Web Machine Learning Community Group draft — WebIDL, annotations, permissions policy
- github.com/webmachinelearning/webmcp — Explainer repository, imperative and declarative API
- developer.chrome.com/docs/ai/webmcp — Origin trial, flags, permissions policy directive
- bug0.com/blog/webmcp-chrome-146-guide — Secondary source for the lethal trifecta framing and the confirmation call
- OWASP — Top 10 for LLM Applications — Prompt injection as a first-class category
Keep reading
Check your own site against this
The Agent Readiness Score measures exactly what this article describes, and shows the evidence behind every finding.
Run the check →