{"record":{"id":"f7661e9938f6bea8","repo":"Hmbown/CodeWhale","slug":"child-returned-a-malformed-mcp-calltoolresult","errorCode":null,"errorMessage":"child returned a malformed MCP CallToolResult","messagePattern":"child returned a malformed MCP CallToolResult","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mcp/src/lib.rs","lineNumber":1175,"sourceCode":"        \"name\": qualified_name.clone(),\n        \"inputSchema\": input_schema,\n        // Retain the pre-0.9.11 management fields for compatibility.\n        \"server_name\": server_name,\n        \"tool_name\": tool_name,\n        \"qualified_name\": qualified_name,\n    });\n    if let Some(description) = description {\n        value[\"description\"] = Value::String(description);\n    }\n    value\n}\n\nfn stdio_tool_call_result(result: Value) -> Result<Value> {\n    let legacy_result = result.clone();\n    match result {\n        Value::Object(mut fields) if looks_like_call_tool_result(&fields) => {\n            if !valid_call_tool_result(&fields) {\n                bail!(\"child returned a malformed MCP CallToolResult\");\n            }\n            // The child already returned a standard MCP CallToolResult. Expose\n            // it directly, while retaining the old nested result for clients\n            // that used the proxy before its MCP envelope was corrected.\n            fields.insert(\"result\".to_string(), legacy_result);\n            Ok(Value::Object(fields))\n        }\n        value => Ok(json!({\n            \"content\": [{\"type\": \"text\", \"text\": legacy_value_text(&value)}],\n            \"result\": legacy_result\n        })),\n    }\n}\n\nfn stdio_resource_descriptor((resource, metadata): (McpResourceDescriptor, Value)) -> Value {\n    let McpResourceDescriptor {\n        server_name,\n        uri,","sourceCodeStart":1157,"sourceCodeEnd":1193,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/mcp/src/lib.rs#L1157-L1193","documentation":"stdio_tool_call_result normalizes the child's response into a standard MCP CallToolResult. If the child claims a CallToolResult shape (looks_like_call_tool_result) but its contents fail valid_call_tool_result validation, the proxy refuses to pass it through. This keeps malformed results from propagating to callers that expect the MCP envelope contract.","triggerScenarios":"Calling a tool over the stdio proxy where the child's JSON response object resembles a CallToolResult (has the recognizable fields) but contains invalid values per valid_call_tool_result (e.g., wrong-typed content entries or missing required fields).","commonSituations":"MCP server implementation bug emitting non-conformant content arrays; version drift where the child uses an older/newer CallToolResult shape; hand-rolled servers that emit the envelope keys with wrong types.","solutions":["Fix or update the MCP server so its CallToolResult conforms to the MCP spec (valid content array and required fields).","Pin/downgrade or upgrade the server version to one matching the proxy's expected envelope shape.","Capture the raw child response (the legacy nested 'result' path) and validate it manually to find the offending field before filing/fixing the server bug."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"fn result_is_conformant(v: &serde_json::Value) -> bool {\n    v.get(\"content\").and_then(|c| c.as_array()).map(|arr|\n        arr.iter().all(|item| item.get(\"type\").and_then(|t| t.as_str()).is_some()))\n        .unwrap_or(false)\n}","typeGuard":"fn is_valid_call_tool_result(fields: &serde_json::Map<String, serde_json::Value>) -> bool {\n    valid_call_tool_result(fields)\n}\n// guard the child response before trusting it","tryCatchPattern":"match manager.call_tool(server, tool, args) {\n    Err(e) if e.to_string().contains(\"malformed MCP CallToolResult\") => {\n        tracing::error!(\"server {server} returned non-conformant result: {e}\");\n        // capture raw response for a bug report; do not retry blindly\n    }\n    other => other,\n}","preventionTips":["Pin the MCP server to a spec-conformant version.","Add an integration check that calls each tool once and validates the envelope.","Never hand-roll CallToolResult serialization server-side; use the official SDK."],"tags":["mcp","stdio","response-validation"],"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"}