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.

  1. 1No sessions. Protocol-level sessions and the Mcp-Session-Id header 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.
  2. 2No handshake. The initialize and notifications/initialized exchange is removed. Each request carries the protocol version and client capabilities in _meta (under io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities). A version the server cannot handle returns UnsupportedProtocolVersionError.
  3. 3A discovery call. Servers must implement server/discover, which advertises supported versions, capabilities and identity. Clients may call it first to pick a version.
  4. 4Server-to-client traffic is reshaped. The HTTP GET stream and resources/subscribe give way to subscriptions/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 an input_required result, 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/discover or subscriptions/listen, and it does not write the _meta fields 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: true as 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

  1. 1Find every place you read the session ID, and replace each with a handle your tools return and accept.
  2. 2Implement server/discover and accept the version and capabilities in _meta on every request.
  3. 3Add ttlMs and cacheScope to list and read results, and use private scope for anything caller-specific.
  4. 4Return tools in a stable order, and make side-effecting tools safe to retry.
  5. 5Replace server-initiated asks with input_required results.
  6. 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.

Sources

Templates mentioned

See the technique running

Play the live demos, open the one you like, and start with a free template or unlock the whole library.

We use essential cookies to keep you signed in, a referral cookie only if you arrive through a partner link, and cookie-free analytics. No ads, no tracking. Cookie Policy