Two calls that start every MCP session
Before an AI application can use an MCP server's tools, two things have to happen: the client and server need to agree on what protocol they're speaking, and the client needs to find out what tools actually exist. In the MCP protocol versions that dominate production today (2025-03-26 through 2025-11-25), that's the initialize request followed by tools/list.
Step 1: initialize
The client opens a connection — over stdio or Streamable HTTP — and sends an initialize request as the very first message. It carries:
protocolVersion— the MCP spec version the client speaks.capabilities— which optional protocol features the client supports (sampling, roots, elicitation, and so on).clientInfo— a name and version identifying the client implementation.
The server responds with its own protocolVersion, capabilities, and serverInfo, plus optionally instructions: free-text guidance the server wants injected into the model's context. Version negotiation is simple: if the server doesn't support the client's requested version, it responds with a version it does support, and the client decides whether to proceed or disconnect.
Step 2: notifications/initialized
Once the client accepts the server's response, it sends an initialized notification. This is a notification, not a request; no reply is expected. It formally marks the session as ready. Servers should not treat any other request as valid before this notification arrives, and well-behaved clients don't send tools/list or tools/call before it either.
Step 3: tools/list
With the session live, the client calls tools/list to discover what the server can do. The response is an array of tool definitions, each with:
name— a stable identifier the model will use to invoke the tool.description— natural-language text the model reads to decide when and how to use the tool.inputSchema— a JSON Schema describing valid arguments.outputSchema(optional, added in 2025-06-18) — a JSON Schema describing the tool's structured output, when it returns one.
Because tools/list responses can be large, the method supports cursor-based pagination: a response can include a nextCursor, and the client requests the next page by passing it back. Servers can also emit a notifications/tools/list_changed notification if their tool set changes mid-session, prompting the client to re-fetch.
Why the description field matters more than it looks
Unlike a REST API's OpenAPI spec, which mostly serves human developers and code generators, a tool's description in MCP is read directly by the model at inference time. It's effectively part of the prompt. That means a wording change, not just a schema change, can shift which tool the model picks or how it fills in arguments. This is the root of a lot of MCP schema drift, which we cover in a separate article.
Common failure modes in the handshake
A few things go wrong often enough to be worth watching for:
- Version mismatch — a client and server that don't share a compatible protocol version, especially after either side upgrades independently.
- Capability mismatch — a client calling a method (like
elicitation/create) the server never declared support for duringinitialize. - Slow or failing tools/list — a server that's healthy at the transport level but returns errors, empty tool lists, or a schema that no longer parses.
- Pagination bugs — cursors that never terminate, or clients that stop after the first page and silently miss tools.
Any one of these can leave an agent connected but non-functional, which is worse than an outright connection failure because nothing in the logs looks obviously broken.
What's changing
The MCP specification finalized on 2026-07-28 removes the initialize/initialized handshake entirely, moving toward a stateless model where protocol version and capabilities travel in request metadata on every call, with a new server/discover method for upfront capability checks. It's a significant redesign aimed at letting remote servers run behind plain load balancers instead of needing sticky sessions. That said, it's brand new (finalized only days ago), and real-world adoption will lag well behind the spec text. New clients are expected to detect older servers and fall back to the classic initialize handshake described above, so for the foreseeable future, understanding this handshake is still the practical baseline for anyone building or operating MCP servers.
Monitoring the handshake, not just the process
A server process staying up doesn't tell you the handshake still succeeds, or that tools/list still returns the schema your agents expect. MCP Vitals runs the actual initialize → tools/list sequence against your servers on a schedule, so a broken handshake or a quietly changed schema shows up as an alert instead of a support ticket. If you operate MCP servers other people depend on, it's worth checking that the handshake behaves the same way from the outside as it does in your own tests.