{"record":{"id":"5cd03f4d9b262d68","repo":"Hmbown/CodeWhale","slug":"mcp-server-server-name-initialize-capabilities-must-be-an","errorCode":null,"errorMessage":"MCP server '{server_name}': initialize capabilities must be an object","messagePattern":"MCP server '(.+?)': initialize capabilities must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/stdio_client.rs","lineNumber":464,"sourceCode":"    let server_info = response\n        .get(\"serverInfo\")\n        .and_then(Value::as_object)\n        .with_context(|| {\n            format!(\"MCP server '{server_name}': initialize result omitted serverInfo\")\n        })?;\n    for field in [\"name\", \"version\"] {\n        if !server_info.get(field).is_some_and(Value::is_string) {\n            bail!(\"MCP server '{server_name}': initialize serverInfo.{field} must be a string\");\n        }\n    }\n\n    match response.get(\"capabilities\") {\n        None => Ok(None),\n        Some(Value::Object(capabilities)) => Ok(Some(ServerCapabilities {\n            tools: capabilities.contains_key(\"tools\"),\n            resources: capabilities.contains_key(\"resources\"),\n        })),\n        Some(_) => bail!(\"MCP server '{server_name}': initialize capabilities must be an object\"),\n    }\n}\n\n/// A live connection to one MCP server subprocess.\npub struct ChildProcessMcpClient {\n    server_name: String,\n    capabilities: Option<ServerCapabilities>,\n    connection: Mutex<Connection>,\n    request_timeout: Duration,\n}\n\nimpl std::fmt::Debug for ChildProcessMcpClient {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ChildProcessMcpClient\")\n            .field(\"server_name\", &self.server_name)\n            .finish_non_exhaustive()\n    }\n}","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/stdio_client.rs#L446-L482","documentation":"After validating serverInfo, validate_initialize_result checks the `capabilities` field of the MCP initialize response. If capabilities is present but is not a JSON object (e.g. a string, array, or null-shaped value), this bail fires. Capability presence (tools/resources keys) is how the client decides what the server supports, so it must be an object.","triggerScenarios":"spawn_with_timeouts completes a handshake where the initialize result contains `capabilities` with a non-object JSON type (string, number, array, boolean).","commonSituations":"Servers that serialize capabilities incorrectly; hand-rolled servers returning `\"capabilities\": \"none\"`; buggy middleware mangling the response.","solutions":["Fix the server to emit `capabilities` as a JSON object (possibly empty `{}`) in the initialize result.","If the server genuinely has no capabilities, have it omit the field entirely (the client treats None as no capabilities).","Inspect the child's raw initialize response to confirm the wire shape.","Update the server to a spec-conformant version."],"exampleFix":"// before\n{\"result\":{\"serverInfo\":{...},\"capabilities\":\"tools\"}}\n// after\n{\"result\":{\"serverInfo\":{...},\"capabilities\":{\"tools\":{}}}}","handlingStrategy":"validation","validationCode":"fn capabilities_ok(init: &serde_json::Value) -> bool {\n    init.get(\"result\").and_then(|r| r.get(\"capabilities\"))\n        .map_or(true, |c| c.is_null() || c.is_object())\n}","typeGuard":"fn as_capabilities(v: &Value) -> Option<&serde_json::Map<String, Value>> {\n    v.get(\"result\")?.get(\"capabilities\")?.as_object()\n}","tryCatchPattern":"match spawn_with_timeouts(&config, handshake, request_timeout) {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"capabilities must be an object\") => {\n        eprintln!(\"server returned non-object capabilities; fix server output\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Assert the initialize response shape in the server's own tests.","Use a JSON-RPC/MCP schema validator on server output during development.","Never emit capabilities as a string or array; use an object or omit it."],"tags":["mcp","json-schema","handshake"],"backgroundTag":"unexpected-response-shape","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}