mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 22:13:39 +09:00
#1605: Guard browser_* MCP tool registration behind agent-browser availability check. Tools are no longer advertised when the binary is missing. #1604: Align memory-tools.ts MEMORY_DIR with memory-initializer.ts (.swarm/memory.db). Legacy migration now correctly reads from the old .claude-flow/memory/ path. #1606: Wrap ensureInitialized() in try/catch to prevent unhandled exceptions from killing the stdio transport. Fix cli.js outer catch to return -32603 (internal error) instead of -32700 (parse error) for handleMessage failures. #1616: Rename .claude/settings copy.json → settings.json.bak so VSCode Copilot no longer treats it as an active settings file. Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
184 lines
5.2 KiB
JavaScript
Executable File
184 lines
5.2 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* @claude-flow/cli - CLI Entry Point
|
|
*
|
|
* Claude Flow V3 Command Line Interface
|
|
*
|
|
* Auto-detects MCP mode when stdin is piped and no args provided.
|
|
* This allows: echo '{"jsonrpc":"2.0",...}' | npx @claude-flow/cli
|
|
*/
|
|
|
|
import { randomUUID } from 'crypto';
|
|
|
|
// Suppress noisy [AgentDB Patch] warnings from agentic-flow's runtime patch
|
|
// These are cosmetic — the patch tries to fix agentdb v1.x imports but we use v3
|
|
const _origWarn = console.warn;
|
|
console.warn = (...args) => {
|
|
const msg = String(args[0] ?? '');
|
|
if (msg.includes('[AgentDB Patch]')) return;
|
|
_origWarn.apply(console, args);
|
|
};
|
|
|
|
// Check if we should run in MCP server mode
|
|
// Conditions:
|
|
// 1. stdin is being piped AND no CLI arguments provided (auto-detect)
|
|
// 2. stdin is being piped AND args are "mcp start" (explicit, e.g. npx claude-flow@alpha mcp start)
|
|
const cliArgs = process.argv.slice(2);
|
|
const isExplicitMCP = cliArgs.length >= 1 && cliArgs[0] === 'mcp' && (cliArgs.length === 1 || cliArgs[1] === 'start');
|
|
const isMCPMode = !process.stdin.isTTY && (process.argv.length === 2 || isExplicitMCP);
|
|
|
|
if (isMCPMode) {
|
|
// Run MCP server mode
|
|
const { listMCPTools, callMCPTool, hasTool } = await import('../dist/src/mcp-client.js');
|
|
|
|
const VERSION = '3.0.0';
|
|
const sessionId = `mcp-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
|
|
console.error(
|
|
`[${new Date().toISOString()}] INFO [claude-flow-mcp] (${sessionId}) Starting in stdio mode`
|
|
);
|
|
|
|
let buffer = '';
|
|
process.stdin.setEncoding('utf8');
|
|
process.stdin.on('data', async (chunk) => {
|
|
buffer += chunk;
|
|
let lines = buffer.split('\n');
|
|
buffer = lines.pop() || '';
|
|
|
|
for (const line of lines) {
|
|
if (line.trim()) {
|
|
let message;
|
|
try {
|
|
message = JSON.parse(line);
|
|
} catch {
|
|
console.log(JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
id: null,
|
|
error: { code: -32700, message: 'Parse error' },
|
|
}));
|
|
continue;
|
|
}
|
|
try {
|
|
const response = await handleMessage(message);
|
|
if (response) {
|
|
console.log(JSON.stringify(response));
|
|
}
|
|
} catch (error) {
|
|
// #1606: Return proper internal error instead of parse error
|
|
console.log(JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
id: message.id ?? null,
|
|
error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' },
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
process.stdin.on('end', () => {
|
|
process.exit(0);
|
|
});
|
|
|
|
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 || {};
|
|
|
|
switch (message.method) {
|
|
case 'initialize':
|
|
return {
|
|
jsonrpc: '2.0',
|
|
id: message.id,
|
|
result: {
|
|
protocolVersion: '2024-11-05',
|
|
serverInfo: { name: 'claude-flow', 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':
|
|
return null;
|
|
|
|
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}` },
|
|
};
|
|
}
|
|
}
|
|
} else {
|
|
// Run normal CLI mode
|
|
const { CLI } = await import('../dist/src/index.js');
|
|
const cli = new CLI();
|
|
cli.run()
|
|
.then(() => {
|
|
// #1552: Exit cleanly after one-shot commands.
|
|
// Long-running commands (daemon foreground, mcp, status --watch) never resolve,
|
|
// so this only fires for normal CLI commands.
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('Fatal error:', error.message);
|
|
process.exit(1);
|
|
});
|
|
}
|