ruvnet/ruflo · error · Error
Failed to register MCP tools: ${registration.failed.join(',
Error message
Failed to register MCP tools: ${registration.failed.join(', ')} What it means
When the server wires up its tools it calls registerTools(cliTools), which returns a per-tool registration result; if any tool fails to register (name collision, invalid definition), start aborts with the list of failed tool names. This is a fail-fast at boot: the server refuses to come up in a degraded state where some advertised tools are missing.
Source
Thrown at v3/@claude-flow/cli/src/mcp-server.ts:767
const dualLoopback = this.options.host === 'localhost';
const mcpServer = createMCPServer(
{
name: 'Claude-Flow MCP Server V3',
version: '3.0.0',
transport: this.options.transport as 'http' | 'websocket',
host: dualLoopback ? '127.0.0.1' : this.options.host,
additionalHosts: dualLoopback && this.options.transport === 'http' ? ['::1'] : undefined,
port: this.options.port,
enableMetrics: true,
enableCaching: true,
},
logger
);
const registration = mcpServer.registerTools(
cliTools as Parameters<typeof mcpServer.registerTools>[0]
);
if (registration.failed.length > 0) {
throw new Error(`Failed to register MCP tools: ${registration.failed.join(', ')}`);
}
await mcpServer.start();
this.mcpServers = [mcpServer];
}
/**
* Wait for server to be ready
*/
private async waitForReady(timeout = 10000): Promise<void> {
// For stdio transport, we're ready immediately (in-process)
if (this.options.transport === 'stdio') {
return;
}
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const health = await this.checkHealth();View on GitHub (pinned to fa13ee4ad6)
Solutions
- Read the comma-separated tool names in the message — those are exactly the definitions that failed
- Find and rename the colliding definition (namespace custom tools, e.g. 'acme_search' instead of 'search') or disable the conflicting plugin
- Validate the tool definition (unique name, well-formed inputSchema) before handing it to the server
- Update all plugins and the CLI together so registration APIs match
Example fix
// before — custom tool collides with a built-in name
const myTools = [{ name: 'memory_search', /* ... */ }];
server.registerTools(myTools); // failed: memory_search
// after — namespace custom tools
const myTools = [{ name: 'acme_memory_search', /* ... */ }]; Defensive patterns
Strategy: try-catch
Validate before calling
// Validate names are unique before handing tools to the server
const seen = new Set<string>();
for (const t of myTools) {
if (seen.has(t.name)) throw new Error(`duplicate tool name: ${t.name}`);
seen.add(t.name);
} Try / catch
try {
await server.start();
} catch (e) {
const m = /Failed to register MCP tools: (.+)/.exec((e as Error).message);
if (m) {
// m[1] lists exactly the colliding/invalid tool names — disable or rename them
throw new Error(`registration failed for: ${m[1]}. Rename/disable those tools.`);
}
throw e;
} Prevention
- Namespace custom tool names (org/product prefix) to avoid built-in collisions
- Assert a unique-name invariant in your tool loader before registration
- Upgrade plugins and the CLI together so registration APIs stay compatible
When it happens
Trigger: Two plugins registering tools with the same name; a tool definition with a malformed schema or duplicate field; a custom tool added to cliTools whose name collides with a built-in; version skew where a plugin was built against a different registration API than the shipped server.
Common situations: Adding an in-house plugin that uses a generic name like 'search' that already exists; upgrading @claude-flow/cli while stale plugin builds remain in the plugins directory; forking built-ins without renaming.
Related errors
- Server failed to start within timeout
- INTERNAL_ERROR
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
- unknown strategy "${name}". Available: ${roster.map((r) => r
- localCompute: no adapter for graphId=${input.graphId}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/951568c0ca76555d.
Report an issue: GitHub.