
On 25 August OpenAI shipped site tools in the ChatGPT desktop app, its implementation of WebMCP. A page you have open can now declare a set of callable tools, and the agent uses those instead of guessing its way around the interface. Three days later OpenAI opened a ten day WebMCP challenge with Google Chrome, Cloudflare, Shopify, Vercel, Render and Netlify, and on 31 August website tools rolled out more widely in the desktop browser.
The part worth your attention is not that agents can now click less. It is that the tool list stopped being a thing you install. It is a JavaScript object the page constructs at load, mutates while you work, and throws away when the tab closes.
And the tools run inside the session you are already signed into. There is no token to scope, no consent screen, no grant sitting in an admin console afterwards. The credential is your cookie.
What shipped
Site tools work in the browser built into the ChatGPT desktop app, for ChatGPT Work and Codex, on GPT-5.6 Sol and Terra. Luna has WebMCP switched off. Enterprise and Edu workspaces do not have it yet. Users can turn the whole thing off under Settings, Browser, Permissions.
A site registers a tool with a few lines of client-side JavaScript:
if (typeof document.modelContext?.registerTool === "function") {
await document.modelContext.registerTool({
name: "update_cart",
description: "Add or remove items in the current cart",
inputSchema: { /* JSON schema */ },
annotations: { readOnlyHint: false },
execute: async (args) => { /* your existing function */ }
});
}
That is the whole integration. No server, no deployment, no manifest file, no registry entry. The spec also gives you unregisterTool() to remove one by name. OpenAI's build ignores tools declared inside iframes and only reads the top-level page, and it supports a subset of the WebMCP surface. The standard itself is a W3C Community Group draft, not a ratified standard, and Chrome runs it behind an origin trial.
Adoption did not wait for the standard. Shopify says WebMCP tools are live on every Liquid storefront, which is millions of them. Expedia, Instacart and Target are experimenting. Cloudflare shipped a developer preview that switches WebMCP on at the edge from the dashboard, with no change to the site's own code.
Read that last sentence again with a security hat on.
The tool list became a runtime object
Every mental model we built for agent tooling assumes the tool list holds still.
You install an MCP server. You review its manifest. The names and schemas are the same on Tuesday as they were on Monday, and if they change, a version number moves and somebody approves it. That stability is what makes a tool allowlist meaningful. It is why "which tools can this agent call" has been answerable at all.
WebMCP removes the stability, not the list. Tools are registered by whatever JavaScript is running on the page, at whatever moment it chooses. A registration can fire after the agent has already read the tool list and started work. A tool can be swapped for a different implementation under the same name. The description string the model reads when deciding what to call is a string literal in page code, and page code is not one author.
Four researchers wrote this up in June, before OpenAI shipped. Lin-Fa Lee, Yi-Yu Chang, Chia-Mu Yu and Kuo-Hui Yeh named it Mid-Session Tool Injection: a third-party script injecting tools into a live session. They split it in two. Tool hijacking changes the set of tools the agent can see, using registration race conditions or the AbortSignal API. Tool framing leaves the set alone and edits how the agent reads it, through the name, the description, the readOnlyHint flag, the input schema. A write tool that presents itself as read-only is a tool framing attack, and it is four characters of diff.
This is not prompt injection wearing a new hat. Prompt injection puts hostile text where the model reads content. Tool surface poisoning puts it where the model reads capability, which is the layer we have been telling everyone to trust because it is structured.
In August I wrote about Salesforce collapsing its API behind four MCP tools, where the tool list stopped being a review artifact but the real boundary was still a permission set an administrator owned. This is a step further out. There is no administrator and no server. The boundary is which scripts can execute on the page.
Your tag manager is now a tool author
If you run a website, the question that matters is short. Who can run JavaScript on your pages?
The honest answer for most production sites is a longer list than anyone wants to say out loud: the tag manager, the analytics snippet, the support chat widget, the A/B testing tool, the consent banner, the session replay vendor, the ad pixel, whatever a marketing team added last quarter through a dashboard. Every one of those can now call registerTool. So can Cloudflare's edge bridge, which is a feature, and which also means a tool surface can exist on your site that is not in your repository.
Chrome's guidance gives you real levers. Tools are origin-isolated by default, so cross-origin iframes cannot see them unless you opt in through exposedTo, which you should treat as the same decision as handing that origin the data directly. There is an untrustedContentHint for labelling user-generated content so agents apply more scrutiny. Chrome is also blunt about the limit: it says plainly that safety cannot be guaranteed inside a language model, because models are probabilistic. The browser will isolate your tools. It will not decide whether calling one was a good idea.
OpenAI's side has its own controls. Each invocation goes through a safety review, normal website-access permissions still apply, and consequential actions like purchases, deletions and permission changes need a confirmation. That covers the loud failures. It does not cover a read tool that quietly returns more of the signed-in user's data than the agent needed, which is exactly the shape tool framing produces.
The mitigations the researchers propose are the ones that will end up in everyone's checklist eventually: bind a tool's identity to its origin, enforce lifecycle consistency so a name cannot be quietly re-pointed, keep third-party tools inside a data boundary, and log registration events rather than only invocations. That last one is the cheapest and the most useful, and it is the one your logging pipeline almost certainly does not have, because until last week registration was not a runtime event worth recording.
What to do this week
If you ship a website, inventory the scripts that can reach your top-level document, and decide whether WebMCP is on before someone turns it on for you from a dashboard. Register tools from your own code, name them narrowly, be accurate in readOnlyHint, and emit a log line every time a tool is registered or removed. Validate arguments server-side exactly as if the call came from a hostile client, because it can.
If your team uses ChatGPT Work or Codex, site tools are already reachable on Sol and Terra. That should be a decision someone made, not a default that arrived on 31 August. The toggle is in Settings, Browser, Permissions.
The pattern underneath is one this stack keeps repeating. Capability keeps moving closer to the user, and each move drops another artifact we were using to reason about it. The API key gave way to the OAuth grant. The grant gave way to the permission set. The permission set has now given way to a page that decides, per load, what an agent is allowed to do with the session you are already inside.
The tool list was never the security boundary. It was just the last place the boundary was written down.