Since the 2026-07-28 revision shipped, I have seen it summarized as “MCP goes stateless.” The headline describes the protocol core, but leaves an important application responsibility unstated.
The protocol session is gone. Application state is not. Someone still has to protect that state, authorize access to it and clean it up. That someone is now the server implementation.
One status check before going further: the final specification was published on July 28, and all four Tier 1 SDKs (TypeScript, Python, Go and C#) speak it. This is no longer a draft. The changelog is the normative list of what changed.
Update, September 3, 2026: this piece was first published against the May 21 release candidate. The final specification shipped on July 28 and the text has been revised to describe the shipped revision.
What was removed
Under the 2025-11-25 revision, a Streamable HTTP connection began with an initialize handshake. The server could return an Mcp-Session-Id, and later requests carried that identifier. The protocol managed a long lived session.
The handshake and the session. The 2026-07-28 revision removes the initialize/initialized handshake and the protocol level session. Each request carries its protocol version, client information and client capabilities in _meta. A new server/discover method lets a client fetch a server's supported versions, capabilities and identity up front. Servers must implement it. Clients may call it.
Application state. Now consider a shopping basket or a remote browser. They still need continuity. The server returns a handle and the model sends it back later as a normal tool argument. Any server instance can process the next request.
That is useful. It also means the handle is untrusted input arriving from the client. MCP is no longer carrying the continuity for you.
Server to client interaction. A server may ask for input only while processing a client request. Multi Round Trip Requests return an InputRequiredResult with opaque requestState; the client collects the answer and retries the original request. Unsolicited server initiated requests such as sampling and elicitation are no longer part of the core flow.
Routing headers. For Streamable HTTP, Mcp-Method and Mcp-Name headers are now required, so control layers can see the operation without parsing the JSON-RPC body. When the headers and the body disagree, the server must reject the request with HeaderMismatchError.
Resumability and tasks. Two removals get less attention. SSE stream resumability is gone: there is no Last-Event-ID header and no message redelivery, so a broken response stream loses the in flight request and the client must reissue it with a new request ID. Tasks left the core protocol and became an extension, io.modelcontextprotocol/tasks.
The security win
There is no Mcp-Session-Id left to steal and replay. Security state is no longer quietly attached to one server instance. A load balancer does not need sticky routing just to satisfy MCP.
Server initiated interaction is narrower. A server cannot prompt a client whenever it wants; it can ask for input only while handling an active request.
Authorization is more explicit. Authorization servers should return the iss parameter per RFC 9207 and clients must validate it before redeeming a code. Client credentials are bound to the issuer that minted them and must not be reused with another authorization server. Clients set application_type at registration to avoid OpenID Connect redirect URI conflicts. And Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents, so client identity moves from a registration call to a published metadata document.
These changes remove mechanics that were easy to misuse. They do not make an MCP server safe by default, and the specification does not claim otherwise.
Where I would look for bugs
Akamai's analysis, written against the release candidate, describes the trade well. Session hijacking at the protocol layer shrinks. Application state, returned handles and long running work deserve more attention.
State returned by the client. Handles and requestState values cross a trust boundary every time they come back. A predictable handle, or one accepted without checking ownership, can let one workflow resume another. A handle that is not bound to a tenant can become a cross tenant bug. Use an integrity-protected token or an unguessable reference to server-held state. Check the authenticated caller’s access, bind the handle to the tenant and operation, and enforce expiry.
Per request metadata. A field does not become trustworthy because it lives in _meta. Reserved MCP fields have protocol meaning. Application fields are still client input. I would not authorize anything from them without authenticated server side context.
Routing headers. A control layer may read Mcp-Method and Mcp-Name while the application reads the JSON-RPC body. If they disagree, reject the request. Do not let two layers make two security decisions about the same operation.
Lost streams and retries. Without resumability, a client that loses a response stream sends the request again with a new ID. If the server already executed the side effect, the retry executes it twice. Tool calls that change state need an idempotency key, carried in the arguments or derived on the server. Clients need to know which operations are safe to repeat.
Tasks. Work can outlive the request that created it, and the tasks extension lets a server return task handles without a per request opt in. Without quotas, deadlines and cancellation, a cheap request can create an expensive background process. This is where reliability and security become the same problem.
My migration checklist
July 28 was not a forced migration. 2025-11-25 still negotiates, and deprecated features have a twelve month minimum removal window. I would still check these assumptions now.
- Treat every returned state handle and application defined metadata field as untrusted input. Protect integrity, bind it to a tenant and operation and enforce expiry.
- Validate
Mcp-MethodandMcp-Nameagainst the JSON-RPC body before authorization or dispatch. - Validate the authorization issuer and resource audience. Key persisted credentials by issuer and never reuse them across authorization servers.
- Plan the move from Dynamic Client Registration to Client ID Metadata Documents while registration still works for compatibility.
- Keep secrets and personal data out of routable metadata and infrastructure logs.
- Make state-changing tool calls safe to retry. A retry needs a stable operation key, not just a new transport request ID.
- Put concurrency limits, budgets, deadlines and cancellation on every long running task.
- Test version negotiation against both
2026-07-28and2025-11-25.
One assumption deserves its own line. Sticky sessions and a shared session store are not an authorization boundary. Keep them for an older revision if you need them operationally. Do not make them carry the security model.
The boundary after the session
Four months ago I wrote about the missing identity layer for agents. The new revision makes request context more explicit and removes an entire class of session mechanics.
As MCP gets easier to operate, the hard questions move closer to execution. Who issued the request? Which tenant owns the state? Does this authorization cover this resource? How much work will the server allow before it says no?
The protocol defines the exchange. The implementation has to enforce the boundary.
Primary sources
- MCP blog, The 2026-07-28 Specification, published July 28, 2026
- MCP specification 2026-07-28, Key Changes
- Akamai security analysis, June 25, 2026
- WorkOS analysis of the authorization changes, June 18, 2026
Status reviewed on July 21, 2026 against the release candidate and on September 3, 2026 against the published specification and changelog.