Hmbown/CodeWhale · error
MCP server ' ': unsupported protocol version ' ' (expected )
Error message
MCP server '{}': unsupported protocol version '{}' (expected {}) What it means
During the MCP stdio handshake, `validate_initialize_result` compares the server's `initialize` response `protocolVersion` against the client's `PROTOCOL_VERSION` and bails on any mismatch. The client speaks exactly one protocol version and refuses to interoperate with a server that negotiated a different one, since message schemas may diverge. This prevents subtle incompatibilities from surfacing later as malformed responses.
Solutions
- Upgrade (or downgrade) the MCP server binary so its protocol version matches the client's expected version.
- Check which version the client expects and, if a newer server is intentionally ahead, update this library to the release supporting that protocol version.
- Print the server's reported protocolVersion (`initialize` response) to confirm the exact mismatch before changing binaries.
- If a proxy/wrapper sits between client and server, ensure it passes `protocolVersion` through unmodified.
Example fix
// server package.json — pinned to an old protocol
"mcp-server": { "protocolVersion": "2024-10-07" }
// after: match the client's expected PROTOCOL_VERSION
"mcp-server": { "protocolVersion": "2025-03-26" } Defensive patterns
Strategy: validation
Validate before calling
// before spawning, pin and document the server version
if !supported_server_versions().contains(&config.server_version.as_str()) {
return Err(format!("server {} predates/passes pinned protocol version", config.name));
} Try / catch
match spawn_with_timeouts(&config, hs, req).await {
Err(e) if e.to_string().contains("unsupported protocol version") => {
eprintln!("upgrade or downgrade the MCP server binary to match the client protocol");
}
other => other,
} Prevention
- Pin the MCP server binary version alongside the client and upgrade them together.
- Log the server's reported protocolVersion on mismatch for fast triage.
- Test handshakes in CI against the exact server versions you ship.
When it happens
Trigger: `spawn_with_timeouts` starts a server subprocess, sends the `initialize` request, and the JSON response's `protocolVersion` field is a string not equal to `PROTOCOL_VERSION`. (A missing/empty protocolVersion produces a different context error.)
Common situations: The installed MCP server binary is older or newer than the protocol version the client pins (e.g. after a client upgrade the server lags behind, or a stale cached binary); a wrapper script proxies and rewrites the response; a custom server hardcodes an old protocol version string like "2024-10-07".
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- MCP server ' ' negotiated unsupported protocol version ' '…
- MCP server ' ': initialize capabilities must be an object
- MCP server ' ' initialize result omitted protocolVersion
- MCP server ' ': initialize serverInfo. must be a string
- MCP server ' ': initialize capabilities must be an object
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/db62f053cf1bb9e0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/mcp/src/stdio_client.rs:440
tools: bool,
resources: bool,
}
fn validate_initialize_result(
server_name: &str,
result: &Value,
) -> Result<Option<ServerCapabilities>> {
let response = result.as_object().with_context(|| {
format!("MCP server '{server_name}': initialize result must be an object")
})?;
let protocol_version = response
.get("protocolVersion")
.and_then(Value::as_str)
.with_context(|| {
format!("MCP server '{server_name}': initialize result omitted protocolVersion")
})?;
if protocol_version != PROTOCOL_VERSION {
bail!(
"MCP server '{server_name}': unsupported protocol version '{protocol_version}' (expected {PROTOCOL_VERSION})"
);
}
let server_info = response
.get("serverInfo")
.and_then(Value::as_object)
.with_context(|| {
format!("MCP server '{server_name}': initialize result omitted serverInfo")
})?;
for field in ["name", "version"] {
if !server_info.get(field).is_some_and(Value::is_string) {
bail!("MCP server '{server_name}': initialize serverInfo.{field} must be a string");
}
}
match response.get("capabilities") {
None => Ok(None),View on GitHub (pinned to 433685b202)