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

  1. Upgrade (or downgrade) the MCP server binary so its protocol version matches the client's expected version.
  2. Check which version the client expects and, if a newer server is intentionally ahead, update this library to the release supporting that protocol version.
  3. Print the server's reported protocolVersion (`initialize` response) to confirm the exact mismatch before changing binaries.
  4. 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

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


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)