Blog · · 5 min read
MCP goes stateless: what the 2026-07-28 spec means for remote servers
The 2026-07-28 revision of the Model Context Protocol removes protocol-level sessions and the initialize handshake. Every request now carries its own protocol version and client capabilities, and servers must answer a new server/discover call. For a remote server, that means any instance behind a load balancer can answer any request, and state you used to hide in a session has to become an explicit argument. We checked the spec pages on October 28, 2026. Our server's code does not use sessions, but we have not yet implemented the new request format, and this post says exactly where that leaves us.
By the AnimatedWebs team
Facts about the specification below were read on modelcontextprotocol.io on October 28, 2026, from the 2026-07-28 changelog and the specification overview. Statements about our own server come from its source code.
What changed
The changelog lists nine major changes. Four of them matter most to anyone running a server on the public internet.
- 1No sessions. Protocol-level sessions and the
Mcp-Session-Idheader are gone from the Streamable HTTP transport. The list endpoints (tools/list,resources/list,prompts/list) no longer vary per connection. A server that needs state across calls hands out explicit handles that the client passes back as ordinary tool arguments. - 2No handshake. The
initializeandnotifications/initializedexchange is removed. Each request carries the protocol version and client capabilities in_meta(underio.modelcontextprotocol/protocolVersionandio.modelcontextprotocol/clientCapabilities). A version the server cannot handle returnsUnsupportedProtocolVersionError. - 3A discovery call. Servers must implement
server/discover, which advertises supported versions, capabilities and identity. Clients may call it first to pick a version. - 4Server-to-client traffic is reshaped. The HTTP GET stream and
resources/subscribegive way tosubscriptions/listen, a single long-lived POST response stream the client opts into. Server-initiated requests (roots, sampling, elicitation) are replaced by a pattern called Multi Round-Trip Requests: the server returns aninput_requiredresult, and the client retries the original request with the answers attached.
Other changes worth knowing: every result now has a required resultType field. SSE stream resumability (Last-Event-ID) is removed, so a broken stream loses the in-flight request and the client must reissue it with a new request ID. Streamable HTTP POSTs require Mcp-Method and Mcp-Name headers. Results of the list and read calls must carry ttlMs and cacheScope (public or private) so clients and intermediaries know how long they may cache. Tasks moved into an official extension, and servers should return tools in a deterministic order.
On the deprecation list: Roots, Sampling and Logging; the older HTTP+SSE transport; and OAuth Dynamic Client Registration (RFC 7591), in favor of Client ID Metadata Documents. Deprecated features stay in the spec for a minimum twelve-month window under a new lifecycle policy.
What it means for a remote server
Scaling gets simpler
A session pins a client to one server process, or forces you to put session data in a shared store. Without it, a request is self-contained: version, capabilities and arguments arrive together, so any replica, serverless function or region can answer. That is the main operational gain.
Per-user state moves to the arguments
If your server used the session to remember something (a draft, a cursor, a chosen project), that memory needs a home you control: a database row keyed by a handle you mint and return. The client carries the handle in later calls.
Identity comes from the request, not the connection
With list endpoints no longer varying per connection, anything that differs between callers must be derived from the credentials on each request. This is our reading of the change, not a quote from the spec: if your tool list or its contents depend on who is asking, mark the response cacheScope: "private" so a shared cache cannot hand one caller's list to another.
Questions to the user become a retry
A server that used to pause mid-call to ask for confirmation now answers with input_required and the questions, then expects the same request back with inputResponses. The server cannot hold the request open waiting.
Retries must be safe
With stream resumption gone, clients retry whole requests, so tool calls with side effects should be idempotent or guarded by a client-supplied key.
Where our server stands
The AnimatedWebs MCP server is a Next.js route built with the mcp-handler package, we have version 2.2.0 installed. It answers at mcp.animatedwebs.com, which our proxy rewrites to /api/mcp. The route exports GET, POST and DELETE handlers, is rate limited to 240 requests per 60 seconds, and requires a bearer token on every request: either an OAuth access token from the browser sign-in or an API key beginning with aw_. An unauthenticated call gets a 401 whose WWW-Authenticate header points at our protected-resource metadata.
Here is what the code shows.
- No session code of ours. Our source has no use of
Mcp-Session-Id. Each tool handler works from the token's tier and the catalog or database, not from anything remembered on the connection. - No server-initiated requests. We register tools, prompts and resources. We do not call sampling or elicitation, and we do not set a log level.
- Not yet the new wire format. Our code does not implement
server/discoverorsubscriptions/listen, and it does not write the_metafields from the new revision. Those would come from the protocol library we build on, or from our own work if the library lags. We have not tested against clients that speak 2026-07-28, so we make no claim that our endpoint is compatible with that revision today. - OAuth already points the right way. Our authorization server metadata advertises
client_id_metadata_document_supported: trueas well as a registration endpoint, S256 PKCE, and the authorization code and refresh token grants. Dynamic Client Registration is deprecated in this revision, and we still offer it for clients that need it.
Our tool for fetching a build prompt takes a template slug as an ordinary argument, for example wren-halloway-diamonds, and returns the result in one round trip. Calls of that shape need no change under a stateless protocol.
A short checklist for your own server
- 1Find every place you read the session ID, and replace each with a handle your tools return and accept.
- 2Implement
server/discoverand accept the version and capabilities in_metaon every request. - 3Add
ttlMsandcacheScopeto list and read results, and use private scope for anything caller-specific. - 4Return tools in a stable order, and make side-effecting tools safe to retry.
- 5Replace server-initiated asks with
input_requiredresults. - 6Plan to retire HTTP+SSE, Roots, Sampling and Logging use, and to support Client ID Metadata Documents.
Read the changelog before you build. If you use our server, setup instructions are at /mcp.
