
On July 28, 2026, the Model Context Protocol shipped its largest revision since launch, and it's a big one: MCP is now stateless by default. No more initialize handshake, no more session IDs, no more sticky connections. Today, we're bringing that shift into NitroStack 🎉
The problem with sessions
Every MCP server built against the 2025-era spec (2025-06-18) started the same way. A client POSTs initialize, the server negotiates a protocol version and capabilities, the client confirms with notifications/initialized, and from then on every request rides that same session, tracked by an Mcp-Session-Id header.
That worked, but it meant your MCP server carried state that had to live somewhere. Scale to multiple replicas and you needed sticky routing or a shared session store, or you risked breaking a client mid-conversation. It's the kind of infrastructure tax that shouldn't exist for something that's really just HTTP underneath.
What changes with 2026-07-28
The new spec removes that tax at the protocol level:
- No handshake.
initialize/initializedare gone. Capability discovery is now a single, optional, cacheableserver/discovercall, and a client can skip it entirely and go straight totools/listortools/call. - No session state. Every request is self-describing. Instead of relying on state pinned at connection time, each request carries its own envelope right in
_meta: protocol version, client capabilities, and client info. Any server replica behind a load balancer can answer any request, because there's nothing to pin. - Standard routing headers. Streamable HTTP requests now carry
MCP-Protocol-Version,Mcp-Method,Mcp-Name, and optionalMcp-Param-*headers that mirror the JSON-RPC body. Gateways and proxies can route and rate-limit MCP traffic without ever parsing the request body, and if a header disagrees with the body, that's now a hard rejection before the tool even runs. - Multi-round-trip requests instead of push. A stateless HTTP request has no open channel for a server to call back to the client mid-flight. So server-to-client interactions like elicitation and sampling are now modeled as a request/response pair: the handler returns an
input_requiredresult with an opaquerequestState, the client gathers the answer, and reissues the original call withinputResponsesattached. The server keeps nothing in memory between the two calls. - Cache hints, built in. Cacheable results like
tools/list,resources/read, andserver/discovercan now declare attlMsand acacheScope(publicorprivate). "Skip discovery, cache the tool list" becomes a standardized behavior instead of a client-specific guess. - Capabilities become extensions. Things that used to be baked into the core spec, like MCP Apps and Tasks, are now named, independently versioned extensions negotiated at
server/discover. The core protocol stays small, and extensions move at their own pace.
Alongside all of that, there's full JSON Schema 2020-12 support for tool schemas (composition, $ref, unrestricted output schemas), tightened OAuth flows including issuer-bound credentials and a shift from Dynamic Client Registration toward Client ID Metadata Documents, and the formal deprecation of Roots, Sampling, and MCP-level logging in favor of tool parameters, your host's own LLM APIs, and standard observability tooling.
What this means if you're building on NitroStack
The headline is simple: an MCP server can now be a plain, horizontally scalable HTTP handler. No sticky sessions, no shared session store, no coordination problem when you add a second replica.
That maps naturally onto how NitroStack servers are already structured. Tools, resources, and prompts are declared with decorators and resolved through dependency injection per request, so there was never really an architectural reason for your server to hold connection-scoped state, and now the protocol doesn't ask it to either. Scale out behind a load balancer the same way you'd scale any stateless service, cache what's cacheable at the edge, and let your gateway route on Mcp-Method and Mcp-Name without unpacking JSON-RPC bodies.
If you rely on server-to-client interactions today, like elicitation or sampling, those now follow the request/resume pattern above instead of a long-lived push channel. That's a much more forgiving shape for HTTP infrastructure (proxies, gateways, load balancers) than a persistent stream ever was.
Where this goes next
We don't think of stateless MCP as a one-time migration to check off. It's the direction the whole protocol is heading: a small, stable core with capabilities like Apps and Tasks moving out into independently versioned extensions, each able to evolve on its own schedule. That's the same bet a lot of good infrastructure makes, keep the core boring and let the edges innovate.
For NitroStack, that means the framework's job isn't just to support 2026-07-28 today. It's to keep tracking the spec as new extensions land, so the servers you build now don't need a rewrite every time MCP adds a capability. Request-scoped, horizontally scalable, and boring in the best way, that's what we're building toward.
Get started
Stateless MCP is a protocol-level change, and NitroStack is built to track the spec closely as it evolves. If you're starting a new server, this is the shape to build for from day one. And if you're running an existing one, the good news is that the architecture NitroStack already encourages, request-scoped resolution with no server-held session state, is exactly what the new spec expects.
Explore the docs at nitrostack.ai or drop into Discord with questions as you migrate.

Sharvari Raut
Author
A blog poet ✍🏻