The 2026-07-28 MCP specification is final, all four Tier 1 SDKs shipped support on publication day, and the protocol is now pulling close to half a billion downloads a month across those SDKs. The TypeScript and Python SDKs have each passed a billion total downloads. That is enough adoption that the design decisions in this revision deserve a careful read rather than a skim of the changelog.
The headline: MCP is now stateless at the protocol layer.
What got removed
The list is longer than most coverage suggests.
- The
initialize/notifications/initializedhandshake is gone. Protocol version and client capabilities now ride in_metaon every request (io.modelcontextprotocol/protocolVersion,io.modelcontextprotocol/clientCapabilities). - Protocol-level sessions and the
Mcp-Session-Idheader are gone from the Streamable HTTP transport. List endpoints no longer vary per connection. - SSE stream resumability and message redelivery are gone, including the
Last-Event-IDheader and SSE event IDs. -
ping,logging/setLevel, andnotifications/roots/list_changedare gone. Log level is set per request via_meta. - Roots, Sampling, and Logging are deprecated under a new twelve-month deprecation window.
The engineering payoff is real and immediate. Any server instance behind a round-robin load balancer can answer any request, so remote MCP servers drop into serverless functions, edge platforms, and autoscaling Kubernetes without anyone building session affinity first. The new Mcp-Method and Mcp-Name headers let gateways route and meter on header values instead of parsing JSON bodies. Required ttlMs and cacheScope fields on list and read results make ordinary HTTP caches useful for the first time. This is MCP deciding to behave like a well-mannered HTTP citizen, and for tool-calling workloads it is the correct call.
Where the state actually went
State in a distributed system is roughly conserved. Taking it out of the protocol layer relocates it, it does not delete it. The spec is explicit about the destination: servers that need cross-call state use explicit, server-minted handles passed as ordinary tool arguments.
So the server mints a handle, returns it in a tool result, and the model threads it back as an argument on the next call. Connection state has become context-window state. It costs tokens on every turn it survives, it competes with everything else in the window, and correctness now depends on a language model reliably carrying an opaque identifier forward across a long trajectory without dropping or mangling it. That is a different reliability story than a session ID in an HTTP header.
The second relocation is blunter. With resumability removed, a broken response stream loses the in-flight request, and the client must re-issue it as a new request with a new request ID. For a 40ms tool call that is nothing. For a thirty second retrieval over a flaky mobile link, the work is thrown away and repeated from zero. The redesigned Tasks extension (io.modelcontextprotocol/tasks, now moved out of the core protocol) handles long-running operations through polling via tasks/get, which is the right pattern, but it is an extension now rather than something every client is guaranteed to speak.
The view from the layer below
Pilot Protocol sits under MCP at OSI L5, and from there the tradeoff reads differently. The framing in our IETF draft is that A2A defines what agents say to each other while Pilot defines how they reach each other, the same way TCP/IP sits beneath HTTP. Under that split, the state MCP pushed up into tokens stays down in the tunnel instead.
Identity gets established once rather than re-asserted per request. X25519 ECDH derives an AES-256-GCM key through HKDF, and the PILA frame binds an Ed25519 signature over the ASCII string auth, the sender's node ID, and the X25519 public key. The sender's node ID is used as GCM additional authenticated data, so every packet is cryptographically bound to its sender. Compare that to a stateless client re-declaring clientInfo in _meta on every single request and hoping the gateway in front of it is doing the validating.
Reliability stays the transport's problem. Sliding window bounded by the minimum of the congestion window and the peer-advertised window, SACK carrying up to four blocks per ACK, fast retransmit after three duplicate ACKs, RTO per RFC 6298 clamped between 200ms and 10 seconds. A dropped packet is retransmitted below the application. There is no lost request to re-issue because the application never finds out the stream hiccuped.
Addressing stays stable too. A 48-bit virtual address (16-bit network ID plus 32-bit node ID) survives an agent moving between networks, with STUN discovery, hole punching, and relay fallback for symmetric NAT handling reachability underneath. Across the roughly 250,000 agents currently connected, that is the part nobody has to think about.
These are complementary layers, not competing ones. MCP running over a Pilot tunnel is still stateless MCP. It just stops paying for the network's forgetfulness in tokens.
The honest read
Both layers are solving for different failure modes. MCP optimized for the deployment reality that most servers are stateless HTTP endpoints behind load balancers, and a genuinely simpler protocol came out of it. The bill arrives in context-window pressure and in discarded work on broken streams, and that bill scales with trajectory length rather than request count.
Which is the thing worth watching as agent runs stretch from seconds into hours. A protocol that is cheap per request and expensive per hour benchmarks very well and behaves differently in production. If handles accumulate in context and every dropped connection replays a slow tool call, the win at the HTTP layer is being financed somewhere harder to see.
References: the 2026-07-28 changelog, the specification announcement, and draft-teodor-pilot-protocol-01.
Top comments (0)