In depth
Every MCP session begins with a three-step handshake. Step 1: client sends `initialize` with its supported protocol version, declared capabilities, and identifying info (name + version). Step 2: server responds with the agreed protocol version, its own capabilities, and server info. Step 3: client sends `notifications/initialized` to confirm the handshake is complete. Only then can tool calls flow.
During initialize, protocol version negotiation happens. If the client asks for `2025-03-26` but the server only supports `2024-11-05`, the server downgrades — or returns an error if it can't meet the client's minimum. This keeps old clients and new servers (and vice versa) interoperable.
Capabilities declared here are fixed for the session. A server can't acquire `sampling` mid-session; the client can't start supporting `roots/listChanged` after the fact. Declare everything up front.
Initialize is also where auth typically happens. The client sends its auth context (Bearer token, OAuth session), and the server validates before proceeding. A failed initialize means the session never starts.