JuliusBrussee/caveman · error · Error
unknown tool
Error message
unknown tool: ${params?.name} What it means
The caveman-delegate MCP server's handle() supports exactly one tool (TOOL, 'cave_delegate'); a tools/call request naming any other tool throws `unknown tool: ${params?.name}`. This is standard MCP server behavior: unknown tool invocations are rejected rather than silently ignored.
Solutions
- Call the tool by its exact registered name (the name field of the single entry in tools/list)
- Refresh the client's tool list via tools/list and retry
- Point the client at the correct MCP server for the tool you intended
- Fix typos/casing in the tool name in your client config or test harness
Example fix
// before
{ method: 'tools/call', params: { name: 'delegate', arguments: { task } } }
// after
{ method: 'tools/call', params: { name: 'cave_delegate', arguments: { task } } } Defensive patterns
Strategy: try-catch
Validate before calling
const tools = await client.request({ method: 'tools/list' });
const names = tools.tools.map(t => t.name);
if (!names.includes(toolName)) throw new Error(`tool ${toolName} not offered by this server; available: ${names}`); Try / catch
try { await callTool('cave_delegate', { task }); } catch (e) {
if (/^unknown tool:/.test(e.message)) {
await refreshToolList(); // stale cache — re-fetch tools/list and retry with exact name
} else throw e;
} Prevention
- Copy the tool name verbatim from tools/list instead of retyping it
- Refresh the client tool cache when connecting or after server updates
- Verify which MCP server you are connected to before dispatching a call
- Use constants for tool names in client code
When it happens
Trigger: An MCP client issues tools/call with params.name different from TOOL.name (typo, stale cached tool list from an older server version, or another server's tool invoked against this one).
Common situations: Client connected to the wrong MCP server, client-side tool cache not refreshed after tools/list changed, a hand-rolled JSON-RPC request with a misspelled tool name, or case-sensitivity mismatch in the tool name.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- MCP changes require the ownership transaction
- : MCP ownership journal failed; native config may already…
- caveman-cloud MCP changed after setup; refusing destructive…
- caveman-cloud MCP postflight mismatch
- caveman-cloud MCP registration failed exact postflight
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5c1db8313253995d.
Report an issue: GitHub.
Appendix: source
Thrown at agents/delegate/caveman-delegate-mcp.mjs:140
cwd: { type: "string", description: "Working directory (default: current)" },
},
required: ["task"],
},
};
async function handle(msg) {
const { id, method, params } = msg;
if (method === "initialize") {
return {
protocolVersion: params?.protocolVersion || PROTOCOL_FALLBACK,
capabilities: { tools: { listChanged: false } },
serverInfo: { name: "caveman-delegate", version: "0.1.0" },
};
}
if (method === "tools/list") return { tools: [TOOL] };
if (method === "ping") return {};
if (method === "tools/call") {
if (params?.name !== TOOL.name) throw new Error(`unknown tool: ${params?.name}`);
if (process.env.CAVEMAN_DELEGATE_CHILD === "1") throw new Error("cave_delegate_depth_exceeded");
const task = params?.arguments?.task;
if (!task || typeof task !== "string") throw new Error("cave_delegate_missing_task");
const r = await runWorker(task, params?.arguments?.cwd);
const lines = [r.out || "(worker produced no output)"];
if (r.usage) {
const u = r.usage;
let usageLine = `delegate usage (measured, provider-reported): input ${u.input + u.cacheRead + u.cacheWrite} (cacheRead ${u.cacheRead}, cacheWrite ${u.cacheWrite}), output ${u.output}`;
if (u.cost > 0) usageLine += `, cost $${u.cost.toFixed(4)}`;
lines.push("---", usageLine);
}
if (r.code !== 0) {
lines.push("---", `worker exit ${r.code}: ${r.err.slice(-500)}`);
return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
}
return { content: [{ type: "text", text: lines.join("\n") }] };
}
if (method?.startsWith("notifications/")) return undefined;View on GitHub (pinned to 3ee70a1026)