The Model Context Protocol just went through what I think is its most consequential architectural change since it appeared. With the MCP 2026-07-28 specification, the protocol drops protocol-level sessions and moves to a stateless request model.
The first reaction I saw everywhere was some version of the same question:
Did MCP just reinvent a normal API?
Not quite. What actually happened is more interesting. MCP keeps the semantic contract that makes it useful for AI agents, and deliberately moves the transport closer to infrastructure patterns that enterprises already know how to run. Ordinary HTTP load balancing. API gateways. WAFs. Rate limiting. OAuth resource boundaries. Per-tool telemetry. Horizontal scaling.
MCP is becoming less exotic at the transport layer while becoming considerably more useful as enterprise infrastructure. From a security point of view that is a very good trade.
This is part one of two. Here I cover the transport change itself: what the old session model cost, what the specification removed, and what that means for application state, retries, and idempotency. Part 2 covers the security side: the new transport headers, per-tool authorization, OAuth resource binding, and where Azure API Management and Microsoft Foundry fit.
The session model that caused the trouble
Earlier versions of Streamable HTTP MCP started with an initialization sequence. The client called
initialize, the server replied, and the client sent
notifications/initialized. After that negotiation, subsequent requests were associated
with the resulting protocol session through a header:
POST /mcp HTTP/1.1
Host: tools.example.com
Mcp-Session-Id: 6f2a91c4-7d33-4b0e-9a11-2f8c5d61e740
Content-Type: application/json
For a protocol designed around a long-running conversation between an AI client and a server, that reads perfectly reasonable. Operationally, it pushed state into the infrastructure.
Picture three MCP server instances behind a load balancer. The initialization request lands on instance #1. If the negotiated state only exists in that process, the following requests cannot be distributed freely across the cluster any more.
To make that work you need one of the usual mechanisms, and every one of them adds operational weight:
- sticky sessions at the load balancer
- distributed session storage
- session replication between instances
- session-aware routing logic
The InfoQ analysis lands on exactly this point: deployments had to preserve or migrate session state, clients effectively became pinned to instances, and plain round-robin load balancing got much harder. (InfoQ)
This hurts most on Kubernetes, on serverless, and on anything elastic. The cloud-native assumption is that an instance dies, another one starts, and traffic continues. A stateful protocol instance turns that into a much longer set of questions: what state did the dead instance hold, can it be reconstructed, where does the client session live now, and can another instance continue it? That is precisely the class of problem distributed systems engineering has spent years pushing out of application servers.
What the 2026-07-28 specification removes
The new specification takes the initialization handshake out of the core request flow. The changelog
removes initialize and notifications/initialized as required
protocol-session establishment, and drops the dependency on Mcp-Session-Id for the
2026-07-28 model. (Model Context Protocol)
Instead, each request carries what the server needs to process it independently.
Protocol information travels in _meta:
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "search",
"arguments": { "query": "critical vulnerabilities" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": { "name": "finance-agent", "version": "3.1.0" },
"io.modelcontextprotocol/clientCapabilities": { "elicitation": {} }
}
}
}
The server can identify itself and advertise capabilities the same way. For clients that want
capability discovery before running anything, there is a server/discover operation.
(Model Context Protocol)
| Concern | Session-based Streamable HTTP | MCP 2026-07-28 |
|---|---|---|
| Handshake | initialize plus notifications/initialized |
None required; optional server/discover |
| Protocol version | Negotiated once per session | Declared per request in _meta and headers |
| Session correlation | Mcp-Session-Id header |
Not used by the transport |
| Load balancing | Sticky sessions or shared session store | Any instance, plain round robin |
| Instance loss | Session reconstruction or client restart | Retry against another instance |
| Where state lives | Implicitly in server process memory | Explicitly in the application domain |
| Operational cost | Session affinity infrastructure to build and run | Standard HTTP infrastructure you already pay for |
Any request, any instance
Architecturally this is a real difference. Before, a client established a session with one server and every following request had to reach that same process. Now every request stands on its own, so an MCP deployment starts to look like a conventional horizontally scaled HTTP service sitting behind a gateway.
No sticky sessions. No MCP session affinity. No distributed protocol-session cache. No session migration during a rolling deployment. Any request can land on any compatible instance. (Model Context Protocol blog)
For anyone running agent infrastructure at scale, that is a substantial improvement, and it is mostly free. You stop building MCP-specific plumbing and start using the load balancer, the autoscaler, and the gateway you already have.
Stateless transport does not mean stateless applications
This distinction gets lost in a lot of the commentary, so it is worth being precise. The new MCP architecture does not mean every MCP-backed application must itself be stateless. It means transport state should not implicitly carry application state.
Say an agent starts a long-running job:
{
"method": "tools/call",
"params": {
"name": "start_security_assessment",
"arguments": { "tenantId": "contoso.onmicrosoft.com" }
}
}
The server replies with an explicit handle:
{
"workflowId": "assessment-84721",
"status": "running"
}
The next invocation carries that handle back. The state now lives in the application domain, where it can be stored in Cosmos DB, PostgreSQL, Redis, Durable Functions, a workflow engine, or object storage, instead of hiding inside a session ID that maps to one server process.
I much prefer this split. When state is implicit, nobody can tell you where it lives, how long it survives, or what happens on a restart. When it is explicit, it has a schema, a lifetime, a backup, and an owner.
Multi round-trip requests replace hidden connection state
Going stateless raises an obvious question: what happens when the server needs something from the client in the middle of an operation? Approval for a destructive action, a missing argument, extra context. Older architectures leaned on the open session or stream to hold that interaction together.
The new model handles it explicitly with multi round-trip requests. The server
returns something equivalent to input_required, the client collects whatever is needed,
and then issues another request. InfoQ calls this out specifically for elicitation: instead of one
invocation parked on an open stream, the interaction becomes multiple requests. (InfoQ)
From a distributed-systems angle I prefer this a lot. The workflow transition becomes visible. It can be logged, retried, correlated, and persisted. And human approval no longer requires an infrastructure connection to stay alive while somebody gets a coffee and comes back twenty minutes later to click Approve.
Failure handling gets simpler, idempotency does not
Under the old model, an instance crash raised a chain of awkward questions. Does the session survive? Does the state survive? Can the request resume? Can another node reconstruct it? With stateless requests, the answer is usually just: retry somewhere else.
That behaviour lines up naturally with retry policies, circuit breakers, autoscaling, blue/green and canary deployments, regional failover, serverless execution, and Kubernetes scale events.
What it does not solve is transaction semantics. search_documents is usually safe to
retry. transfer_money absolutely is not. Stateless transport makes retries easy, which
means tool authors now carry more of the responsibility for making them correct:
- idempotency keys on write operations
- stable operation or workflow identifiers
- server-side deduplication windows
- a status lookup so a client can ask "did my earlier call actually land?"
- compensating operations for partial failure
POST /mcp HTTP/1.1
Host: finance-tools.contoso.com
MCP-Protocol-Version: 2026-07-28
Idempotency-Key: 9415d0be-3f2c-4d18-8a55-0b7f5e2c9a41
Authorization: Bearer eyJ...
That header lets a backend reject a duplicate execution even when the agent SDK, the gateway, or an impatient retry loop sends the same HTTP request twice. This matters more every quarter, because agents are increasingly executing actions rather than running queries.
MCP still is not REST
This is where part of the debate goes sideways. The claim "MCP became stateless, therefore MCP is now just REST" mixes up transport architecture with protocol semantics.
MCP still uses JSON-RPC. An operation is tools/call with parameters, not
POST /tools/search or DELETE /users/1234. More importantly, MCP defines
concepts that a plain REST API does not inherently provide: tools, resources, prompts, capabilities,
elicitation, sampling, tasks, MCP Apps, and protocol discovery. Tools in particular are discoverable
entities with names, descriptions, and input schemas that a model can reason over. (Model Context Protocol)
You could of course expose GET /customers and POST /orders to a model
directly. But the model still needs to know which capabilities exist, which operation fits the task,
what parameters are required, what the tool actually means, and what the server supports. Without a
shared convention for answering those questions, every AI platform ends up writing its own adapter
for every API.
The layering is easier to reason about like this:
- HTTP moves bytes
- JSON-RPC frames the message
- MCP gives agents and tools a shared semantic contract
REST solves a different abstraction problem. Nothing about going stateless changed that.
Who is actually driving this
One correction worth making, because I keep seeing it framed wrong. This was not an Anthropic-only decision. Anthropic created MCP and remains a major participant, but the specification work now runs through the broader MCP project and its maintainer community, with implementation and design input from Microsoft, AWS, Google Cloud, Cloudflare, Figma, Netlify, Supabase, and others. The 2026-07-28 release is better read as a redesign driven by real production experience across many implementers. (Model Context Protocol blog)
Strategically that matters more than any individual feature. Protocols get valuable when competitors agree to implement them. TCP/IP was never valuable because one vendor owned it, and HTTP was never valuable because one browser spoke it. MCP's long-term value comes from agent vendors, AI platforms, cloud providers, SaaS vendors, and developer tooling converging on one way for agents to discover and invoke capabilities.
Where this goes next
The statelessness change is the part that got the attention, and it is genuinely useful. But it is not the part I find most interesting.
The same release added HTTP metadata that exposes the MCP operation to infrastructure, which means a
gateway can finally see which tool an agent is calling before the request reaches the
server. That is the change that turns MCP from a convenient agent protocol into something an
enterprise security team can actually govern, and it is what part 2
covers: Mcp-Method and Mcp-Name, per-tool authorization, resource-bound
OAuth tokens, header versus body trust, and where Azure API Management and Microsoft Foundry fit in
that picture.