Kuberwastaken/claurst · error
rmcp call_tool ' ' failed
Error message
rmcp call_tool '{}' failed: {} What it means
Raised in `RmcpClientBackend::call_tool` when the rmcp peer's `call_tool(params)` future fails after building a `CallToolRequestParams` for the named tool. This wraps transport failures and JSON-RPC error responses from the server's `tools/call` handler; note it does NOT cover tool-level execution errors reported inside a successful CallToolResult — only protocol-level failure of the call itself.
Solutions
- Call list_tools first and confirm the tool name and its input schema match the arguments being sent.
- Validate arguments against the tool's JSON schema before calling (required fields, types).
- Read the inner error: 'unknown tool' means the name is wrong or the server changed; 'timeout' means the tool ran too long.
- Reconnect and retry if the session or transport was closed.
Example fix
// before: calling with ad-hoc arguments
backend.call_tool("query_db", Some(serde_json::json!({"sql": q}))).await?;
// after: check the tool exists and arguments validate first
let tools = backend.list_tools().await?;
let tool = tools.iter().find(|t| t.name == "query_db")
.ok_or_else(|| anyhow::anyhow!("tool query_db not available"))?;
validate_against_schema(&tool.input_schema, &args)?;
backend.call_tool("query_db", Some(args)).await?; Defensive patterns
Strategy: validation
Validate before calling
fn validate_tool_args(tools: &[McpTool], name: &str, args: &serde_json::Value) -> anyhow::Result<()> {
let tool = tools.iter().find(|t| t.name == name)
.ok_or_else(|| anyhow::anyhow!("tool '{name}' not found"))?;
if let Some(obj) = args.as_object() {
for req in &tool.required_args {
anyhow::ensure!(obj.contains_key(req), "missing required argument '{req}' for tool '{name}'");
}
}
Ok(())
} Try / catch
match backend.call_tool(name, args).await {
Ok(result) => result,
Err(e) if e.to_string().contains("unknown tool") => {
eprintln!("tool '{name}' not offered by this server; refresh tool list");
default_result()
}
Err(e) => return Err(e),
} Prevention
- Refresh the tool list after server updates and never hardcode tool names in long-lived code.
- Validate arguments against the tool's input JSON schema before calling.
- Set a call timeout appropriate for the tool's expected duration.
- Distinguish tool execution errors (inside a successful result) from call failures at the protocol level.
When it happens
Trigger: Calling `call_tool(name, arguments)` where the request never completes successfully: unknown tool name (server returns 'unknown tool' JSON-RPC error), arguments fail server-side validation, session closed mid-call, transport error while sending the POST, or a timeout waiting for a long-running tool.
Common situations: Typo or stale tool name after the server updated its tool list; passing arguments whose types/required keys don't match the tool's input schema; tool execution exceeds the request timeout; server crashed while executing the tool.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- rmcp list_tools failed
- rmcp get_prompt ' ' failed
- rmcp list_resources failed
- rmcp read_resource ' ' failed
- rmcp list_prompts failed
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/5c2df91bef278d0a.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/rmcp_backend.rs:525
.await
.map_err(|e| anyhow::anyhow!("rmcp list_tools failed: {}", e))?;
Ok(tools.into_iter().map(convert_tool).collect())
}
async fn call_tool(
&self,
name: &str,
arguments: Option<Value>,
) -> anyhow::Result<CallToolResult> {
let mut params = rmcp_model::CallToolRequestParams::new(name.to_string());
if let Some(arguments) = arguments {
params = params.with_arguments(json_value_to_object(arguments)?);
}
let result = self
.peer
.call_tool(params)
.await
.map_err(|e| anyhow::anyhow!("rmcp call_tool '{}' failed: {}", name, e))?;
Ok(convert_call_tool_result(result))
}
async fn list_resources(&self) -> anyhow::Result<Vec<McpResource>> {
let resources = self
.peer
.list_all_resources()
.await
.map_err(|e| anyhow::anyhow!("rmcp list_resources failed: {}", e))?;
Ok(resources.into_iter().map(convert_resource).collect())
}
async fn read_resource(&self, uri: &str) -> anyhow::Result<ResourceContents> {
let result = self
.peer
.read_resource(rmcp_model::ReadResourceRequestParams::new(uri.to_string()))
.await
.map_err(|e| anyhow::anyhow!("rmcp read_resource '{}' failed: {}", uri, e))?;View on GitHub (pinned to b0637c97ec)