Hmbown/CodeWhale · error

MCP server ' ': initialize serverInfo. must be a string

Error message

MCP server '{}': initialize serverInfo.{} must be a string

What it means

`validate_initialize_result` requires the `initialize` response's `serverInfo` object to contain string-valued `name` and `version` fields; it bails when either is absent or of another JSON type. The client records server identity from these fields, and a malformed identity would make diagnostics and dedup unreliable. This is strict schema validation of the MCP handshake response.

Solutions

  1. Fix the MCP server so `initialize` returns `serverInfo: {"name": "<string>", "version": "<string>"} with both fields as strings.
  2. If you control the server, validate its initialize response against the MCP schema before deployment.
  3. Check for a proxy or middleware rewriting the response and remove/fix it.
  4. File an issue with the server vendor if it is a third-party binary returning non-conformant serverInfo.

Example fix

// before: non-string version in initialize result
{"protocolVersion":"...","serverInfo":{"name":"my-server","version":2}}
// after: both fields are strings
{"protocolVersion":"...","serverInfo":{"name":"my-server","version":"2.0.1"}}
Defensive patterns

Strategy: validation

Validate before calling

fn initialize_result_has_valid_server_info(v: &serde_json::Value) -> bool {
    let si = v.get("serverInfo");
    si.is_some_and(|s| s.is_object())
        && ["name", "version"].iter().all(|f| {
            si.unwrap().get(f).is_some_and(serde_json::Value::is_string)
        })
}

Type guard

fn as_string_field<'a>(v: &'a serde_json::Value, k: &str) -> Option<&'a str> {
    v.get(k).and_then(serde_json::Value::as_str)
}

Try / catch

match spawn_with_timeouts(&config, hs, req).await {
    Err(e) if e.to_string().contains("serverInfo") => {
        eprintln!("server returned malformed serverInfo; fix server initialize response");
    }
    other => other,
}

Prevention

When it happens

Trigger: `spawn_with_timeouts` receives an `initialize` JSON result whose `serverInfo` exists (otherwise a different context error fires) but where `serverInfo.name` or `serverInfo.version` is missing, null, a number, or some other non-string JSON value.

Common situations: A hand-rolled or homebrew MCP server returning `"version": 1` (number) instead of `"1"`; a server omitting `version` entirely; a buggy proxy that mangles the serverInfo object; a server emitting a JSON array instead of an object for serverInfo.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/a2bf95e16d2d0bb6. Report an issue: GitHub.

Appendix: source

Thrown at crates/mcp/src/stdio_client.rs:453

        .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),
        Some(Value::Object(capabilities)) => Ok(Some(ServerCapabilities {
            tools: capabilities.contains_key("tools"),
            resources: capabilities.contains_key("resources"),
        })),
        Some(_) => bail!("MCP server '{server_name}': initialize capabilities must be an object"),
    }
}

/// A live connection to one MCP server subprocess.
pub struct ChildProcessMcpClient {
    server_name: String,
    capabilities: Option<ServerCapabilities>,
    connection: Mutex<Connection>,

View on GitHub (pinned to 433685b202)