@claude-flow/cli 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agentic-flow/intelligence.json +16 -0
- package/.claude-flow/metrics/agent-metrics.json +1 -0
- package/.claude-flow/metrics/performance.json +87 -0
- package/.claude-flow/metrics/task-metrics.json +10 -0
- package/README.md +1186 -0
- package/__tests__/README.md +140 -0
- package/__tests__/TEST_SUMMARY.md +144 -0
- package/__tests__/cli.test.ts +558 -0
- package/__tests__/commands.test.ts +726 -0
- package/__tests__/config-adapter.test.ts +362 -0
- package/__tests__/config-loading.test.ts +106 -0
- package/__tests__/coverage/.tmp/coverage-0.json +1 -0
- package/__tests__/coverage/.tmp/coverage-1.json +1 -0
- package/__tests__/coverage/.tmp/coverage-2.json +1 -0
- package/__tests__/coverage/.tmp/coverage-3.json +1 -0
- package/__tests__/coverage/.tmp/coverage-4.json +1 -0
- package/__tests__/coverage/.tmp/coverage-5.json +1 -0
- package/__tests__/mcp-client.test.ts +480 -0
- package/__tests__/p1-commands.test.ts +1064 -0
- package/bin/cli.js +14 -0
- package/dist/src/commands/agent.d.ts +8 -0
- package/dist/src/commands/agent.d.ts.map +1 -0
- package/dist/src/commands/agent.js +803 -0
- package/dist/src/commands/agent.js.map +1 -0
- package/dist/src/commands/config.d.ts +8 -0
- package/dist/src/commands/config.d.ts.map +1 -0
- package/dist/src/commands/config.js +406 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/hive-mind.d.ts +8 -0
- package/dist/src/commands/hive-mind.d.ts.map +1 -0
- package/dist/src/commands/hive-mind.js +627 -0
- package/dist/src/commands/hive-mind.js.map +1 -0
- package/dist/src/commands/hooks.d.ts +8 -0
- package/dist/src/commands/hooks.d.ts.map +1 -0
- package/dist/src/commands/hooks.js +2098 -0
- package/dist/src/commands/hooks.js.map +1 -0
- package/dist/src/commands/index.d.ts +51 -0
- package/dist/src/commands/index.d.ts.map +1 -0
- package/dist/src/commands/index.js +105 -0
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/init.d.ts +8 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +532 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/mcp.d.ts +11 -0
- package/dist/src/commands/mcp.d.ts.map +1 -0
- package/dist/src/commands/mcp.js +662 -0
- package/dist/src/commands/mcp.js.map +1 -0
- package/dist/src/commands/memory.d.ts +8 -0
- package/dist/src/commands/memory.d.ts.map +1 -0
- package/dist/src/commands/memory.js +911 -0
- package/dist/src/commands/memory.js.map +1 -0
- package/dist/src/commands/migrate.d.ts +8 -0
- package/dist/src/commands/migrate.d.ts.map +1 -0
- package/dist/src/commands/migrate.js +398 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/process.d.ts +10 -0
- package/dist/src/commands/process.d.ts.map +1 -0
- package/dist/src/commands/process.js +566 -0
- package/dist/src/commands/process.js.map +1 -0
- package/dist/src/commands/session.d.ts +8 -0
- package/dist/src/commands/session.d.ts.map +1 -0
- package/dist/src/commands/session.js +750 -0
- package/dist/src/commands/session.js.map +1 -0
- package/dist/src/commands/start.d.ts +8 -0
- package/dist/src/commands/start.d.ts.map +1 -0
- package/dist/src/commands/start.js +398 -0
- package/dist/src/commands/start.js.map +1 -0
- package/dist/src/commands/status.d.ts +8 -0
- package/dist/src/commands/status.d.ts.map +1 -0
- package/dist/src/commands/status.js +560 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/swarm.d.ts +8 -0
- package/dist/src/commands/swarm.d.ts.map +1 -0
- package/dist/src/commands/swarm.js +573 -0
- package/dist/src/commands/swarm.js.map +1 -0
- package/dist/src/commands/task.d.ts +8 -0
- package/dist/src/commands/task.d.ts.map +1 -0
- package/dist/src/commands/task.js +671 -0
- package/dist/src/commands/task.js.map +1 -0
- package/dist/src/commands/workflow.d.ts +8 -0
- package/dist/src/commands/workflow.d.ts.map +1 -0
- package/dist/src/commands/workflow.js +617 -0
- package/dist/src/commands/workflow.js.map +1 -0
- package/dist/src/config-adapter.d.ts +15 -0
- package/dist/src/config-adapter.d.ts.map +1 -0
- package/dist/src/config-adapter.js +185 -0
- package/dist/src/config-adapter.js.map +1 -0
- package/dist/src/index.d.ts +55 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +312 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/infrastructure/in-memory-repositories.d.ts +68 -0
- package/dist/src/infrastructure/in-memory-repositories.d.ts.map +1 -0
- package/dist/src/infrastructure/in-memory-repositories.js +264 -0
- package/dist/src/infrastructure/in-memory-repositories.js.map +1 -0
- package/dist/src/init/claudemd-generator.d.ts +15 -0
- package/dist/src/init/claudemd-generator.d.ts.map +1 -0
- package/dist/src/init/claudemd-generator.js +626 -0
- package/dist/src/init/claudemd-generator.js.map +1 -0
- package/dist/src/init/executor.d.ts +11 -0
- package/dist/src/init/executor.d.ts.map +1 -0
- package/dist/src/init/executor.js +647 -0
- package/dist/src/init/executor.js.map +1 -0
- package/dist/src/init/helpers-generator.d.ts +42 -0
- package/dist/src/init/helpers-generator.d.ts.map +1 -0
- package/dist/src/init/helpers-generator.js +613 -0
- package/dist/src/init/helpers-generator.js.map +1 -0
- package/dist/src/init/index.d.ts +12 -0
- package/dist/src/init/index.d.ts.map +1 -0
- package/dist/src/init/index.js +15 -0
- package/dist/src/init/index.js.map +1 -0
- package/dist/src/init/mcp-generator.d.ts +18 -0
- package/dist/src/init/mcp-generator.d.ts.map +1 -0
- package/dist/src/init/mcp-generator.js +71 -0
- package/dist/src/init/mcp-generator.js.map +1 -0
- package/dist/src/init/settings-generator.d.ts +14 -0
- package/dist/src/init/settings-generator.d.ts.map +1 -0
- package/dist/src/init/settings-generator.js +257 -0
- package/dist/src/init/settings-generator.js.map +1 -0
- package/dist/src/init/statusline-generator.d.ts +14 -0
- package/dist/src/init/statusline-generator.d.ts.map +1 -0
- package/dist/src/init/statusline-generator.js +206 -0
- package/dist/src/init/statusline-generator.js.map +1 -0
- package/dist/src/init/types.d.ts +240 -0
- package/dist/src/init/types.d.ts.map +1 -0
- package/dist/src/init/types.js +210 -0
- package/dist/src/init/types.js.map +1 -0
- package/dist/src/mcp-client.d.ts +92 -0
- package/dist/src/mcp-client.d.ts.map +1 -0
- package/dist/src/mcp-client.js +189 -0
- package/dist/src/mcp-client.js.map +1 -0
- package/dist/src/mcp-server.d.ts +153 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/dist/src/mcp-server.js +448 -0
- package/dist/src/mcp-server.js.map +1 -0
- package/dist/src/mcp-tools/agent-tools.d.ts +8 -0
- package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/agent-tools.js +90 -0
- package/dist/src/mcp-tools/agent-tools.js.map +1 -0
- package/dist/src/mcp-tools/config-tools.d.ts +8 -0
- package/dist/src/mcp-tools/config-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/config-tools.js +86 -0
- package/dist/src/mcp-tools/config-tools.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +41 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.js +1646 -0
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -0
- package/dist/src/mcp-tools/index.d.ts +12 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -0
- package/dist/src/mcp-tools/index.js +11 -0
- package/dist/src/mcp-tools/index.js.map +1 -0
- package/dist/src/mcp-tools/memory-tools.d.ts +8 -0
- package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/memory-tools.js +87 -0
- package/dist/src/mcp-tools/memory-tools.js.map +1 -0
- package/dist/src/mcp-tools/swarm-tools.d.ts +8 -0
- package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/swarm-tools.js +67 -0
- package/dist/src/mcp-tools/swarm-tools.js.map +1 -0
- package/dist/src/mcp-tools/types.d.ts +31 -0
- package/dist/src/mcp-tools/types.d.ts.map +1 -0
- package/dist/src/mcp-tools/types.js +7 -0
- package/dist/src/mcp-tools/types.js.map +1 -0
- package/dist/src/output.d.ts +117 -0
- package/dist/src/output.d.ts.map +1 -0
- package/dist/src/output.js +471 -0
- package/dist/src/output.js.map +1 -0
- package/dist/src/parser.d.ts +41 -0
- package/dist/src/parser.d.ts.map +1 -0
- package/dist/src/parser.js +353 -0
- package/dist/src/parser.js.map +1 -0
- package/dist/src/prompt.d.ts +44 -0
- package/dist/src/prompt.d.ts.map +1 -0
- package/dist/src/prompt.js +501 -0
- package/dist/src/prompt.js.map +1 -0
- package/dist/src/types.d.ts +198 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +38 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/CONFIG_LOADING.md +236 -0
- package/docs/IMPLEMENTATION_COMPLETE.md +421 -0
- package/docs/MCP_CLIENT_GUIDE.md +620 -0
- package/docs/REFACTORING_SUMMARY.md +247 -0
- package/package.json +29 -0
- package/src/commands/agent.ts +941 -0
- package/src/commands/config.ts +452 -0
- package/src/commands/hive-mind.ts +762 -0
- package/src/commands/hooks.ts +2603 -0
- package/src/commands/index.ts +115 -0
- package/src/commands/init.ts +597 -0
- package/src/commands/mcp.ts +753 -0
- package/src/commands/memory.ts +1063 -0
- package/src/commands/migrate.ts +447 -0
- package/src/commands/process.ts +617 -0
- package/src/commands/session.ts +891 -0
- package/src/commands/start.ts +457 -0
- package/src/commands/status.ts +705 -0
- package/src/commands/swarm.ts +648 -0
- package/src/commands/task.ts +792 -0
- package/src/commands/workflow.ts +742 -0
- package/src/config-adapter.ts +210 -0
- package/src/index.ts +383 -0
- package/src/infrastructure/in-memory-repositories.ts +310 -0
- package/src/init/claudemd-generator.ts +631 -0
- package/src/init/executor.ts +756 -0
- package/src/init/helpers-generator.ts +628 -0
- package/src/init/index.ts +60 -0
- package/src/init/mcp-generator.ts +83 -0
- package/src/init/settings-generator.ts +274 -0
- package/src/init/statusline-generator.ts +211 -0
- package/src/init/types.ts +447 -0
- package/src/mcp-client.ts +227 -0
- package/src/mcp-server.ts +571 -0
- package/src/mcp-tools/agent-tools.ts +92 -0
- package/src/mcp-tools/config-tools.ts +88 -0
- package/src/mcp-tools/hooks-tools.ts +1819 -0
- package/src/mcp-tools/index.ts +12 -0
- package/src/mcp-tools/memory-tools.ts +89 -0
- package/src/mcp-tools/swarm-tools.ts +69 -0
- package/src/mcp-tools/types.ts +33 -0
- package/src/output.ts +593 -0
- package/src/parser.ts +417 -0
- package/src/prompt.ts +619 -0
- package/src/types.ts +287 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI MCP Server Management
|
|
3
|
+
*
|
|
4
|
+
* Provides server lifecycle management for MCP integration:
|
|
5
|
+
* - Start/stop/status methods with process management
|
|
6
|
+
* - Health check endpoint integration
|
|
7
|
+
* - Graceful shutdown handling
|
|
8
|
+
* - PID file management for daemon detection
|
|
9
|
+
* - Event-based status monitoring
|
|
10
|
+
*
|
|
11
|
+
* Performance Targets:
|
|
12
|
+
* - Server startup: <400ms
|
|
13
|
+
* - Health check: <10ms
|
|
14
|
+
* - Graceful shutdown: <5s
|
|
15
|
+
*
|
|
16
|
+
* @module @claude-flow/cli/mcp-server
|
|
17
|
+
* @version 3.0.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
22
|
+
import { createServer, Server } from 'http';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import * as fs from 'fs';
|
|
25
|
+
import * as os from 'os';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* MCP Server configuration
|
|
29
|
+
*/
|
|
30
|
+
export interface MCPServerOptions {
|
|
31
|
+
transport?: 'stdio' | 'http' | 'websocket';
|
|
32
|
+
host?: string;
|
|
33
|
+
port?: number;
|
|
34
|
+
pidFile?: string;
|
|
35
|
+
logFile?: string;
|
|
36
|
+
tools?: string[] | 'all';
|
|
37
|
+
daemonize?: boolean;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* MCP Server status
|
|
43
|
+
*/
|
|
44
|
+
export interface MCPServerStatus {
|
|
45
|
+
running: boolean;
|
|
46
|
+
pid?: number;
|
|
47
|
+
transport?: string;
|
|
48
|
+
host?: string;
|
|
49
|
+
port?: number;
|
|
50
|
+
uptime?: number;
|
|
51
|
+
tools?: number;
|
|
52
|
+
startedAt?: string;
|
|
53
|
+
health?: {
|
|
54
|
+
healthy: boolean;
|
|
55
|
+
error?: string;
|
|
56
|
+
metrics?: Record<string, number>;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Default configuration
|
|
62
|
+
*/
|
|
63
|
+
const DEFAULT_OPTIONS: Required<MCPServerOptions> = {
|
|
64
|
+
transport: 'stdio',
|
|
65
|
+
host: 'localhost',
|
|
66
|
+
port: 3000,
|
|
67
|
+
pidFile: path.join(os.tmpdir(), 'claude-flow-mcp.pid'),
|
|
68
|
+
logFile: path.join(os.tmpdir(), 'claude-flow-mcp.log'),
|
|
69
|
+
tools: 'all',
|
|
70
|
+
daemonize: false,
|
|
71
|
+
timeout: 30000,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* MCP Server Manager
|
|
76
|
+
*
|
|
77
|
+
* Manages the lifecycle of the MCP server process
|
|
78
|
+
*/
|
|
79
|
+
export class MCPServerManager extends EventEmitter {
|
|
80
|
+
private options: Required<MCPServerOptions>;
|
|
81
|
+
private process?: ChildProcess;
|
|
82
|
+
private server?: Server;
|
|
83
|
+
private startTime?: Date;
|
|
84
|
+
private healthCheckInterval?: NodeJS.Timeout;
|
|
85
|
+
|
|
86
|
+
constructor(options: MCPServerOptions = {}) {
|
|
87
|
+
super();
|
|
88
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Start the MCP server
|
|
93
|
+
*/
|
|
94
|
+
async start(): Promise<MCPServerStatus> {
|
|
95
|
+
// Check if already running
|
|
96
|
+
const status = await this.getStatus();
|
|
97
|
+
if (status.running) {
|
|
98
|
+
throw new Error(`MCP Server already running (PID: ${status.pid})`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const startTime = performance.now();
|
|
102
|
+
this.startTime = new Date();
|
|
103
|
+
|
|
104
|
+
this.emit('starting', { options: this.options });
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
if (this.options.transport === 'stdio') {
|
|
108
|
+
// For stdio transport, spawn the server process
|
|
109
|
+
await this.startStdioServer();
|
|
110
|
+
} else {
|
|
111
|
+
// For HTTP/WebSocket, start in-process server
|
|
112
|
+
await this.startHttpServer();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const duration = performance.now() - startTime;
|
|
116
|
+
|
|
117
|
+
// Write PID file
|
|
118
|
+
await this.writePidFile();
|
|
119
|
+
|
|
120
|
+
// Start health check monitoring
|
|
121
|
+
this.startHealthMonitoring();
|
|
122
|
+
|
|
123
|
+
const finalStatus = await this.getStatus();
|
|
124
|
+
|
|
125
|
+
this.emit('started', {
|
|
126
|
+
...finalStatus,
|
|
127
|
+
startupTime: duration,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return finalStatus;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this.emit('error', error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Stop the MCP server
|
|
139
|
+
*/
|
|
140
|
+
async stop(force = false): Promise<void> {
|
|
141
|
+
const status = await this.getStatus();
|
|
142
|
+
|
|
143
|
+
if (!status.running) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.emit('stopping', { force });
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Stop health monitoring
|
|
151
|
+
if (this.healthCheckInterval) {
|
|
152
|
+
clearInterval(this.healthCheckInterval);
|
|
153
|
+
this.healthCheckInterval = undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (this.process) {
|
|
157
|
+
// Graceful shutdown
|
|
158
|
+
if (!force) {
|
|
159
|
+
this.process.kill('SIGTERM');
|
|
160
|
+
await this.waitForExit(5000);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Force kill if still running
|
|
164
|
+
if (this.process && !this.process.killed) {
|
|
165
|
+
this.process.kill('SIGKILL');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.process = undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (this.server) {
|
|
172
|
+
await new Promise<void>((resolve) => {
|
|
173
|
+
this.server!.close(() => resolve());
|
|
174
|
+
});
|
|
175
|
+
this.server = undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Remove PID file
|
|
179
|
+
await this.removePidFile();
|
|
180
|
+
|
|
181
|
+
this.startTime = undefined;
|
|
182
|
+
this.emit('stopped');
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.emit('error', error);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get server status
|
|
191
|
+
*/
|
|
192
|
+
async getStatus(): Promise<MCPServerStatus> {
|
|
193
|
+
// Check PID file
|
|
194
|
+
const pid = await this.readPidFile();
|
|
195
|
+
|
|
196
|
+
if (!pid) {
|
|
197
|
+
return { running: false };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check if process is running
|
|
201
|
+
const isRunning = this.isProcessRunning(pid);
|
|
202
|
+
|
|
203
|
+
if (!isRunning) {
|
|
204
|
+
// Clean up stale PID file
|
|
205
|
+
await this.removePidFile();
|
|
206
|
+
return { running: false };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Build status
|
|
210
|
+
const status: MCPServerStatus = {
|
|
211
|
+
running: true,
|
|
212
|
+
pid,
|
|
213
|
+
transport: this.options.transport,
|
|
214
|
+
host: this.options.host,
|
|
215
|
+
port: this.options.port,
|
|
216
|
+
startedAt: this.startTime?.toISOString(),
|
|
217
|
+
uptime: this.startTime
|
|
218
|
+
? Math.floor((Date.now() - this.startTime.getTime()) / 1000)
|
|
219
|
+
: undefined,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Get health status for HTTP transport
|
|
223
|
+
if (this.options.transport !== 'stdio') {
|
|
224
|
+
status.health = await this.checkHealth();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return status;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Check server health
|
|
232
|
+
*/
|
|
233
|
+
async checkHealth(): Promise<{
|
|
234
|
+
healthy: boolean;
|
|
235
|
+
error?: string;
|
|
236
|
+
metrics?: Record<string, number>;
|
|
237
|
+
}> {
|
|
238
|
+
if (this.options.transport === 'stdio') {
|
|
239
|
+
// For stdio, just check if process is running
|
|
240
|
+
const pid = await this.readPidFile();
|
|
241
|
+
return {
|
|
242
|
+
healthy: pid !== null && this.isProcessRunning(pid),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// For HTTP/WebSocket, make health check request
|
|
247
|
+
try {
|
|
248
|
+
const response = await this.httpRequest(
|
|
249
|
+
`http://${this.options.host}:${this.options.port}/health`,
|
|
250
|
+
'GET',
|
|
251
|
+
this.options.timeout
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
healthy: response.status === 'ok',
|
|
256
|
+
metrics: {
|
|
257
|
+
connections: response.connections || 0,
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
healthy: false,
|
|
263
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Restart the server
|
|
270
|
+
*/
|
|
271
|
+
async restart(): Promise<MCPServerStatus> {
|
|
272
|
+
await this.stop();
|
|
273
|
+
return await this.start();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Start stdio server process
|
|
278
|
+
*/
|
|
279
|
+
private async startStdioServer(): Promise<void> {
|
|
280
|
+
// Resolve server script path relative to this file
|
|
281
|
+
const serverScript = path.resolve(
|
|
282
|
+
__dirname,
|
|
283
|
+
'../../../mcp/server-entry.ts'
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Check if tsx is available
|
|
287
|
+
const command = 'npx';
|
|
288
|
+
const args = [
|
|
289
|
+
'tsx',
|
|
290
|
+
serverScript,
|
|
291
|
+
'--transport', this.options.transport,
|
|
292
|
+
'--host', this.options.host,
|
|
293
|
+
'--port', String(this.options.port),
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
if (this.options.tools !== 'all') {
|
|
297
|
+
args.push('--tools', this.options.tools.join(','));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
this.process = spawn(command, args, {
|
|
301
|
+
stdio: this.options.daemonize ? 'ignore' : ['pipe', 'pipe', 'pipe'],
|
|
302
|
+
detached: this.options.daemonize,
|
|
303
|
+
env: {
|
|
304
|
+
...process.env,
|
|
305
|
+
NODE_ENV: process.env.NODE_ENV || 'production',
|
|
306
|
+
MCP_SERVER_MODE: 'true',
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
if (this.options.daemonize) {
|
|
311
|
+
this.process.unref();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Handle process events
|
|
315
|
+
this.process.on('error', (error) => {
|
|
316
|
+
this.emit('error', error);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
this.process.on('exit', (code, signal) => {
|
|
320
|
+
this.emit('exit', { code, signal });
|
|
321
|
+
this.process = undefined;
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Wait for server to be ready
|
|
325
|
+
await this.waitForReady();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Start HTTP server in-process
|
|
330
|
+
*/
|
|
331
|
+
private async startHttpServer(): Promise<void> {
|
|
332
|
+
// Dynamically import the MCP server
|
|
333
|
+
// Note: Path is relative to the compiled output location
|
|
334
|
+
const mcpServerPath = path.resolve(__dirname, '../../../mcp/server.js');
|
|
335
|
+
const { MCPServer, createMCPServer } = await import(mcpServerPath);
|
|
336
|
+
|
|
337
|
+
const logger = {
|
|
338
|
+
debug: (msg: string, data?: unknown) => this.emit('log', { level: 'debug', msg, data }),
|
|
339
|
+
info: (msg: string, data?: unknown) => this.emit('log', { level: 'info', msg, data }),
|
|
340
|
+
warn: (msg: string, data?: unknown) => this.emit('log', { level: 'warn', msg, data }),
|
|
341
|
+
error: (msg: string, data?: unknown) => this.emit('log', { level: 'error', msg, data }),
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const mcpServer = createMCPServer(
|
|
345
|
+
{
|
|
346
|
+
name: 'Claude-Flow MCP Server V3',
|
|
347
|
+
version: '3.0.0',
|
|
348
|
+
transport: this.options.transport as 'http' | 'websocket',
|
|
349
|
+
host: this.options.host,
|
|
350
|
+
port: this.options.port,
|
|
351
|
+
enableMetrics: true,
|
|
352
|
+
enableCaching: true,
|
|
353
|
+
},
|
|
354
|
+
logger
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
await mcpServer.start();
|
|
358
|
+
|
|
359
|
+
// Store reference for stopping
|
|
360
|
+
(this as any)._mcpServer = mcpServer;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Wait for server to be ready
|
|
365
|
+
*/
|
|
366
|
+
private async waitForReady(timeout = 10000): Promise<void> {
|
|
367
|
+
const startTime = Date.now();
|
|
368
|
+
|
|
369
|
+
while (Date.now() - startTime < timeout) {
|
|
370
|
+
const health = await this.checkHealth();
|
|
371
|
+
if (health.healthy) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
await this.sleep(100);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// For stdio, just check if process is running
|
|
378
|
+
if (this.options.transport === 'stdio' && this.process && !this.process.killed) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
throw new Error('Server failed to start within timeout');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Wait for process to exit
|
|
387
|
+
*/
|
|
388
|
+
private async waitForExit(timeout: number): Promise<void> {
|
|
389
|
+
if (!this.process) return;
|
|
390
|
+
|
|
391
|
+
return new Promise((resolve) => {
|
|
392
|
+
const timer = setTimeout(() => {
|
|
393
|
+
resolve();
|
|
394
|
+
}, timeout);
|
|
395
|
+
|
|
396
|
+
this.process!.once('exit', () => {
|
|
397
|
+
clearTimeout(timer);
|
|
398
|
+
resolve();
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Start health monitoring
|
|
405
|
+
*/
|
|
406
|
+
private startHealthMonitoring(): void {
|
|
407
|
+
this.healthCheckInterval = setInterval(async () => {
|
|
408
|
+
try {
|
|
409
|
+
const health = await this.checkHealth();
|
|
410
|
+
this.emit('health', health);
|
|
411
|
+
|
|
412
|
+
if (!health.healthy) {
|
|
413
|
+
this.emit('unhealthy', health);
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
this.emit('health-error', error);
|
|
417
|
+
}
|
|
418
|
+
}, 30000);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Write PID file
|
|
423
|
+
*/
|
|
424
|
+
private async writePidFile(): Promise<void> {
|
|
425
|
+
const pid = this.process?.pid || process.pid;
|
|
426
|
+
await fs.promises.writeFile(this.options.pidFile, String(pid), 'utf8');
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Read PID file
|
|
431
|
+
*/
|
|
432
|
+
private async readPidFile(): Promise<number | null> {
|
|
433
|
+
try {
|
|
434
|
+
const content = await fs.promises.readFile(this.options.pidFile, 'utf8');
|
|
435
|
+
const pid = parseInt(content.trim(), 10);
|
|
436
|
+
return isNaN(pid) ? null : pid;
|
|
437
|
+
} catch {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Remove PID file
|
|
444
|
+
*/
|
|
445
|
+
private async removePidFile(): Promise<void> {
|
|
446
|
+
try {
|
|
447
|
+
await fs.promises.unlink(this.options.pidFile);
|
|
448
|
+
} catch {
|
|
449
|
+
// Ignore errors
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Check if process is running
|
|
455
|
+
*/
|
|
456
|
+
private isProcessRunning(pid: number): boolean {
|
|
457
|
+
try {
|
|
458
|
+
process.kill(pid, 0);
|
|
459
|
+
return true;
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Make HTTP request
|
|
467
|
+
*/
|
|
468
|
+
private async httpRequest(
|
|
469
|
+
url: string,
|
|
470
|
+
method: string,
|
|
471
|
+
timeout: number
|
|
472
|
+
): Promise<any> {
|
|
473
|
+
return new Promise((resolve, reject) => {
|
|
474
|
+
const urlObj = new URL(url);
|
|
475
|
+
const http = require('http');
|
|
476
|
+
|
|
477
|
+
const req = http.request(
|
|
478
|
+
{
|
|
479
|
+
hostname: urlObj.hostname,
|
|
480
|
+
port: urlObj.port,
|
|
481
|
+
path: urlObj.pathname,
|
|
482
|
+
method,
|
|
483
|
+
timeout,
|
|
484
|
+
},
|
|
485
|
+
(res: any) => {
|
|
486
|
+
let data = '';
|
|
487
|
+
res.on('data', (chunk: string) => {
|
|
488
|
+
data += chunk;
|
|
489
|
+
});
|
|
490
|
+
res.on('end', () => {
|
|
491
|
+
try {
|
|
492
|
+
resolve(JSON.parse(data));
|
|
493
|
+
} catch {
|
|
494
|
+
resolve({ status: res.statusCode === 200 ? 'ok' : 'error' });
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
req.on('error', reject);
|
|
501
|
+
req.on('timeout', () => {
|
|
502
|
+
req.destroy();
|
|
503
|
+
reject(new Error('Request timeout'));
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
req.end();
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Sleep utility
|
|
512
|
+
*/
|
|
513
|
+
private sleep(ms: number): Promise<void> {
|
|
514
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Create MCP server manager
|
|
520
|
+
*/
|
|
521
|
+
export function createMCPServerManager(
|
|
522
|
+
options?: MCPServerOptions
|
|
523
|
+
): MCPServerManager {
|
|
524
|
+
return new MCPServerManager(options);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Singleton server manager instance
|
|
529
|
+
*/
|
|
530
|
+
let serverManager: MCPServerManager | null = null;
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Get or create server manager singleton
|
|
534
|
+
*/
|
|
535
|
+
export function getServerManager(
|
|
536
|
+
options?: MCPServerOptions
|
|
537
|
+
): MCPServerManager {
|
|
538
|
+
if (!serverManager) {
|
|
539
|
+
serverManager = new MCPServerManager(options);
|
|
540
|
+
}
|
|
541
|
+
return serverManager;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Quick start MCP server
|
|
546
|
+
*/
|
|
547
|
+
export async function startMCPServer(
|
|
548
|
+
options?: MCPServerOptions
|
|
549
|
+
): Promise<MCPServerStatus> {
|
|
550
|
+
const manager = getServerManager(options);
|
|
551
|
+
return await manager.start();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Quick stop MCP server
|
|
556
|
+
*/
|
|
557
|
+
export async function stopMCPServer(force = false): Promise<void> {
|
|
558
|
+
if (serverManager) {
|
|
559
|
+
await serverManager.stop(force);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Get MCP server status
|
|
565
|
+
*/
|
|
566
|
+
export async function getMCPServerStatus(): Promise<MCPServerStatus> {
|
|
567
|
+
const manager = getServerManager();
|
|
568
|
+
return await manager.getStatus();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export default MCPServerManager;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent MCP Tools for CLI
|
|
3
|
+
*
|
|
4
|
+
* Tool definitions for agent lifecycle management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { MCPTool } from './types.js';
|
|
8
|
+
|
|
9
|
+
export const agentTools: MCPTool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'agent/spawn',
|
|
12
|
+
description: 'Spawn a new agent',
|
|
13
|
+
category: 'agent',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
agentType: { type: 'string', description: 'Type of agent to spawn' },
|
|
18
|
+
agentId: { type: 'string', description: 'Optional custom agent ID' },
|
|
19
|
+
config: { type: 'object', description: 'Agent configuration' },
|
|
20
|
+
},
|
|
21
|
+
required: ['agentType'],
|
|
22
|
+
},
|
|
23
|
+
handler: async (input) => {
|
|
24
|
+
// Stub implementation - actual coordinator integration at runtime
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
agentId: (input.agentId as string) || `agent-${Date.now()}`,
|
|
28
|
+
agentType: input.agentType,
|
|
29
|
+
status: 'spawned',
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'agent/terminate',
|
|
35
|
+
description: 'Terminate an agent',
|
|
36
|
+
category: 'agent',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
agentId: { type: 'string', description: 'ID of agent to terminate' },
|
|
41
|
+
force: { type: 'boolean', description: 'Force immediate termination' },
|
|
42
|
+
},
|
|
43
|
+
required: ['agentId'],
|
|
44
|
+
},
|
|
45
|
+
handler: async (input) => {
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
agentId: input.agentId,
|
|
49
|
+
terminated: true,
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'agent/status',
|
|
55
|
+
description: 'Get agent status',
|
|
56
|
+
category: 'agent',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
agentId: { type: 'string', description: 'ID of agent' },
|
|
61
|
+
},
|
|
62
|
+
required: ['agentId'],
|
|
63
|
+
},
|
|
64
|
+
handler: async (input) => {
|
|
65
|
+
return {
|
|
66
|
+
agentId: input.agentId,
|
|
67
|
+
status: 'idle',
|
|
68
|
+
health: 1.0,
|
|
69
|
+
taskCount: 0,
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'agent/list',
|
|
75
|
+
description: 'List all agents',
|
|
76
|
+
category: 'agent',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
status: { type: 'string', description: 'Filter by status' },
|
|
81
|
+
domain: { type: 'string', description: 'Filter by domain' },
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
handler: async (input) => {
|
|
85
|
+
return {
|
|
86
|
+
agents: [],
|
|
87
|
+
total: 0,
|
|
88
|
+
filters: input,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
];
|