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
- Fix the MCP server so `initialize` returns `serverInfo: {"name": "<string>", "version": "<string>"} with both fields as strings.
- If you control the server, validate its initialize response against the MCP schema before deployment.
- Check for a proxy or middleware rewriting the response and remove/fix it.
- 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
- Validate your server's initialize response against the MCP JSON schema before release.
- Always emit name and version as strings, never numbers or null.
- Include a handshake conformance test in the server's own test suite.
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
- MCP server ' ': initialize capabilities must be an object
- MCP server ' ' initialize result omitted protocolVersion
- MCP server ' ' negotiated unsupported protocol version ' '…
- MCP server ' ': initialize capabilities must be an object
- MCP server ' ': initialize serverInfo. must be a string
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)