zeroclaw-labs/zeroclaw · error · anyhow::Error
MCP tool `{tool_name}` error {}: {}
Error message
MCP tool `{tool_name}` error {}: {} What it means
call_tool received a JSON-RPC response whose top-level error object is set — a protocol-level failure (unknown tool, invalid params, server internal error), as opposed to isError:true, which signals tool-execution failure. The message embeds the JSON-RPC error code and the server's message text.
Source
Thrown at crates/zeroclaw-tools/src/mcp_client.rs:745
let inner = self.inner.lock().await;
inner
.config
.tool_timeout_secs
.unwrap_or(DEFAULT_TOOL_TIMEOUT_SECS)
.min(MAX_TOOL_TIMEOUT_SECS)
};
let operation = format!("tool call `{tool_name}`");
let resp = self
.dispatch_rpc(
"tools/call",
json!({ "name": tool_name, "arguments": arguments }),
tool_timeout,
&operation,
)
.await?;
if let Some(err) = resp.error {
bail!("MCP tool `{tool_name}` error {}: {}", err.code, err.message);
}
let result = resp.result.unwrap_or(serde_json::Value::Null);
// MCP servers signal *tool-execution* failures (as opposed to JSON-RPC
// protocol errors) with HTTP 200 + `result.isError: true` and the detail
// in `result.content[].text`, per the MCP spec. Surface it (scrubbed and
// length-bounded) so the failure is visible to the model and the log.
let server_name = self.inner.lock().await.config.name.clone();
check_result_is_error(&result, tool_name, &server_name)?;
Ok(result)
}
/// Generic JSON-RPC method dispatch with the same timeout, bounded
/// reconnect, and error surfacing as `call_tool`. Returns the raw
/// `result` value; callers apply any method-specific envelope handling.
pub(crate) async fn dispatch_method(View on GitHub (pinned to 88bb9c8533)
Solutions
- Map the embedded code: -32601 unknown tool, -32602 invalid params, -32603 internal error — each points at a different fix
- Re-fetch tools/list and call the exact advertised name
- Validate arguments against the tool's inputSchema before the call
- For -32603, inspect the server's own logs; the client surface just relays the server's message
Example fix
// before
let res = server.call_tool("fs_read", json!({"path": p})).await?;
// after: use the advertised name and schema
let tools = list_tools(&server).await?;
let t = tools.iter().find(|t| t.name.contains("read")).context("no read tool")?;
validate_args(&t.input_schema, &args)?;
let res = server.call_tool(&t.name, args).await?; Defensive patterns
Strategy: try-catch
Validate before calling
// Resolve the tool name from the live registry, then validate args
let tools = list_tools(&server).await?;
let tool = tools.iter().find(|t| t.name == wanted).context("tool not advertised")?;
validate_against_schema(&args, &tool.input_schema)?;
server.call_tool(&tool.name, args).await?; Type guard
fn is_jsonrpc_protocol_error(err: &anyhow::Error) -> bool {
let s = err.to_string();
s.contains("error -3") // -32601 unknown tool, -32602 invalid params, -32603 internal
} Try / catch
Catch, extract the code from the message, and branch: -32601 -> refresh tool list and use the advertised name; -32602 -> fix arguments against the schema; -32603 -> inspect server logs, retry only if the server message indicates a transient fault.
Prevention
- Never hardcode tool names; resolve them from tools/list at runtime
- Re-validate cached tool schemas after server upgrades
- Log the full code+message pair so -3260x triage is mechanical
When it happens
Trigger: Calling a tool name the server never advertised (-32601 method not found); arguments failing JSON-RPC param validation (-32602); server-internal errors returned as -32603; params that cannot be parsed against the expected shape.
Common situations: Tool renamed in a server upgrade while the caller still uses the old name; stale cached tool lists; argument type typos (string vs number) that the tool's inputSchema would have caught.
Related errors
- MCP `{op}` (server `{server_name}`) returned isError: {detai
- MCP `{rpc_method}` error {}: {}
- MCP server `{server_name}` rejected initialize: {:?}
- MCP server `{server_name}` timed out after {timeout_secs}s d
- MCP server `{server_name}` recovery task failed before writi
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/e980554c912ce3e5.
Report an issue: GitHub.