Read the changelog for the 2026-07-28 MCP revision and the security section reads like a victory lap. Protocol-level session hijacking: gone, because there is no protocol-level session anymore. Unsolicited server-to-host prompts: gone, because Sampling is deprecated and the host owns the model again. Weak authorization: hardened by six OAuth SEPs that make clients validate the iss parameter per RFC 9207, declare an OpenID Connect application_type at registration, and bind credentials to the issuing server. These are real fixes to real, exploited classes of bug. The spec genuinely got safer.
Then you notice what the security researchers noticed. Akamai's team, reading the same release candidate, published a warning with a title that is doing a lot of work: what security teams must prepare for. Not celebrate. Prepare. Their finding, echoed by SecurityWeek and SiliconANGLE, is that the release kills old risks and opens fresh attack surfaces — three of them, one for each headline feature.
The temptation is to read that as a wash: some risks down, some up, net neutral, ship it. That misreads what actually happened. This release did not reduce the attack surface. It moved it — off the protocol, onto your code.
The state moved, and the trust anchor moved with it#
Here is the thing the "stateless is simpler" framing skips. In old MCP, the server held the session. That sounds like a scaling headache — sticky sessions, shared stores — and it was. But it also meant the server was the trust anchor: the authoritative copy of who you are and what you're allowed to do lived on the server, where an attacker couldn't touch it.
Stateless MCP inverts that. With no server-side session, the server hands the client tracking identifiers and state objects, and the client hands them back to resume a workflow. The client is now holding the keys to a task's state. And because those values arrive from the client, the server cannot blindly trust them.
Statelessness didn't just delete the session — it moved the state to the one party the server is supposed to distrust.
Akamai spells out the failure mode. A server that mints predictable task IDs, or that accepts a returned state object without verifying its integrity, invites an attacker to guess or tamper with those values — and the payoff is not a nuisance. It is hijacking another user's active workflow, reaching data belonging to a different agent, or triggering unauthorized cross-tenant actions. This is a confused-deputy problem wearing a new hat: the server, trying to be helpful about resuming work, becomes the deputy that acts on forged instructions.
It gets sharper. The fields in _meta — where protocol version, client info, and now trace context all ride on every request — are not cryptographically signed. If a server reads that metadata to make a routing or authorization decision, one crafted request is an instant privilege escalation. The spec's own guidance tells developers to verify the integrity of these objects; it does not hand them a standard for how. That gap is the attack surface.
Two more: the panel and the background#
The second surface is MCP Apps, the new capability that lets a server ship an interactive HTML UI rendered in a sandboxed iframe. The design here is genuinely careful: templates are pre-declared so hosts can review them, the content runs sandboxed, and every UI-initiated action routes back through the same audited JSON-RPC consent path as a direct tool call. That is more disciplined than the webview free-for-all it replaces.
But sandboxing constrains what a panel can reach, not what it can say. A compromised or malicious app can still render deceptive content, mock up a convincing "re-authenticate" prompt, and phish for credentials or exfiltrate whatever data is already visible in the panel — all inside a surface the user has been trained to trust because it lives in their AI client. The trust users extend to the assistant now extends to anything the assistant frames, and framing is exactly what an iframe does.
The third is the Tasks extension. Graduating long-running work out of a live SSE stream into a poll-driven async model (tasks/get, tasks/update, tasks/cancel) is the right architecture. It also hands an attacker a lever: background processing that can be kicked off and left running is a resource-exhaustion primitive, a way to abuse async workflows into service disruption. Anything that does expensive work off the request path needs a budget and a rate limit, or it becomes a denial-of-service button.
The bill is a to-do list#
Notice the pattern across all three. None of these are protocol bugs. Every one is a responsibility the protocol used to shoulder and has now set down for the server developer to pick up — tenant isolation, ID unguessability, state integrity, UI trust, task budgeting. The spec even hardened OAuth, but it can only require the checks; you still have to implement them.
So the pre-July-28 to-do list writes itself, and it is not optional: bind every request to a verified user identity; derive tenant context server-side instead of reading it out of tool arguments; make task IDs unguessable and verify the integrity of any state you accept back; refuse to trust unsigned _meta for anything security-relevant; and put a rate limit on background Tasks. Do that and the stateless spec is, on balance, a security upgrade — which is the point. It is an upgrade conditional on work that the protocol can no longer do for you. The old spec failed closed. This one fails to whoever read the security section past the victory lap.



