MCP went stateless in the July spec revision — here's what breaks if you self-host a server
I spend a fair amount of time with MCP servers — some self-hosted, some I've written myself — so when the Model Context Protocol pushed a new spec revision, I actually read the changelog diff against the previous one instead of skimming the summary. It's one of the more aggressive breaking redesigns I've seen in a spec that's supposed to be stabilizing, and two specific removals are the kind of thing that'll silently break a self-hosted setup rather than throw an obvious error.
No more sessions, no more handshake
The initialize / notifications/initialized handshake is gone, along with the Mcp-Session-Id header on the Streamable HTTP transport (SEP-2567, SEP-2575). Every request now carries its own protocol version and client capabilities in _meta — io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities — and a version mismatch returns a dedicated UnsupportedProtocolVersionError instead of failing at connection time. List endpoints like tools/list no longer vary per-connection, since there's no connection-scoped state left to vary by. Any server logic that assumed "the client initialized once, so I can remember things about this connection" needs an explicit server-minted handle passed as an ordinary tool argument instead.
There's a new required RPC for this world: server/discover, which every server must implement to advertise its supported protocol versions, capabilities, and identity. Clients can call it up front for version negotiation, including over STDIO where there's no handshake to piggyback version info onto anymore.
The one that actually hurts: SSE resumability is gone
This is the change I'd flag first if you're running anything behind Traefik, nginx, or any reverse proxy with its own idle-connection or buffering behavior. The old GET/SSE subscribe model — and with it, the Last-Event-ID header and SSE event IDs that let a client resume a dropped stream without redoing the whole request — is removed. It's replaced by subscriptions/listen, a single long-lived POST-response stream that clients explicitly opt into for specific notification types (toolsListChanged, resourceSubscriptions, and friends). But there's no redelivery mechanism for it. If that stream breaks mid-request, the client has to re-issue the entire request with a new request ID. For a long-running tool call over a flaky network hop or through a proxy with aggressive timeouts, that's the difference between "briefly stalled" and "silently restarted from zero." If you're fronting an MCP server with any reverse proxy, this is worth testing deliberately rather than assuming it degrades the way the old transport did.
What's deprecated, not removed — yet
Roots, Sampling, and Logging are formally deprecated under a newly codified twelve-month minimum deprecation window — still functional, but new implementations shouldn't add support for them. The suggested migrations are concrete: pass directories/files via tool parameters instead of Roots, integrate directly with the LLM provider's API instead of Sampling, and log to stderr or OpenTelemetry instead of the Logging feature. HTTP+SSE transport, already deprecated since an earlier revision, is now formally reclassified as Deprecated under that same lifecycle policy — migrate to Streamable HTTP if you haven't.
One detail I actually like: tools/list responses now require ttlMs and cacheScope fields (a new CacheableResult interface), specifically so clients can cache tool listings and improve LLM prompt-cache hit rates. That's a small spec detail with a direct line to token cost for anyone running agents against a stable toolset.
What I'm actually changing
For any MCP server I'm self-hosting behind a reverse proxy, the SSE resumability removal is the thing I'm testing for first — deliberately killing the connection mid-stream and confirming the client actually re-issues cleanly instead of hanging. Everything that leaned on session state gets rechecked for whether it needs to become an explicit handle argument. And the deprecated features — Roots, Sampling, Logging, HTTP+SSE — go on the "migrate before the twelve-month window closes" list rather than the "ignore until it breaks" pile, since this spec is explicit that the window is now a real, tracked policy rather than an informal heads-up.