Learn MCP - Lifecycle: Initialize, Session & Stateless (2026-07-28)
Episode 3 of 23

Learn MCP - Lifecycle: Initialize, Session & Stateless (2026-07-28)

Comparing the two MCP lifecycle eras: the legacy flow with the initialize handshake and session state based on Mcp-Session-Id, versus the modern stateless 2026-07-28 flow that sends version, identity, and capabilities per request and uses server/discover for bootstrapping.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 2 we covered MCP's three-layer architecture and primitives. Now the question is: how does a connection start, stay alive, and end? The answer is what we call the lifecycle — and this is where MCP underwent its biggest overhaul on 2026-07-28.

This episode dissects two opposing eras: the legacy era that required an initialize handshake and stored session state, versus the modern era that is fully stateless. You'll understand why modern MCP servers can be deployed behind a load balancer without sticky sessions — something that was painful in the legacy era.

Legacy Era: The initialize Handshake and Session State

In specifications up to 2025-11-25, every MCP connection began with a mandatory initialize handshake:

  1. The client sends an initialize request containing the protocol version, client name, and capabilities.
  2. The server replies with the agreed version, its name, and its capabilities.
  3. The client sends an initialized notification to mark the handshake complete.
  4. Only then may the client send other requests like tools/list.
Contoh request initialize (era legacy)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": { "name": "my-host", "version": "1.0.0" }
  }
}

The key to this era is session state: the server remembers the handshake information per connection and identifies the connection via the Mcp-Session-Id header. All subsequent requests must carry this Mcp-Session-Id, because the server needs to know who is speaking and which version is in use.

The Session State Problem

Session state creates a problem that's uncomfortable in production: the same request must always land on the same server instance. If there are two server instances behind a load balancer, the second instance won't recognize the session from the first — the result is an error or a re-handshake.

The usual workaround is sticky sessions on the load balancer. The problem is that sticky sessions limit load distribution, complicate rolling restarts, and constrain horizontal scaling. That's the big task the stateless era answered.

Modern Era 2026-07-28: Stateless

On 2026-07-28, the protocol core was reworked to be stateless. The principle is simple: no more handshake, no more session state. Every request stands on its own and carries all the information the server needs:

  • Protocol version — sent per request, not negotiated once at the start.
  • Identity — which client is speaking, sent per request.
  • Capabilities — client and server capabilities, sent per request.

Because there's no state on the server, any request can be routed to any server. You can even place several different servers behind a single endpoint — a request is answered by whichever one is available. To try this stateless pattern, make sure your SDK is the latest version with npm install @modelcontextprotocol/sdk@latest.

Perbandingan mental: satu koneksi vs request mandiri
Legacy  : [initialize handshake] -> [session A] -> [request pakai Mcp-Session-Id]
Modern  : [request mandiri + versi + identity + capabilities] -> [response]

server/discover: The Handshake Replacement

If there's no handshake, how does the client know the server's version and capabilities? The answer is server/discover — a new method that replaces the handshake's role for bootstrapping, not for establishing a session.

Contoh request server/discover
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "server/discover",
  "params": {
    "protocolVersion": "2026-07-28",
    "clientInfo": { "name": "my-host", "version": "1.0.0" }
  }
}

The server replies with the list of supported protocols, its capabilities, and the chosen version. The client then stores the result as context — but there's no "live" session on the server. Subsequent requests still carry their own version and identity information.

Info

Don't confuse this with episode 8 on MRTR: server/discover is a one-way bootstrap for negotiating version, whereas MRTR (Multi-Round-Trip Requests) is a mechanism for interactive mid-call requests via messageId and routingId. Both were born in the same era (2026-07-28) but serve different purposes.

Consequences of Statelessness: Round-Robin and Horizontal Scaling

The stateless decision opens the door wide for production operations:

CapabilityLegacy EraModern Era
Load balancerRequires sticky sessionsFree (round-robin)
Rolling restartsComplicated (sessions lost)Safe (no sessions)
Horizontal scalingLimitedFree
Health checksNeed to know the instanceStateless, easy

With statelessness, requests can be routed to any server — a round-robin load balancer simply distributes requests evenly without tracking sessions. Server down? The next request is served by another instance with no re-handshake. This is the foundation that makes large-scale MCP deployments (episode 9) and fleet management (episode 16) feasible.

Managing Both Eras at Once

Because the ecosystem still has old servers, modern clients are advised to be dual-era compatible: knowing how to talk to both legacy and modern servers. The pattern is simple:

  • Start with a self-contained request (modern era).
  • If the server responds with an error because it doesn't recognize the stateless pattern, the client can fall back to the initialize handshake (legacy era).
  • Store the version negotiation result in client memory, then use the detected era.
Pseudokode fallback era (ilustrasi)
const discovered = await tryStatelessDiscover();
if (discovered.failed) {
  await initializeLegacyHandshake();
}

Note: this is just a flow illustration. The full implementation using the official SDKs will be covered in episode 6 (server) and episode 7 (client), and the two-era compatibility strategy in more depth in episode 17.

Conclusion

In this episode 3 you've understood the two MCP lifecycle eras: the legacy era using the initialize handshake plus Mcp-Session-Id session state, and the modern 2026-07-28 era which is stateless with version, identity, and capabilities sent per request, supported by server/discover for bootstrapping.

Key takeaways:

  • The legacy era required an initialize handshake and used Mcp-Session-Id to store state.
  • Session state forced sticky sessions and limited scaling — the main problem of the legacy era.
  • The modern era is stateless: version, identity, and capabilities are sent per request.
  • server/discover replaces the handshake for version and capability bootstrapping.
  • Statelessness enables round-robin load balancing and horizontal scaling without sticky sessions.

In the next episode 4 we'll cover Tools in depth — how a tool is defined with JSON Schema, called via tools/call, produces structured output, and what tool annotations are along with their limitations. See you in episode 4!