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

  1. Call list_tools first and confirm the tool name and its input schema match the arguments being sent.
  2. Validate arguments against the tool's JSON schema before calling (required fields, types).
  3. Read the inner error: 'unknown tool' means the name is wrong or the server changed; 'timeout' means the tool ran too long.
  4. 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

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


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)