← All posts
Blog

MCP's Stateless Rewrite Changes How You Deploy Every Tool Server

July 30, 2026

The infrastructure everyone quietly depends on stopped being quiet

MCP shipped from Anthropic in November 2024 as a way to wire a local model to a handful of tools on your laptop. By the time the protocol's maintainers finalized the 2026-07-28 specification this week, it had become the default connective tissue between agents and business systems, with Tier 1 SDKs seeing "close to half-a-billion downloads a month" [1]. That kind of curve turns a protocol into infrastructure, and infrastructure gets rewritten the moment its original assumptions stop matching how people actually run it. The original MCP assumed a single client talking to a single local server over a persistent connection. Once teams started running MCP servers behind load balancers, in Kubernetes, and across regions, that assumption became the bottleneck: sticky sessions, shared Redis stores, and reconnection logic that had nothing to do with what the protocol was supposed to solve. The 2026-07-28 spec, locked as a release candidate on May 21 and shipped final on July 28 after a ten-week validation window, removes that assumption entirely [1][2].

What actually breaks on the wire

The headline change is real: MCP is now stateless at the protocol layer. The initialize/initialized handshake and the Mcp-Session-Id header are gone (SEP-2575, SEP-2567). Every request now carries its own protocol version, client identity, and capabilities in _meta, so any server instance can answer any request [1]. Two smaller changes make that practical to operate. Streamable HTTP requests must now include Mcp-Method and Mcp-Name headers (SEP-2243), so a load balancer or gateway can route and rate-limit purely on those headers without parsing the JSON-RPC body — and servers reject requests where the headers and body disagree. Second, tools/list, resources/list, and resources/read results now carry ttlMs and cacheScope fields modeled on HTTP's Cache-Control (SEP-2549), so a client knows exactly how long a tool list is fresh and whether it's safe to share across users. A minimal client-side cache now looks like this:

async function getTools(client) {
  const cached = toolCache.get(client.id);
  if (cached && Date.now() < cached.expiresAt) {
    return cached.tools;
  }
  const res = await client.request('tools/list', {}, {
    headers: { 'Mcp-Method': 'tools/list' }
  });
  toolCache.set(client.id, {
    tools: res.tools,
    expiresAt: Date.now() + (res.ttlMs ?? 0),
  });
  return res.tools;
}

Server-initiated calls that used to require a held-open SSE stream — clarifying questions mid-tool-call, elicitation — move to a Multi Round-Trip pattern (SEP-2322). A server answers tools/call with an InputRequiredResult containing the fields it still needs and an opaque requestState; the client collects the inputs and re-issues the call, echoing requestState back. The server reconstructs context from that state — the protocol never holds a connection open. As one lead maintainer put it, this is a deliberate transfer, not a removal: "we did shift the responsibility of creating and managing state to the developers" [2].

Long-running work finally gets a real primitive

Tasks shipped as an experimental core feature in the 2025-11-25 spec; production use pushed it out of core and into an official extension (SEP-2663), contributed by AWS and available day one in Bedrock AgentCore [1]. A server now answers tools/call with a task handle instead of a blocking result. The client drives the lifecycle with tasks/get, tasks/update, and tasks/cancel. Task creation is server-directed — the server decides when a call becomes a task, once the client has advertised support for the extension — and handles are designed to survive a dropped connection, so a different client instance can resume polling the same taskId. tasks/list is gone entirely, because listing every task across clients isn't a well-defined operation once there's no session to scope it to. If you built against the experimental Tasks API, this is a full migration, not a patch.

The security perimeter didn't disappear, it moved

Statelessness eliminates a real class of protocol-level risk — session hijacking, unsolicited server pushes, ambiguous auth defaults. It does not eliminate risk; it relocates it. As Akamai's threat research team put it, once state, UI, and async tasks live at the application layer, "critical security boundaries are now entirely dependent on how developers implement them" [3]. The auth changes reinforce that. Clients must now validate the iss parameter on authorization responses per RFC 9207 to close a mix-up attack that's more common in MCP's one-client, many-server pattern; Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents; and Enterprise-Managed Authorization lets an identity provider provision MCP server access centrally instead of per-app OAuth prompts [4]. Roots, Sampling, Logging, the old HTTP+SSE transport, and DCR all move into a formal 12-month deprecation window rather than disappearing overnight [6] — but MCP Apps, which render tool-declared HTML in a sandboxed iframe, is now a first-class extension, and every UI-initiated action still has to travel through the same JSON-RPC audit path as a direct tool call. That's a genuinely new governance surface for anything security teams haven't yet mapped.

MCP still isn't your gateway

The stateless core makes MCP servers themselves cheaper to run — round-robin load balancing instead of session affinity, serverless and edge deployment instead of pinned instances. It does not replace the layer that decides who is allowed to call a tool, what an App is permitted to render, how long a Task may run, and what it cost. That work — RBAC, output guardrails on rendered UI, per-task cost attribution to the launching agent — still belongs at a gateway sitting in front of your MCP servers, using the same discovery and observability it already provides rather than bolting a parallel governance system onto every server individually [5]. The protocol standardizes the wire format; it was never going to standardize your policy.

Avant Concepts take

This is the correct rewrite, and it's overdue — we've watched clients burn engineering weeks on sticky-session infrastructure for what is, at bottom, a tool-calling interface. But don't read "stateless protocol" as "less work." It's the opposite: MCP just handed you the state management, the auth validation, and the UI trust boundary it used to paper over, and the 12-month deprecation clock is real. If you're running MCP servers in production and don't already have a gateway doing RBAC, cost attribution, and output guardrails in front of them, this migration is the forcing function to add one — hand-rolling that governance per server is exactly the kind of infrastructure work you shouldn't be building from scratch when proven primitives exist. And if your team is about to wire up Tasks or MCP Apps without an eval harness watching what those long-running calls actually do and what that rendered UI actually triggers, you're not adopting a new capability, you're adding an unmeasured one. Ship the observability first.