MCP deleted the session, and the session was doing your access checks
Hey đź‘‹
I spent part of this week migrating a lab MCP server onto the 2026-07-28 transport, and the first question the migration asked me was not about performance. It was: which of my tools decide what a caller may do based on an identifier the caller handed me?
Under the old transport I did not have to answer that. A session existed, the transport held on to it, and the server could lean on it to know who it was talking to. The session was quietly doing security work that nobody had written down. The new transport is stateless, meaning there is no session and every request has to stand on its own, so that work comes back to your application code. Every trust decision that used to be implicit is now one you either wrote or forgot to write.
That is the through-line for everything below. Last week’s issue was the report from the floor at AGNTCon + MCPCon Europe, where I spoke: who said what, and what the program says about where this is going. This one is the other half, the protocol itself. The two sets of talks in Amsterdam, the transport ones and the identity ones, were describing the same shift from opposite ends: things that used to be implicit in the plumbing are now ordinary arguments and headers that pass through the model’s context, where a prompt injection can reach them.
1. Sessions are gone, and a new bug class takes their place: state handle hijacking
The 2026-07-28 specification is the largest change to MCP since launch. The handshake that used to open a connection, initialize and initialized, is retired, and so is the Mcp-Session-Id header. Each request now carries its own protocol version, client identity and capabilities, so, in the maintainers’ words, “any request can land on any instance behind a plain round-robin load balancer.” That is the point of the change: MCP servers can finally scale like ordinary web services. Responses to tools/list, prompts/list, resources/list and resources/read gained two new fields, ttlMs and cacheScope, which let a client cache the catalog of tools instead of fetching it on every connection.
Three features are deprecated with at least a twelve month support window: roots (the client telling a server which directories it may work in), sampling (a server asking the client’s model to generate something for it) and logging. The old HTTP+SSE transport goes the same way. Elicitation, where a server needs to ask the user something in the middle of a request, is reshaped rather than removed: the server returns resultType: "input_required" and the client sends the request again with inputResponses attached, a pattern the spec calls Multi Round-Trip Requests.
Shaun Smith, an MCP and transport working group maintainer who runs MCP as infrastructure for agents, interactive and inference workloads at Hugging Face, presented the migration data twice at the conference: in Stateless: The Future of MCP Transports with Kurtis Van Gent, the Google MCP core maintainer who leads that working group, and again in his Friday keynote with production analytics from Hugging Face’s own MCP traffic. The operational advice was concrete: move to current SDK versions, turn on list caching, and ask whether your server still needs discovery or subscription at all.
The security consequence is the part worth your attention. A server that needs to remember something between requests now has to hand out an explicit handle, a cart ID or a workflow ID, and gets it back as an ordinary tool argument, in the open, like any other parameter. The spec’s security best practices added a section for what happens next, called State Handle Hijacking, with a requirement in capitals: servers “MUST NOT treat possession of a state handle as authentication.” In other words, knowing the ID must not be the same thing as being allowed to use it. Handles should be unguessable, should expire, and should be stored against the principal the token proves you are talking to, the user or service making the call, for example keyed as <user_id>:<handle> where the user ID comes from the verified token and never from the client.
Equixly’s analysis works through the rest of the surface. A handle “now travels through model context, visible to the model and to anything that can influence the model’s input.” The requestState a server attaches to an input_required result goes out to the client and comes back again, so treat it as attacker-controlled input. Method and tool names now travel in the Mcp-Method and Mcp-Name headers as well as in the body, so the two can disagree and your server has to decide which one it believes. Because catalogs are cached, a poisoned tool description now has a lifetime and a set of other users it is shared with. And a broken stream can no longer be resumed, so the client retries the whole request, which means a tool with a side effect can run twice.
So what: the session was an access check nobody had to write. Removing it does not remove the need for one, it moves it into tool arguments that sit in the model’s context window. Anything a prompt injection can read, it can now use.
Do this: enumerate every tool that takes an identifier as an argument and confirm the server re-derives the principal from the verified token on every call, never from the argument. Treat
requestStateas something you have to sign and expire before it can influence an authorization decision, the way you would a cookie. Decide what your server does when the routing headers and the body disagree, because it now has to answer that question. And make side-effecting tools idempotent, because retries are normal in a world with no stream resumability. Equixly’s write-up has the fuller mitigation table.
2. Dynamic client registration is out, and the replacement makes your auth server fetch a URL the client chose
The same release formally deprecates Dynamic Client Registration, the mechanism that lets a client an authorization server has never seen register itself on the fly, in favor of Client ID Metadata Documents, now at draft-02 in the IETF OAuth working group (6 July 2026, Aaron Parecki and Emelia Smith). The idea is compact: a client hosts JSON metadata at an HTTPS URL and uses that URL as its client_id. No pre-registration. The authorization server fetches the document and shows a consent screen built from it.
This closes a real hole. The confused deputy attack documented in the MCP spec, where a trusted middleman is tricked into using its own authority on an attacker’s behalf, depends on a proxy server that combines one fixed upstream client ID with dynamic registration and a consent cookie. The user consented once, the cookie remembers it, and an attacker who then registers a client with their own redirect_uri collects an authorization code without any consent screen ever appearing.
It also opens two, and both are written down. First, server-side request forgery against your own authorization server. The MCP spec puts it plainly: an authorization server that accepts CIMD “takes a URL as input from an unknown client and fetches that URL,” and a malicious client can use that to reach “private administration endpoints the authorization server has access to.” Cloud metadata endpoints are my own first guess at the target, and the CIMD draft agrees enough to forbid the obvious mistake: authorization servers MUST NOT fetch from loopback addresses in production, an exception it allows only for development.
Second, client impersonation. The draft leaves the relationship between a client’s redirect_uris and its metadata URL to the authorization server, and Section 8.1 warns why that matters: without a restriction such as requiring the redirect to share an origin with the document, “the client attempts to impersonate a more well-known client or otherwise act in a way which is malicious or puts the end-user at risk.” The consent screen is built from a document the attacker wrote. Trust policy is explicitly left to you: allowlists for trusted domains, reputation checks for unknown ones, restrictions based on domain age or certificate validation, or accepting any HTTPS client ID if you are running an open server.
The enterprise half of this moved too. Enterprise-Managed Authorization went stable on 18 June 2026, built on ID-JAG, the Identity Assertion JWT Authorization Grant: the client obtains a grant from the IdP during SSO and exchanges it for an access token at the MCP server’s authorization server. Access decisions move into the IdP admin console. At the conference, Joey Orlando (Archestra.AI), Aaron Parecki (Okta) and Paul Carleton (Anthropic) presented it as a production pattern and named the missing piece themselves: SaaS authorization servers have to support the flow before any of it unlocks.
So what: registration friction was never the interesting part of MCP auth. Who the client is, what it may do, and whether you can revoke it were, and those questions are now answered by documents your authorization server fetches over the network and policies you have to write yourself.
Do this: if you operate an authorization server that accepts CIMD, treat the metadata fetch as an untrusted outbound request: block private and link-local ranges, pin DNS between check and use, and route it through an egress proxy. Decide your domain trust policy before you ship, not after the first unknown client. Display the CIMD hostname prominently, as the spec asks, and decide whether you require a client’s redirect URI to share an origin with its metadata document.
3. WebMCP shipped: the web page itself now tells the agent what tools it has
ChatGPT’s browser added WebMCP support, letting a site register tools with document.modelContext?.registerTool(...) in the top-level page and have an agent call them without any MCP server in the path. The website’s own JavaScript declares which actions it offers, and the agent in the browser calls them directly. Two conference sessions took it seriously as an architectural choice rather than a novelty. In Three Doors to One Tool: MCP vs WebMCP vs CLI, Frédéric Barthelet of Alpic and Dominic Farolino, who edits the WebMCP specification at Google, mapped the three places a tool can live, an MCP server, the page itself and a command line, by where the code runs, who holds the state and the credentials, and which trust boundaries get crossed. Liad Yosef’s MCP Apps and The Agentic Web argued that most sites will be reached by agents rather than humans, with interactive UI sent to the agent only for the last mile, where a human has to choose a seat or confirm intent.
For security work the relevant sentence is in OpenAI’s own documentation: “Website-provided tool definitions and results are untrusted content.” In its built-in browser, each tool invocation receives a safety review before it runs, and normal website-access and confirmation policies still apply to consequential actions such as sending messages, making purchases or deleting data. Chrome’s guidance gives site authors the declarative half: readOnlyHint for tools that change nothing, consequentialHint for actions like booking travel or moving money so the browser can require confirmation, untrustedContentHint for tools returning user-generated or external data, and exposedTo to restrict which origins can see a tool at all.
Those hints are advisory, which is the gap WebMCP-Phalanx measures (Lee, Chang and Yeh, 25 August 2026). Against an unprotected implementation, tool revocation and overwrite attacks succeeded 100% of the time, and prompt injection landed through both tool descriptions and tool return values. Two defences changed that. Binding each tool cryptographically to whoever registered it, so nobody else can replace or withdraw it, took revocation and overwrite to 0%. And a quarantine model, where the component that inspects tool content has no authority to invoke anything, blocked all 80 description-injection attempts and held return-value attacks to 2 of 80, while the agent still completed its tasks about as well as it did with no attack running at all.
Read the last paragraph of that abstract before you quote the numbers. Against an attacker who knows the defence and adapts to it, the description filter still falls over, because a malicious tool name can be invoked before anything inspects it, which is why the authors end on a gate at call time: nothing runs until every piece of metadata the agent can see has been checked. It is a preprint, submitted to AAAI 2027, so treat the figures as a first measurement rather than a settled one.
So what: a hint is a request, not a control. When the tool catalog is served by the page, the catalog is attacker-controlled input, and tool lifecycle (registration, overwrite, revocation) becomes an authorization question rather than a rendering detail.
Do this: if you publish WebMCP tools, set the annotations, restrict
exposedTo, and enforce authorization server side on every call, because the annotation is a signal to the agent and not a check on your backend. If you are evaluating an agentic browser, test tool overwrite and revocation explicitly, and treat tool descriptions and returns as content to be quarantined before they reach a privileged context.
Who presented these changes in Amsterdam
All three of these had a session in Amsterdam, presented by maintainers and spec editors rather than commentators: Kurtis Van Gent and Shaun Smith from the transports working group, Aaron Parecki and Paul Carleton on ID-JAG alongside Joey Orlando, and Dominic Farolino, who edits the WebMCP specification, with Frédéric Barthelet on the three ways to expose a tool. Last week’s issue is the report from the floor, including what the rest of the program said about agent identity, provenance and the consent gap. Read issue #28 →
From the lab
My lab notes from the migration, in case they save you a step.
The state handle question is not hypothetical, and it is easy to get wrong in a way that passes every test you already have. A tool that accepts workflow_id and looks up the workflow will work perfectly for every legitimate caller and will also work perfectly for anyone who obtains that ID. The old transport masked this because the session established who was asking. Nothing masks it now. The fix is mechanical: derive the principal from the verified token, key state by principal and handle together, and reject a handle presented by anyone else.
The second thing worth testing before you migrate is the cache. ttlMs and cacheScope mean a tool description is no longer fetched fresh on each connection, so a poisoned description now has a defined lifetime and a defined blast radius. Work out what that lifetime is for your deployment and who else shares the cached copy, because that pair of numbers is the answer to how long a rug pull would last: a server that serves a clean tool description while you review it and a malicious one later.
Lab code for the MCP attack classes I work with is public at github.com/aminrj-labs/mcp-attack-labs.
Tooling worth knowing
- Agent Security Scorecard: my free self-assessment against the OWASP Agentic Top 10, about 12 minutes, no login. It now asks the state handle question. Score your agents →
- AI Agent Pre-Deployment Security Checklist: 25 yes/no controls to clear before an agent with tool access reaches production. Get the checklist →
- AAIF Agentic AI Momentum Report (9 July 2026): tracks 116 projects across five layers of the stack. The number I keep coming back to is unpatched CVEs growing roughly 2.6x since December 2025, to about 9,200 by June 2026, with the average project carrying around 137. The report’s own conclusion is that the ecosystem is accumulating security debt faster than it resolves it. Read the report →
One thing to check this week
Open your MCP server code and grep for every tool argument that is an identifier: *_id, handle, token, ref, cart, session, workflow.
For each one, find the line that checks whether the caller is allowed to use it. If the lookup goes straight to storage using the value the caller supplied, you have a state handle hijacking bug, and after the 2026-07-28 migration you have it in a protocol the specification now says MUST NOT work that way.
The check takes twenty minutes. The fix is keying state by verified principal plus handle.
What I’m watching
→ The twelve month clock. Roots, sampling and logging are deprecated, and so is HTTP+SSE. Both have roughly a year of runway. The interesting question is whether clients retire them on schedule, or whether the long tail keeps a deprecated code path alive past its support window. A code path that outlives its deprecation is where downgrade attacks live: the attacker asks for the old protocol, and gets it.
→ Whether authorization servers ship CIMD trust policies or just CIMD. The draft is at -02 and the MCP spec hands policy to implementers. Fetching a metadata document is easy. Deciding which domains you accept, and defending the fetch itself against SSRF, is the part that gets skipped.
→ SaaS adoption of ID-JAG. Enterprise-Managed Authorization is stable, and the people who built it said the blocker is authorization server support on the provider side. Worth tracking which major SaaS vendors announce it, because enterprise agent deployments are gated on it.
→ Whether tool lifecycle gets a real control. The Phalanx results suggest cryptographic binding of tools to their registering principal works and costs little. Advisory annotations are what shipped. I expect that gap to produce an incident before it produces a specification change.
If you run the grep and find a tool that trusts a handle it was handed, reply and tell me what the argument was called. I read everything, and the naming is half the reason these get missed.
Cheers, Amine
If a colleague deploys agents in production, forward this to them.
Sources
- The 2026-07-28 Specification, Model Context Protocol Blog
- MCP Security Best Practices, 2026-07-28
- Stateless MCP: What the 2026-07-28 specification changes for security, Equixly
- OAuth Client ID Metadata Document, draft-ietf-oauth-client-id-metadata-document-02, IETF OAuth WG
- Enterprise-Managed Authorization: Zero-touch OAuth for MCP, Model Context Protocol Blog
- Site tools (WebMCP), ChatGPT documentation
- WebMCP tool security, Chrome for Developers
- WebMCP-Phalanx: Enforcing and Characterizing Trust Boundaries for Browser-Integrated LLM Agents, Lee, Chang and Yeh, arXiv, 25 August 2026
- AGNTCon + MCPCon Europe 2026 schedule, Sched
- Agentic AI Momentum Report, Agentic AI Foundation, 9 July 2026