What is InitializeRequest?
InitializeRequest is the mandatory first message a client sends over a newly opened MCP transport connection. It establishes the shared context for the entire session by advertising the client's supported protocol version, its declared capabilities, and identifying metadata about the client implementation. Until the server processes this message and returns an InitializeResult, the protocol is in a pre-handshake state and no other request types are valid — sending them early is a protocol violation that conformant servers must reject. \n\n Within the MCP message flow, InitializeRequest sits at the very top of the lifecycle: transport opens, client sends InitializeRequest, server replies with InitializeResult, then the client emits the initialized notification to signal it has processed the server's capabilities. Only after that three-step exchange does the session enter the ready state where tools, resources, and prompts can be invoked. The params.protocolVersion field drives version negotiation — the server may accept the proposed version or respond with a version it supports, and the client must decide whether to proceed. \n\n The API surface is intentionally flat at the top level: a literal method string "initialize" and a params object containing three required fields. ClientCapabilities is the extensible object where optional feature support (roots, sampling, experimental) is declared. The clientInfo Implementation object carries name and version strings used for logging and diagnostics. Because this message defines what the session can do, its payload is typically small and latency-sensitive — it should never be deferred or batched.
When to use
Once per session — it MUST be the first request.
When NOT to use
Never re-send during an active session.
Notes
Version negotiation is client-driven
The client proposes a protocolVersion string in the request; the server either echoes it back in InitializeResult or responds with a lower version it supports. Clients must check the version in the response before assuming any capability is available. Hard-coding the latest version string without a fallback path is a common source of breakage when connecting to older server implementations.
No requests before handshake completes
The spec explicitly prohibits sending any request other than InitializeRequest before the initialized notification has been sent by the client. Race conditions in async client code — firing tool calls immediately after connect — will cause servers to return protocol-error responses. In SDKs like the TypeScript MCP SDK, the client.connect() promise does not resolve until the full three-step handshake is complete, protecting against this.
ClientCapabilities controls feature negotiation
Only capabilities declared in the InitializeRequest params.capabilities object are available for the session. For example, if the client omits the sampling capability block, the server must not issue sampling/createMessage requests. Forgetting to declare a capability your handler depends on is a silent failure — the server simply never invokes that path, with no error surfaced.
clientInfo is for diagnostics, not auth
The name and version fields inside clientInfo are informational only — servers should log them for observability but must not use them as an authentication or authorization signal. Malicious clients can supply arbitrary strings. Any access control must be enforced at the transport layer (TLS client certificates, OAuth tokens) before InitializeRequest is even parsed.
Reconnect resets the session entirely
If the underlying transport drops and reconnects, the handshake must be repeated from scratch with a fresh InitializeRequest. There is no resume or re-attach mechanism in the base protocol. Clients that cache session state (tool lists, resource subscriptions) must re-fetch that state after every successful re-initialization.
Fields
| Field | Type | Required | Purpose |
|---|---|---|---|
| method | 'initialize' | yes | Method identifier. |
| params.protocolVersion | string | yes | e.g. '2025-11-25'. |
| params.capabilities | ClientCapabilities | yes | Optional features the client supports. |
| params.clientInfo | Implementation | yes | Client identity. |
Examples
Initialize handshake
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": { "sampling": {} },
"clientInfo": { "name": "claude-desktop", "version": "0.7.0" }
}
}
Common mistakes
❌ Sending tools/list before initialize
✅ Wait for InitializeResult and send notifications/initialized first.