The MCP handshake (initialize → server capabilities → notifications/initialized) is a mandatory protocol sequence. A server that does not handle the initialize method returns a method-not-found error on the very first client message, causing immediate disconnection before any tool can be called. Processing tool requests before the handshake completes violates the protocol ordering guarantee and causes undefined behavior in clients that enforce the sequence. This is a baseline conformance requirement with no workaround on the client side.
Critical because a missing `initialize` handler causes clients to disconnect on first contact — no tools, resources, or prompts are reachable until the handshake completes successfully.
If using the MCP SDK, the handshake is handled automatically when you call server.connect(transport). Verify your server config includes all capabilities you implement.
// src/index.ts — SDK handles initialize automatically
const server = new Server(
{ name: 'my-server', version: '1.0.0' },
{ capabilities: { tools: { listChanged: true }, resources: {} } }
)
const transport = new StdioServerTransport()
await server.connect(transport) // handshake negotiated here
For custom implementations, add a handler for initialize that returns protocolVersion, capabilities, and serverInfo, and gates all other method handlers behind an isInitialized flag.
ID: mcp-server.transport-protocol.initialize-handshake
Severity: critical
What to look for: Enumerate the initialization sequence. Count the steps: client sends initialize, server responds with capabilities, client sends initialized notification. The MCP spec requires a handshake: the client sends initialize with its capabilities, the server responds with its capabilities, then the client sends notifications/initialized. Check that the server handles the initialize method. If using an SDK, verify the SDK's server is instantiated with the correct capabilities object. For custom implementations, check that the server responds to initialize with protocolVersion, capabilities, and serverInfo. Check that the server does not process tool/resource requests before initialization is complete.
Pass criteria: The server handles the initialize method and returns a valid response with protocolVersion, capabilities (listing which features are supported), and serverInfo (name and version). SDK-based servers have the server properly configured. The handshake must complete within no more than 3 message exchanges and under 5 seconds.
Fail criteria: Custom server does not handle initialize, or the response is missing required fields, or the server processes requests before initialization, or capabilities are incorrect.
Skip (N/A) when: All checks skip when no MCP server is detected.
Cross-reference: For version negotiation during handshake, see version-negotiation.
Detail on fail: "Custom JSON-RPC handler has no case for 'initialize' method — clients will get a method-not-found error and disconnect" or "Server processes tool calls before receiving 'notifications/initialized' — violates the MCP handshake protocol"
Remediation: If using an SDK, the handshake is handled automatically. For custom implementations:
// src/handlers/initialize.ts — proper handshake handler
server.setRequestHandler(InitializeRequestSchema, async (request) => ({ protocolVersion: "2025-03-26", capabilities: { tools: {} } }))
// Custom implementation — handle initialize
function handleMessage(message: JsonRpcMessage) {
if (message.method === 'initialize') {
return {
jsonrpc: '2.0',
id: message.id,
result: {
protocolVersion: '2024-11-05',
capabilities: {
tools: { listChanged: true },
resources: { subscribe: false, listChanged: true },
},
serverInfo: {
name: 'my-mcp-server',
version: '1.0.0',
},
},
}
}
// Only process other methods after initialized
if (!isInitialized) {
return { jsonrpc: '2.0', id: message.id, error: { code: -32600, message: 'Not initialized' } }
}
// ... handle other methods
}