---
slug: "mcp-stateless-spec-change"
title: "MCP Went Stateless: The 2026-07-28 Spec Change, Explained"
description: "MCP's 2026-07-28 revision removed sessions and the initialize handshake. Here's what changed, why it matters on Workers, and what our own MCP server does about it."
date: "2026-08-30"
category: "Engineering"
featured: false
readingTime: 6
author: "Karlis Bērziņš"
image: "/blog/images/mcp-stateless-spec-change.jpg"
updatedAt: "2026-09-14"
---

## MCP No Longer Needs a Session

As of the [2026-07-28 revision](https://modelcontextprotocol.io/specification/2026-07-28/changelog) of the Model Context Protocol (MCP), a server no longer negotiates a session with a client before it can do anything. The `initialize` / `notifications/initialized` handshake is gone, the `Mcp-Session-Id` header is gone, and every request now simply declares its own protocol version and capabilities. A Streamable HTTP MCP server can be, for the first time, an actually stateless HTTP endpoint — which matters a lot if that endpoint runs on Cloudflare Workers, where "stateless by default" is the whole execution model.

<div class="stat-grid">
  <div class="stat-cell"><div class="val">6</div><div class="lbl">spec revisions since Nov 2024</div></div>
  <div class="stat-cell"><div class="val">0</div><div class="lbl">Durable Objects our /mcp needs</div></div>
  <div class="stat-cell"><div class="val">8</div><div class="lbl">tools it exposes, session-free</div></div>
</div>

## What actually changed

The [full changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog) against the prior revision (2025-11-25) is long, but the parts that matter for anyone running an MCP server are:

1. **Sessions are gone.** No more `Mcp-Session-Id` header, no more per-connection state on `tools/list` or `resources/list` — every request is independent. Servers that genuinely need cross-call state now pass an explicit, server-minted handle as an ordinary tool argument, instead of relying on the transport to remember who they're talking to.
2. **The handshake is gone.** `initialize` and `notifications/initialized` are removed. Every request carries its own protocol version and client capabilities in `_meta`, and a version mismatch just returns an `UnsupportedProtocolVersionError` instead of failing a connection-level negotiation.
3. **A new mandatory RPC, `server/discover`.** Servers must implement it so a client can ask up front which protocol versions, capabilities, and identity a server supports — useful once negotiation isn't tied to a handshake anymore.
4. **Long-lived streaming moved to opt-in.** The old GET-endpoint-plus-`resources/subscribe` pattern is replaced by `subscriptions/listen`, a single stream a client explicitly asks for change notifications on, rather than something implicitly kept open per connection.
5. **Roots, Sampling, and Logging are deprecated**, and the old HTTP+SSE transport (already deprecated since 2025-03-26) is formally reclassified under the spec's new feature-lifecycle policy, which sets a minimum twelve-month deprecation window before anything is actually removed.

None of this is a small tidy-up. It's a deliberate move away from treating an MCP connection like a stateful RPC channel, and toward treating each request like what it actually is on most real deployments: one HTTP call that a load balancer, a CDN, or a Worker can route anywhere, with no guarantee it lands on the same machine as the last one.

## Why this specifically mattered on Workers

Cloudflare Workers don't hold state between requests unless you explicitly reach for something like a Durable Object. That's the entire appeal of the platform — a request comes in, a fresh isolate handles it, and nothing sticks around waiting for the next one. The old session-based MCP transport fought that model directly: a client picked up an `Mcp-Session-Id` on `initialize` and was expected to keep using it, which meant a server either needed a Durable Object to remember that session existed, or it needed to fake statelessness and hope no client relied on the parts that assumed otherwise.

Our own `/mcp` endpoint (it's live — see the [MCP calculators guide](/blog/ai-agents-mcp-servers-vs-wordpress) for what it exposes) never used a Durable Object for this. It's a single `defineEventHandler` in a Nuxt server route, built on Cloudflare's [`agents`](https://www.npmjs.com/package/agents) package and `@modelcontextprotocol/server`'s `McpServer` class:

```ts
function createServer() {
  const server = new McpServer({ name: 'tunovix-calculators', version: '1.0.0' })
  server.registerTool('website_cost_calculator', { description, inputSchema }, handler)
  // …7 more tools, same shape
  return server
}

export const mcpHandler = createMcpHandler(createServer)
```

That already worked fine under the old handshake-based spec — `createMcpHandler` handled session bookkeeping internally. But it's worth noting that Cloudflare's own `agents` package (v0.22.0, the one that ships `createMcpHandler`) already has logic that special-cases the string `"2026-07-28"` when deciding whether to treat a connection as stateless without a prior handshake. The SDK authors were tracking this spec change closely enough to code against the exact revision date before most servers had a reason to care — a decent signal for how significant they considered it.

## What breaks, and what doesn't

Backward compatibility is handled through version negotiation, not a hard cutover. A client built against 2025-11-25 or earlier can still talk to a 2026-07-28 server: the spec explicitly carries forward interop guidance for "handshake-based protocol revisions," and `server/discover` doubles as a backward-compatibility probe on stdio transports. What *doesn't* survive the transition unmodified is any server implementation that leaned on session state as a substitute for passing real identifiers around — if a tool call implicitly depended on "whichever session called `initialize` five minutes ago," that assumption needs to become an explicit argument now.

For a server like ours — [22 tools today](/services/mcp-development), from calculators to multi-turn workflow walkthroughs — each call still takes its input and returns a JSON result with no memory of what came before, so there was nothing to migrate. That's less a claim about being ahead of the curve than a reflection of what these tools actually needed to be: request in, calculation out, nothing held in between. The spec just caught up to what a stateless deployment platform already made the practical default.

## The takeaway for anyone building an MCP server on Workers (or Lambda, or any FaaS platform)

If your MCP server's tools don't need to remember anything about the caller between requests, the 2026-07-28 spec removes a category of infrastructure you never actually needed — session storage, sticky routing, or a Durable Object whose only job was outliving a handshake. Check the [live server on `/status`](/status), which runs a real `tools/call` against `website_cost_calculator` on every status check, not just a binding check — that's the same request/response shape every other call gets, no session required. Full request/response examples for this exact server are in the [API reference](/api-reference).

If you're scoping a new MCP server and want a second opinion on whether the tools you're planning actually need cross-call state or just look like they do, [get in touch](/contact) — that's usually a fifteen-minute conversation with the [team actually building MCP servers](/services/mcp-development) — listed as a [Designrush Partner](https://www.designrush.com/agency/ai-companies) among AI development companies — not a redesign.

