ultraworkers/claw-code · error · std::io::Error
MCP response for {method} used unsupported jsonrpc version `
Error message
MCP response for {method} used unsupported jsonrpc version `{}` What it means
`McpStdioProcess::request` (runtime/src/mcp_stdio.rs:1304) validates every JSON-RPC response from the spawned MCP server: `response.jsonrpc` must equal "2.0" exactly. claw always sends "2.0" on requests; a server replying with "1.0", "2", or any other string gets `InvalidData`. Note `jsonrpc` is a required String field on JsonRpcResponse — a response OMITTING it fails serde deserialization earlier, so this error specifically means a wrong explicit value.
Source
Thrown at rust/crates/runtime/src/mcp_stdio.rs:1304
}
pub async fn read_response<T: DeserializeOwned>(&mut self) -> io::Result<JsonRpcResponse<T>> {
self.read_jsonrpc_message().await
}
pub async fn request<TParams: Serialize, TResult: DeserializeOwned>(
&mut self,
id: JsonRpcId,
method: impl Into<String>,
params: Option<TParams>,
) -> io::Result<JsonRpcResponse<TResult>> {
let method = method.into();
let request = JsonRpcRequest::new(id.clone(), method.clone(), params);
self.send_request(&request).await?;
let response = self.read_response().await?;
if response.jsonrpc != "2.0" {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"MCP response for {method} used unsupported jsonrpc version `{}`",
response.jsonrpc
),
));
}
if response.id != id {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"MCP response for {method} used mismatched id: expected {id:?}, got {:?}",
response.id
),
));
}
View on GitHub (pinned to 08106b0c37)
Solutions
- In the server, always serialize responses with "jsonrpc":"2.0" (use the SDK's response type, not hand-built JSON).
- Check the exact value in the error message against the server's serialization code.
- If using an SDK, upgrade it — old JSON-RPC 1.0 libraries do not emit the field correctly.
Example fix
// before (server)
{"id":1,"result":{}} // no/invalid version -> deserialize fail or this error
// after (server)
{"jsonrpc":"2.0","id":1,"result":{}} Defensive patterns
Strategy: try-catch
Try / catch
match process.request(id, method, params).await {
Err(e) if e.kind() == io::ErrorKind::InvalidData
&& e.to_string().contains("unsupported jsonrpc version") => {
// peer is not spec-compliant: log the version it sent and drop the server
}
other => other,
} Prevention
- Always serialize responses with "jsonrpc":"2.0"; use an SDK response type
- A response with NO jsonrpc field fails deserialization before this check — include it
- Assert the field in your server's response unit tests
When it happens
Trigger: A homegrown MCP server echoing JSON-RPC 1.0 style responses ("jsonrpc":"1.0" or version pulled from config); a server deriving the field from the negotiated protocolVersion of initialize; hardcoded "2" without the patch component.
Common situations: Adapting an old JSON-RPC 1.0 service as an MCP server; typos in hand-serialized responses; forks that renamed the constant.
Related errors
- MCP stdio stream closed while reading headers
- MCP stdio stream closed while reading line
- MCP response for {method} used mismatched id: expected {id:?
- missing Content-Length header
- MCP stdio stream closed while reading headers
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/895e7412a17a98c8.
Report an issue: GitHub.