ruvnet/ruflo · error · MCPClientError

MCP tool not found: ${toolName}

Error message

MCP tool not found: ${toolName}

What it means

Thrown by callMCPTool() when TOOL_REGISTRY.get(toolName) returns undefined — i.e. no MCP tool has been registered under that exact name. The registry is populated at module load by the MCP tool definitions (agentdb, agentbbs, agenticow, etc.), so an unknown name means either a typo, a tool that was filtered out by the `mcp start --tools` selection, or a tool whose owning package/feature flag is disabled. The error is wrapped as MCPClientError so callers can read `.toolName`.

Source

Thrown at v3/@claude-flow/cli/src/mcp-client.ts:245

 * });
 *
 * // Initialize swarm
 * const swarm = await callMCPTool('swarm_init', {
 *   topology: 'hierarchical-mesh',
 *   maxAgents: 15
 * });
 * ```
 */
export async function callMCPTool<T = unknown>(
  toolName: string,
  input: Record<string, unknown> = {},
  context?: Record<string, unknown>
): Promise<T> {
  // Look up tool in registry
  const tool = TOOL_REGISTRY.get(toolName);

  if (!tool) {
    throw new MCPClientError(
      `MCP tool not found: ${toolName}`,
      toolName
    );
  }

  try {
    // ADR-324: one policy chokepoint for every local CLI/MCP invocation.
    // Policy administration is not exempt: authorization calls the engine
    // directly, so there is no recursive MCP dispatch. In enforce mode an
    // administrator must explicitly allow policy.* actions or use the local
    // CLI bootstrap path.
    const decision = await authorizeMcpTool(toolName, input, context, classifyMcpTool(toolName));
    if (decision.enforcedOutcome !== 'allowed') {
      throw new Error(`policy-${decision.enforcedOutcome}:${decision.reason}; receipt=${decision.receiptId}`);
    }
    // Call the tool handler
    const result = await tool.handler(input, context);
    // ADR-146 P2: scan every tool result for indirect-injection before it

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. List the actually-registered tools: `TOOL_REGISTRY` keys, or `npx @claude-flow/cli mcp tools` / the `mcp list-tools` command, and copy the exact name.
  2. Check that the tool's owning module is imported — for tools behind optional deps verify the package is installed and the tool module was loaded.
  3. Inspect CLAUDE_FLOW_MCP_TOOLS and the `--tools` flag passed to `mcp start`; widen the selection or remove the env var.
  4. Align the integrator's version of the CLI with the docs (pin `@claude-flow/cli` to the same minor).

Example fix

// before — typo
callMCPTool('agent_spaawn', { agentType: 'coder' });
// after
callMCPTool('agent_spawn', { agentType: 'coder' });
Defensive patterns

Strategy: validation

Validate before calling

import { TOOL_REGISTRY } from '@claude-flow/cli/mcp-client';
function isRegisteredTool(name: string): boolean {
  return TOOL_REGISTRY.has(name);
}
if (!isRegisteredTool(toolName)) {
  throw new Error(`unknown tool '${toolName}'; registered: ${[...TOOL_REGISTRY.keys()].join(', ')}`);
}

Type guard

const isMcpToolName = (s: string, registry: ReadonlyMap<string, unknown>): s is string =>
  typeof s === 'string' && registry.has(s);

Try / catch

try {
  return await callMCPTool(toolName, input, ctx);
} catch (e) {
  if (e?.name === 'MCPClientError' && /^MCP tool not found:/.test(e.message)) {
    // surface the list of registered tools for the caller to pick from
    return { error: 'unknown-tool', available: [...TOOL_REGISTRY.keys()] };
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `callMCPTool('agent_spaawn', ...)` (typo); calling a tool name that exists only when a feature flag or CLAUDE_FLOW_MCP_TOOLS env entry is set; calling before the tool registry module has been imported in the current process; calling a tool removed in the current minor version.

Common situations: Typo in the toolName string; using a tool gated behind an optional dependency that was filtered out; version skew between the docs an integrator copied and the installed CLI; calling a tool whose registration import was tree-shaken by a bundler.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/8c9209df0ec7dd65. Report an issue: GitHub.