C.W.K.
Stream
Lesson 04 of 05 · published

Lifecycle and Capability Negotiation

~22 min · initialize, lifecycle, capabilities, handshake

Level 0Curious Reader
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

An MCP session has four phases: connect, initialize, operate, shutdown. The first one is transport-specific (open a stdio pipe, open an HTTP connection); the last one is graceful close. The middle two carry all the protocol weight.

Initialize is the handshake. The client sends an initialize request advertising its protocolVersion, its capabilities (e.g. sampling, roots, elicitation), and its clientInfo. The server replies with the same envelope: the negotiated version (the highest both sides understand), its own capabilities, and serverInfo. After this exchange, both sides have a precise, written agreement about what the rest of the session can use.

The handshake matters because it is what lets the protocol evolve without lock-step upgrades. A new client can talk to an old server and only use the capabilities both advertise. A new server can ship an experimental capability that older clients ignore. Capabilities in MCP play the role version numbers play in HTTP/REST — they are how the parts move at different speeds without breaking each other.

Operate is everything after the handshake: tool calls, resource reads, prompt requests, sampling round-trips, log messages, progress events. Shutdown is a goodbye that cleans up resources on both sides; servers should treat shutdown as a chance to flush logs and release file handles.

Code

The initialize handshake on the wire·json
// Client -> Server
{
  "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "sampling": {},
      "roots": { "listChanged": true }
    },
    "clientInfo": { "name": "claude-code", "version": "1.42.0" }
  }
}

// Server -> Client
{
  "jsonrpc": "2.0", "id": 1, "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": {},
      "prompts": {}
    },
    "serverInfo": { "name": "github-mcp", "version": "0.4.0" }
  }
}
Reading capabilities to gate behavior·python
async with ClientSession(read, write) as session:
    init_result = await session.initialize()
    if "tools" not in init_result.capabilities:
        raise RuntimeError("This server does not advertise tools — wrong server?")
    if init_result.capabilities.get("logging"):
        await session.set_logging_level("info")  # safe to call only if advertised
    tools = await session.list_tools()

External links

Exercise

Run any MCP server (the SDK ships an 'echo' example, or any community server). Capture the initialize request and response with a transport sniffer or a debug logger. Read the negotiated capabilities. Now mentally reason: which methods are safe to call, and which would 405 you because the capability isn't advertised?

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.