Files
ruvnet__ruflo/v3/@claude-flow/cli/bin/mcp-server.js
Reuven 1884ed1010 fix(3.7.0-alpha.12): batch resolve issue-fix loop #1839-#1847
Closes #1839, #1840, #1841, #1842, #1843, #1844, #1845, #1846, #1847.

#1839 — bin/cli.js, bin/mcp-server.js, config-adapter.ts: rename
  serverInfo.name 'claude-flow' → 'ruflo' so MCP wire identity matches the
  package name. Live probe of `ruflo mcp start` now returns
  `serverInfo.name="ruflo"` (verified post-build).

#1840 — init/executor.ts: detectExistingRufloMCP() now also walks
  parsed.projects[*].mcpServers.ruflo with normalized path matching
  (lowercase + forward-slash) so `claude mcp add ruflo` registrations
  stored project-scoped in ~/.claude.json are detected and init skips
  writing a duplicate .mcp.json.

#1841 — init/mcp-generator.ts: generated .mcp.json now keys
  `mcpServers.ruflo` instead of legacy `mcpServers['claude-flow']`. The
  printed `claude mcp add` instructions match. Prevents the
  duplicate-tool-prefix problem when init runs over an existing ruflo
  registration.

#1842 — commands/doctor.ts: checkMcpServers() now traverses
  ~/.claude.json.projects[*].mcpServers in addition to top-level
  mcpServers, so doctor stops warning "No MCP config found" when a
  working project-scoped ruflo registration exists.

#1843 — mcp-tools/system-tools.ts: exclude `unknown` from health
  denominator (was dragging the score down even when actionable checks
  were green) and surface as `advisory` count. Add YAML config and
  sql.js/HNSW/.swarm/ruvector memory paths to detection. Local probe:
  4 healthy / 5 actionable = 80% (was 4/8 = 50%) with 3 advisory.

#1844 — services/worker-daemon.ts: WorkerDaemon now reads
  .claude-flow/config.yaml (and .yml) when config.json is absent, with
  lazy `yaml` import so missing dep falls back to a clear warning.
  Adds yaml@^2.8.0 to runtime deps. Logs the chosen source.

#1845 — services/worker-daemon.ts + mcp-tools/hooks-tools.ts: durable
  queue boundary between MCP dispatch and daemon. mcp__hooks_worker-
  dispatch now writes .claude-flow/daemon-queue/<id>.json (the
  process-local Map was invisible to the daemon). The daemon polls this
  directory every 5s, executes triggerWorker(), and moves processed
  entries to .processed/. Adds `mcp-only` status for queue-write
  failures so we never claim `queued` without proof on disk.

#1846 — mcp-tools/memory-tools.ts + commands/memory.ts: feature-detect
  smartSearch on the resolved @claude-flow/memory build. When absent
  (older published memory packages predate the export), fall through to
  plain semantic search with an explicit warning + `smartFallback`
  field on the response, instead of throwing
  `smartSearch is not a function`.

#1847 — mcp-tools/hooks-tools.ts: hooks_pretrain now emits an explicit
  note when a non-empty corpus produces zero patterns, naming the
  supported extraction extensions (.ts/.js/.py/.go/.rs/.java) and
  clarifying that pretrain is a separate axis from live trajectories
  and statusline state. Local probe on a Markdown vault returns the
  new note correctly.

