mcp

MCP went the HTTP way: what changed in the 2026 protocol

MCP's 2026-07-28 revision removes protocol sessions and makes each request self-contained. Here is what changed and how to migrate.

Kirti Rathore··8 min read

Your MCP server works in development. Then you put three replicas behind a load balancer.

The first tool call reaches replica A. The next reaches replica C, which knows nothing about the session created by A. Now you need sticky routing, a shared session store, or a recovery path when A disappears.

The 2026-07-28 revision of the Model Context Protocol removes that problem from the protocol itself. There is no initialize handshake and no Mcp-Session-Id. Each request carries the information a server needs to handle it.

The short version is:

MCP removed hidden protocol state. It did not ban application state.

That distinction explains almost every important change in the new specification.

One terminology note before we continue: "MCP v1" and "MCP v2" are convenient labels, but the protocol is officially versioned by date. This article compares the legacy era through 2025-11-25 with the modern era beginning at 2026-07-28. SDK package versions are a separate thing.

The old model: initialize, then talk

Legacy MCP began with an initialize request. The client and server negotiated the protocol version and capabilities, then the client sent notifications/initialized. Over Streamable HTTP, a server could also issue an Mcp-Session-Id that the client returned on later requests.

That model was useful for interactive, bidirectional clients. It also tied later messages to context created earlier in the connection.

Diagram of legacy MCP architecture, session flow, JSON-RPC messages, primitives, and security boundaries
Legacy MCP through 2025-11-25: initialization and optional protocol sessions provide context across calls.

At small scale, the coupling is easy to miss. At production scale, it creates questions that infrastructure must answer:

  • Which replica owns this session?
  • What happens when that replica restarts?
  • Is session data shared, copied, or lost?
  • How long should the server retain it?

Worse, a protocol session is a poor container for application state. A browser, shopping basket, and debugging investigation can have different owners and lifetimes. Putting all three behind one connection-shaped identifier hides those differences.

The new model: every request stands alone

Modern MCP makes a request self-describing. The protocol version and client capabilities travel in request _meta; clients should also include their identity. For Streamable HTTP, routing information is mirrored into headers.

POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Authorization: Bearer ...
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "search",
    "arguments": { "query": "checkout timeout" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "incident-agent",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

Any compatible replica can process that request. A client that wants to inspect the server first can call server/discover, but discovery is optional. It is no longer a mandatory state-creating handshake.

Diagram of modern stateless MCP architecture, request flow, JSON-RPC messages, primitives, and security boundaries
Modern MCP from 2026-07-28: self-describing requests, optional discovery, explicit state, and no protocol session.

Here is the practical protocol diff:

  • Startup: legacy MCP uses initialize then initialized; modern MCP has no mandatory handshake.
  • Version and capabilities: legacy clients negotiate them once; modern clients send them with each request.
  • Protocol session: the optional Mcp-Session-Id is gone.
  • Discovery: initialization-based discovery becomes the optional server/discover call.
  • Server-to-client requests: an open-stream backchannel becomes Multi Round-Trip Requests.
  • HTTP routing: Mcp-Method and Mcp-Name expose information that previously lived mostly inside JSON-RPC.
  • Catalog caching: modern list responses include explicit ttlMs and cacheScope hints.
  • Standalone HTTP GET/SSE: it is not part of modern Streamable HTTP.

MCP is still JSON-RPC, usually sent to one HTTP endpoint. It did not become REST. It simply became easier to run on ordinary HTTP infrastructure.

Stateful applications still work

Suppose a debugging tool needs to preserve an investigation across calls. The server can create an explicit handle:

{
  "investigation_id": "inv_447",
  "status": "collecting_evidence"
}

The agent passes that handle into the next tool call:

{
  "name": "inspect_deployment",
  "arguments": {
    "investigation_id": "inv_447",
    "deployment_id": "deploy_92"
  }
}

The investigation may live in a database or durable object. The important change is that its identity and lifecycle are explicit. It can survive a client restart, move between agents, expire under its own policy, and be authorized independently of the HTTP connection.

This is the same separation that makes the web scalable. HTTP is stateless, but web applications still have accounts, carts, jobs, and databases. The state belongs to the application, not to an implicit transport conversation.

MRTR replaces the backchannel

Removing server-initiated requests creates one obvious problem: what if a tool starts work and then needs user approval or another missing input?

Modern MCP uses Multi Round-Trip Requests (MRTR):

client -> server: tools/call(restart_service)
client <- server: resultType=input_required, requestState=opaque_value

client asks the user for approval

client -> any server replica: retry tools/call with inputResponses
client <- server: resultType=complete

The server does not keep the original worker suspended while it waits. It returns what it needs, and the client retries the operation with the answer. An opaque requestState can carry continuation data, but servers must treat it as untrusted input and protect its integrity when it affects authorization or business logic.

This improves resilience, but it also makes retry behavior more important. A side-effecting tool such as restart_service or create_incident should accept an idempotency key so a network retry does not perform the action twice.

Why operators should care

Stateless MCP makes several production concerns more conventional.

Scaling becomes simpler. Requests can use round-robin routing without pinning a client to one replica or sharing protocol-session memory.

Gateways gain useful context. Mcp-Method and Mcp-Name let a gateway distinguish tools/list from tools/call, or read_logs from restart_service, without parsing an arbitrary JSON body. That enables per-tool authorization, rate limits, metering, and audit rules.

Discovery becomes cacheable. Tool, prompt, and resource listings include freshness and cache-scope hints. Deterministic ordering also keeps prompt caches stable when the catalog has not changed.

Failure is more explicit. A broken response stream means the client may need to issue a new request. Application handles and idempotency rules make the recovery contract visible instead of relying on a server session to survive.

For teams building AI tools for developers, this is the larger design lesson: make state, authority, cost, and retries visible to the caller. A machine client cannot safely infer lifecycle rules hidden inside a connection.

A practical migration path

You do not need a flag-day rewrite. The official compatibility model allows clients and servers to support both eras.

  1. Find session-shaped application state. Search for data keyed by Mcp-Session-Id or held only in a connection-local object.
  2. Create explicit handles. Give each durable object, such as a browser or investigation, its own identifier and lifecycle.
  3. Make retries safe. Add idempotency keys to tools with external side effects.
  4. Implement the modern request path. Support per-request metadata, server/discover, modern headers, and MRTR where needed.
  5. Keep legacy behavior at the edge. Continue accepting initialize for older clients while routing both eras into the same application services.
  6. Test across replicas. Run consecutive calls against different instances and terminate a worker between calls. The workflow should continue from explicit state.

Do not use a transport session as a substitute for authentication, rate-limit identity, trace correlation, or application ownership. Use the authenticated principal for authority, trace IDs for telemetry, and application handles for durable work.

The useful mental model

The 2026-07-28 revision is a breaking protocol change, but its architecture is easier to reason about:

MCP request state     -> travels with the request
application state     -> explicit handle or durable store
authorization state   -> identity and policy system
long-running work     -> task or extension

Modern MCP does less on your behalf. That is the point.

The protocol no longer pretends that a connection is the right home for a browser, an investigation, or a job. Those objects remain stateful, but now they are visible, addressable, and independently scalable.

That makes MCP a better foundation for the kind of agent systems we are building at Modulo AI: disposable compute around explicit, durable work.

The official references are the 2026-07-28 release announcement, the modern Streamable HTTP specification, and the accepted proposals for stateless MCP and session removal.