JuliusBrussee/caveman · error

unknown tool: ${params?.name}

Error message

unknown tool: ${params?.name}

What it means

The caveman-delegate MCP server (a stdio JSON-RPC handler) throws when a tools/call request names anything other than its single advertised tool. It is strict per the MCP contract: tools/list returns exactly one tool (TOOL), and calls with other names are protocol errors, not silent no-ops.

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 27d5a3981a)

Solutions

  1. Call tools/list first and use the exact name it returns
  2. Update your client to the current delegate tool name exposed by the server
  3. If wrapping the server, assert the name string in one place rather than retyping it

Example fix

// before
{ "method": "tools/call", "params": { "name": "delegate", "arguments": { ... } } }

// after — use the exact name from tools/list
const [{ name }] = (await rpc("tools/list")).tools;
await rpc("tools/call", { name, arguments: { task } });
Defensive patterns

Strategy: validation

Validate before calling

const tools = (await rpc("tools/list")).tools;
const names = new Set(tools.map((t) => t.name));
if (!names.has(requestedName)) {
  throw new Error(`unknown tool ${requestedName}; available: ${[...names].join(", ")}`);
}

Type guard

function isKnownTool(name, advertised) {
  return advertised.some((t) => t.name === name);
}

Try / catch

try { await rpc("tools/call", { name, arguments }); }
catch (e) {
  if (/^unknown tool: /.test(String(e?.message))) { /* re-list tools, correct the name, retry once */ }
  else throw e;
}

Prevention

When it happens

Trigger: A client sending tools/call with a misspelled or outdated tool name (for example after a rename), or a client that never called tools/list and guessed the name.

Common situations: Version skew between client hard-coded tool names and the server's current name; typos in hand-rolled MCP clients; copy-paste from a different MCP server's config.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/5c1db8313253995d. Report an issue: GitHub.