src/index.ts: pre-existing TS strict-null error in showCommandHelp()
  that was preventing `npm run build` (introduced by #1791.4 / 93b97548e)
  fixed by extracting a non-null `current: Command` local instead of
  reassigning the optional `command`. Unblocks publish + restores the
  integration-docker `npm run build succeeds` test.

Verification:
- 1963 tests pass / 0 fail (was 1962 / 1 due to the index.ts TS error)
- Live MCP probe: serverInfo.name="ruflo" ✅
- Live mcp-generator: emits mcpServers.ruflo ✅
- Live system_health: 80% (4/5 healthy, 3 advisory) ✅
- Live hooks_pretrain on Markdown vault: emits explicit note ✅

Published @claude-flow/cli@3.7.0-alpha.12, claude-flow@3.7.0-alpha.12,
ruflo@3.7.0-alpha.12 with all dist-tags (alpha, latest, v3alpha for
cli/claude-flow; alpha, latest for ruflo).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-05-08 09:36:58 -04:00

225 lines
6.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* @claude-flow/cli - MCP Server Entry Point
*
* Direct stdio MCP server for Claude Code integration.
* This entry point handles stdin/stdout directly for MCP protocol
* without any CLI formatting output that would corrupt the protocol.
*/
import { randomUUID } from 'crypto';
// Suppress the SPECIFIC cosmetic "[AgentDB Patch] Controller index not found"
// noise. Tight match (both prefix AND "Controller index not found") so other
// [AgentDB Patch] warnings about real issues still flow through. Also patch
// console.log because the underlying call site uses it. See bin/cli.js for
// the same rationale.
const _origWarn = console.warn;
const _origLog = console.log;
const _isCosmeticAgentdbPatchNoise = (msg) =>
msg.includes('[AgentDB Patch]') && msg.includes('Controller index not found');
console.warn = (...args) => {
if (_isCosmeticAgentdbPatchNoise(String(args[0] ?? ''))) return;
_origWarn.apply(console, args);
};
console.log = (...args) => {
if (_isCosmeticAgentdbPatchNoise(String(args[0] ?? ''))) return;
_origLog.apply(console, args);
};
import { listMCPTools, callMCPTool, hasTool } from '../dist/src/mcp-client.js';
const VERSION = '3.0.0';
const sessionId = `mcp-${Date.now()}-${randomUUID().slice(0, 8)}`;
// Log to stderr (doesn't corrupt stdout for MCP protocol)
console.error(
`[${new Date().toISOString()}] INFO [claude-flow-mcp] (${sessionId}) Starting in stdio mode`
);
console.error(JSON.stringify({
arch: process.arch,
mode: 'mcp-stdio',
nodeVersion: process.version,
pid: process.pid,
platform: process.platform,
protocol: 'stdio',
sessionId,
version: VERSION,
}));
// Handle stdin messages
// Audit-flagged DoS protection (audit_1776483149979): cap stdin buffer
// to 10MB. See bin/cli.js for the same protection on the auto-detect path.
const MCP_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
let buffer = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', async (chunk) => {
buffer += chunk;
if (buffer.length > MCP_MAX_BUFFER_BYTES) {
console.log(JSON.stringify({
jsonrpc: '2.0',
id: null,
error: {
code: -32700,
message: `Buffered stdin exceeds ${MCP_MAX_BUFFER_BYTES} bytes without newline; resetting`,
},
}));
buffer = '';
return;
}
// Process complete JSON messages (newline-delimited)
let lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep incomplete line in buffer
for (const line of lines) {
if (line.trim()) {
try {
const message = JSON.parse(line);
const response = await handleMessage(message);
if (response) {
console.log(JSON.stringify(response));
}
} catch (error) {
console.error(
`[${new Date().toISOString()}] ERROR [claude-flow-mcp] Failed to parse:`,
error instanceof Error ? error.message : String(error)
);
// Send parse error response
console.log(JSON.stringify({
jsonrpc: '2.0',
id: null,
error: { code: -32700, message: 'Parse error' },
}));
}
}
}
});
process.stdin.on('end', () => {
console.error(
`[${new Date().toISOString()}] INFO [claude-flow-mcp] (${sessionId}) stdin closed, shutting down...`
);
process.exit(0);
});
// Handle process termination
process.on('SIGINT', () => {
console.error(`[${new Date().toISOString()}] INFO [claude-flow-mcp] Received SIGINT`);
process.exit(0);
});
process.on('SIGTERM', () => {
console.error(`[${new Date().toISOString()}] INFO [claude-flow-mcp] Received SIGTERM`);
process.exit(0);
});
/**
* Handle MCP message
*/
async function handleMessage(message) {
if (!message.method) {
return {
jsonrpc: '2.0',
id: message.id,
error: { code: -32600, message: 'Invalid Request: missing method' },
};
}
const params = message.params || {};
try {
switch (message.method) {
case 'initialize':
return {
jsonrpc: '2.0',
id: message.id,
result: {
protocolVersion: '2024-11-05',
serverInfo: { name: 'ruflo', version: VERSION },
capabilities: {
tools: { listChanged: true },
resources: { subscribe: true, listChanged: true },
},
},
};
case 'tools/list': {
const tools = listMCPTools();
return {
jsonrpc: '2.0',
id: message.id,
result: {
tools: tools.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
},
};
}
case 'tools/call': {
const toolName = params.name;
const toolParams = params.arguments || {};
if (!hasTool(toolName)) {
return {
jsonrpc: '2.0',
id: message.id,
error: { code: -32601, message: `Tool not found: ${toolName}` },
};
}
try {
const result = await callMCPTool(toolName, toolParams, { sessionId });
return {
jsonrpc: '2.0',
id: message.id,
result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] },
};
} catch (error) {
return {
jsonrpc: '2.0',
id: message.id,
error: {
code: -32603,
message: error instanceof Error ? error.message : 'Tool execution failed',
},
};
}
}
case 'notifications/initialized':
console.error(`[${new Date().toISOString()}] INFO [claude-flow-mcp] Client initialized`);
return null; // No response for notifications
case 'ping':
return {
jsonrpc: '2.0',
id: message.id,
result: {},
};
default:
return {
jsonrpc: '2.0',
id: message.id,
error: { code: -32601, message: `Method not found: ${message.method}` },
};
}
} catch (error) {
console.error(`[${new Date().toISOString()}] ERROR [claude-flow-mcp] ${message.method}:`, error);
return {
jsonrpc: '2.0',
id: message.id,
error: {
code: -32603,
message: error instanceof Error ? error.message : 'Internal error',
},
};
}
}