@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,620 @@
|
|
|
1
|
+
# MCP Client Guide for CLI Commands
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The MCP Client (`mcp-client.ts`) provides a thin wrapper for CLI commands to call MCP tools, implementing **ADR-005: MCP-First API Design** where CLI acts as a thin wrapper around MCP tools.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────┐
|
|
11
|
+
│ CLI Command │ ← User interaction & display only
|
|
12
|
+
└────────┬────────┘
|
|
13
|
+
│ callMCPTool()
|
|
14
|
+
▼
|
|
15
|
+
┌─────────────────┐
|
|
16
|
+
│ MCP Client │ ← Tool registry & routing
|
|
17
|
+
└────────┬────────┘
|
|
18
|
+
│ tool.handler()
|
|
19
|
+
▼
|
|
20
|
+
┌─────────────────┐
|
|
21
|
+
│ MCP Tool │ ← Business logic lives here
|
|
22
|
+
│ Handler │
|
|
23
|
+
└─────────────────┘
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Import the MCP Client
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Call an MCP Tool
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
try {
|
|
38
|
+
const result = await callMCPTool('agent/spawn', {
|
|
39
|
+
agentType: 'coder',
|
|
40
|
+
priority: 'normal',
|
|
41
|
+
config: { timeout: 300 }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Handle success - display output
|
|
45
|
+
output.printSuccess(`Agent ${result.agentId} spawned`);
|
|
46
|
+
return { success: true, data: result };
|
|
47
|
+
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof MCPClientError) {
|
|
50
|
+
output.printError(`Failed: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
return { success: false, exitCode: 1 };
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Available MCP Tools
|
|
57
|
+
|
|
58
|
+
### Agent Tools
|
|
59
|
+
|
|
60
|
+
| Tool Name | Description | Input Parameters |
|
|
61
|
+
|-----------|-------------|------------------|
|
|
62
|
+
| `agent/spawn` | Spawn a new agent | `agentType`, `id?`, `config?`, `priority?`, `metadata?` |
|
|
63
|
+
| `agent/list` | List all agents | `status?`, `agentType?`, `limit?`, `offset?` |
|
|
64
|
+
| `agent/status` | Get agent status | `agentId`, `includeMetrics?`, `includeHistory?` |
|
|
65
|
+
| `agent/terminate` | Terminate an agent | `agentId`, `graceful?`, `reason?` |
|
|
66
|
+
|
|
67
|
+
### Swarm Tools
|
|
68
|
+
|
|
69
|
+
| Tool Name | Description | Input Parameters |
|
|
70
|
+
|-----------|-------------|------------------|
|
|
71
|
+
| `swarm/init` | Initialize swarm | `topology`, `maxAgents?`, `config?`, `metadata?` |
|
|
72
|
+
| `swarm/status` | Get swarm status | `includeAgents?`, `includeMetrics?`, `includeTopology?` |
|
|
73
|
+
| `swarm/scale` | Scale swarm | `targetAgents`, `scaleStrategy?`, `agentTypes?`, `reason?` |
|
|
74
|
+
|
|
75
|
+
### Memory Tools
|
|
76
|
+
|
|
77
|
+
| Tool Name | Description | Input Parameters |
|
|
78
|
+
|-----------|-------------|------------------|
|
|
79
|
+
| `memory/store` | Store memory | `content`, `type?`, `category?`, `tags?`, `importance?`, `ttl?` |
|
|
80
|
+
| `memory/search` | Search memories | `query`, `searchType?`, `type?`, `category?`, `tags?`, `limit?`, `minRelevance?` |
|
|
81
|
+
| `memory/list` | List memories | `type?`, `category?`, `tags?`, `sortBy?`, `sortOrder?`, `limit?`, `offset?` |
|
|
82
|
+
|
|
83
|
+
### Config Tools
|
|
84
|
+
|
|
85
|
+
| Tool Name | Description | Input Parameters |
|
|
86
|
+
|-----------|-------------|------------------|
|
|
87
|
+
| `config/load` | Load configuration | `path?`, `scope?`, `merge?`, `includeDefaults?` |
|
|
88
|
+
| `config/save` | Save configuration | `config`, `path?`, `scope?`, `merge?`, `createBackup?` |
|
|
89
|
+
| `config/validate` | Validate config | `config`, `strict?`, `fixIssues?` |
|
|
90
|
+
|
|
91
|
+
## MCP Client API
|
|
92
|
+
|
|
93
|
+
### Core Functions
|
|
94
|
+
|
|
95
|
+
#### `callMCPTool<T>(toolName, input, context?): Promise<T>`
|
|
96
|
+
|
|
97
|
+
Call an MCP tool by name and return typed result.
|
|
98
|
+
|
|
99
|
+
**Parameters:**
|
|
100
|
+
- `toolName`: MCP tool name (e.g., `'agent/spawn'`)
|
|
101
|
+
- `input`: Tool input parameters (validated by tool's schema)
|
|
102
|
+
- `context?`: Optional context object
|
|
103
|
+
|
|
104
|
+
**Returns:** Promise resolving to tool result
|
|
105
|
+
|
|
106
|
+
**Throws:** `MCPClientError` if tool not found or execution fails
|
|
107
|
+
|
|
108
|
+
**Example:**
|
|
109
|
+
```typescript
|
|
110
|
+
const result = await callMCPTool<{ agentId: string }>('agent/spawn', {
|
|
111
|
+
agentType: 'coder',
|
|
112
|
+
priority: 'normal'
|
|
113
|
+
});
|
|
114
|
+
console.log(`Spawned agent: ${result.agentId}`);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### `getToolMetadata(toolName): ToolMetadata | undefined`
|
|
118
|
+
|
|
119
|
+
Get tool metadata without executing it.
|
|
120
|
+
|
|
121
|
+
**Example:**
|
|
122
|
+
```typescript
|
|
123
|
+
const metadata = getToolMetadata('agent/spawn');
|
|
124
|
+
if (metadata) {
|
|
125
|
+
console.log(`Description: ${metadata.description}`);
|
|
126
|
+
console.log(`Category: ${metadata.category}`);
|
|
127
|
+
console.log(`Schema:`, metadata.inputSchema);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### `listMCPTools(category?): ToolMetadata[]`
|
|
132
|
+
|
|
133
|
+
List all available MCP tools, optionally filtered by category.
|
|
134
|
+
|
|
135
|
+
**Example:**
|
|
136
|
+
```typescript
|
|
137
|
+
// List all tools
|
|
138
|
+
const allTools = listMCPTools();
|
|
139
|
+
|
|
140
|
+
// List only agent tools
|
|
141
|
+
const agentTools = listMCPTools('agent');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `hasTool(toolName): boolean`
|
|
145
|
+
|
|
146
|
+
Check if an MCP tool exists.
|
|
147
|
+
|
|
148
|
+
**Example:**
|
|
149
|
+
```typescript
|
|
150
|
+
if (hasTool('agent/spawn')) {
|
|
151
|
+
console.log('Agent spawn tool is available');
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `validateToolInput(toolName, input): { valid: boolean; errors?: string[] }`
|
|
156
|
+
|
|
157
|
+
Validate input against tool schema before calling.
|
|
158
|
+
|
|
159
|
+
**Example:**
|
|
160
|
+
```typescript
|
|
161
|
+
const validation = validateToolInput('agent/spawn', {
|
|
162
|
+
agentType: 'coder'
|
|
163
|
+
// missing required field
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!validation.valid) {
|
|
167
|
+
console.error('Validation errors:', validation.errors);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `getToolCategories(): string[]`
|
|
172
|
+
|
|
173
|
+
Get all unique tool categories.
|
|
174
|
+
|
|
175
|
+
**Example:**
|
|
176
|
+
```typescript
|
|
177
|
+
const categories = getToolCategories();
|
|
178
|
+
console.log('Available categories:', categories);
|
|
179
|
+
// Output: ['agent', 'swarm', 'memory', 'config']
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Error Handling
|
|
183
|
+
|
|
184
|
+
#### `MCPClientError`
|
|
185
|
+
|
|
186
|
+
Custom error class for MCP tool failures.
|
|
187
|
+
|
|
188
|
+
**Properties:**
|
|
189
|
+
- `message`: Error message
|
|
190
|
+
- `toolName`: Name of the tool that failed
|
|
191
|
+
- `cause?`: Original error if available
|
|
192
|
+
|
|
193
|
+
**Example:**
|
|
194
|
+
```typescript
|
|
195
|
+
try {
|
|
196
|
+
await callMCPTool('agent/spawn', { ... });
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (error instanceof MCPClientError) {
|
|
199
|
+
console.error(`Tool '${error.toolName}' failed: ${error.message}`);
|
|
200
|
+
if (error.cause) {
|
|
201
|
+
console.error('Caused by:', error.cause);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## CLI Command Pattern
|
|
208
|
+
|
|
209
|
+
### Standard Pattern
|
|
210
|
+
|
|
211
|
+
All CLI commands should follow this pattern:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import type { Command, CommandContext, CommandResult } from '../types.js';
|
|
215
|
+
import { output } from '../output.js';
|
|
216
|
+
import { select, confirm, input } from '../prompt.js';
|
|
217
|
+
import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
218
|
+
|
|
219
|
+
const myCommand: Command = {
|
|
220
|
+
name: 'my-command',
|
|
221
|
+
description: 'Command description',
|
|
222
|
+
options: [ /* command options */ ],
|
|
223
|
+
|
|
224
|
+
action: async (ctx: CommandContext): Promise<CommandResult> => {
|
|
225
|
+
// STEP 1: Gather input (interactive prompts if needed)
|
|
226
|
+
let param = ctx.flags.param as string;
|
|
227
|
+
if (!param && ctx.interactive) {
|
|
228
|
+
param = await input({
|
|
229
|
+
message: 'Enter parameter:',
|
|
230
|
+
validate: (v) => v.length > 0 || 'Required'
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// STEP 2: Validate required inputs
|
|
235
|
+
if (!param) {
|
|
236
|
+
output.printError('Parameter is required');
|
|
237
|
+
return { success: false, exitCode: 1 };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// STEP 3: Call MCP tool (business logic)
|
|
241
|
+
try {
|
|
242
|
+
const result = await callMCPTool<ResultType>('tool/name', {
|
|
243
|
+
param,
|
|
244
|
+
// ... other inputs
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// STEP 4: Format and display output
|
|
248
|
+
if (ctx.flags.format === 'json') {
|
|
249
|
+
output.printJson(result);
|
|
250
|
+
} else {
|
|
251
|
+
output.printTable({
|
|
252
|
+
columns: [ /* ... */ ],
|
|
253
|
+
data: [ /* format result for display */ ]
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
output.printSuccess('Operation successful');
|
|
258
|
+
return { success: true, data: result };
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
// STEP 5: Handle errors
|
|
262
|
+
if (error instanceof MCPClientError) {
|
|
263
|
+
output.printError(`Failed: ${error.message}`);
|
|
264
|
+
} else {
|
|
265
|
+
output.printError(`Unexpected error: ${String(error)}`);
|
|
266
|
+
}
|
|
267
|
+
return { success: false, exitCode: 1 };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Key Principles
|
|
274
|
+
|
|
275
|
+
1. **CLI is thin**: Only handles UI/UX, no business logic
|
|
276
|
+
2. **MCP tool has logic**: All business logic in MCP tool handlers
|
|
277
|
+
3. **Type safety**: Use TypeScript generics for tool results
|
|
278
|
+
4. **Error handling**: Always catch and handle MCPClientError
|
|
279
|
+
5. **Display formatting**: CLI adds visual enhancements only
|
|
280
|
+
|
|
281
|
+
### What Belongs in CLI vs MCP Tool
|
|
282
|
+
|
|
283
|
+
#### CLI Command Responsibilities (Display Layer)
|
|
284
|
+
|
|
285
|
+
✅ Interactive prompts (select, confirm, input)
|
|
286
|
+
✅ Flag/argument parsing
|
|
287
|
+
✅ Input validation (basic checks)
|
|
288
|
+
✅ Output formatting (tables, boxes, colors)
|
|
289
|
+
✅ Progress indicators
|
|
290
|
+
✅ Success/error messages
|
|
291
|
+
✅ JSON output formatting
|
|
292
|
+
|
|
293
|
+
#### MCP Tool Responsibilities (Business Logic)
|
|
294
|
+
|
|
295
|
+
✅ Data validation (schema validation)
|
|
296
|
+
✅ Business rules enforcement
|
|
297
|
+
✅ Resource management (agents, swarms, memory)
|
|
298
|
+
✅ State changes
|
|
299
|
+
✅ Database operations
|
|
300
|
+
✅ External API calls
|
|
301
|
+
✅ Calculations and transformations
|
|
302
|
+
|
|
303
|
+
## Examples
|
|
304
|
+
|
|
305
|
+
### Example 1: Simple Tool Call
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// Spawn an agent
|
|
309
|
+
const spawnCommand: Command = {
|
|
310
|
+
name: 'spawn',
|
|
311
|
+
action: async (ctx: CommandContext) => {
|
|
312
|
+
const agentType = ctx.flags.type as string;
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
const result = await callMCPTool('agent/spawn', {
|
|
316
|
+
agentType,
|
|
317
|
+
priority: 'normal'
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
output.printSuccess(`Spawned agent: ${result.agentId}`);
|
|
321
|
+
return { success: true, data: result };
|
|
322
|
+
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (error instanceof MCPClientError) {
|
|
325
|
+
output.printError(error.message);
|
|
326
|
+
}
|
|
327
|
+
return { success: false, exitCode: 1 };
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Example 2: Tool Call with Filtering
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// List agents with filters
|
|
337
|
+
const listCommand: Command = {
|
|
338
|
+
name: 'list',
|
|
339
|
+
action: async (ctx: CommandContext) => {
|
|
340
|
+
try {
|
|
341
|
+
const result = await callMCPTool<{
|
|
342
|
+
agents: Agent[];
|
|
343
|
+
total: number;
|
|
344
|
+
}>('agent/list', {
|
|
345
|
+
status: ctx.flags.status || 'all',
|
|
346
|
+
agentType: ctx.flags.type,
|
|
347
|
+
limit: 100
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Display results
|
|
351
|
+
output.printTable({
|
|
352
|
+
columns: [
|
|
353
|
+
{ key: 'id', header: 'ID', width: 20 },
|
|
354
|
+
{ key: 'type', header: 'Type', width: 15 },
|
|
355
|
+
{ key: 'status', header: 'Status', width: 10 }
|
|
356
|
+
],
|
|
357
|
+
data: result.agents
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
output.printInfo(`Total: ${result.total} agents`);
|
|
361
|
+
return { success: true, data: result };
|
|
362
|
+
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (error instanceof MCPClientError) {
|
|
365
|
+
output.printError(error.message);
|
|
366
|
+
}
|
|
367
|
+
return { success: false, exitCode: 1 };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Example 3: Interactive Input with Tool Call
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// Store memory with interactive input
|
|
377
|
+
const storeCommand: Command = {
|
|
378
|
+
name: 'store',
|
|
379
|
+
action: async (ctx: CommandContext) => {
|
|
380
|
+
// Get input interactively if not provided
|
|
381
|
+
let content = ctx.flags.content as string;
|
|
382
|
+
if (!content && ctx.interactive) {
|
|
383
|
+
content = await input({
|
|
384
|
+
message: 'Enter content to store:',
|
|
385
|
+
validate: (v) => v.length > 0 || 'Content required'
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (!content) {
|
|
390
|
+
output.printError('Content is required');
|
|
391
|
+
return { success: false, exitCode: 1 };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Select memory type interactively
|
|
395
|
+
let type = ctx.flags.type as string;
|
|
396
|
+
if (!type && ctx.interactive) {
|
|
397
|
+
type = await select({
|
|
398
|
+
message: 'Select memory type:',
|
|
399
|
+
options: [
|
|
400
|
+
{ value: 'episodic', label: 'Episodic' },
|
|
401
|
+
{ value: 'semantic', label: 'Semantic' },
|
|
402
|
+
{ value: 'procedural', label: 'Procedural' }
|
|
403
|
+
]
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const result = await callMCPTool('memory/store', {
|
|
409
|
+
content,
|
|
410
|
+
type: type || 'episodic',
|
|
411
|
+
tags: ctx.flags.tags?.split(',') || [],
|
|
412
|
+
importance: ctx.flags.importance
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
output.printSuccess(`Stored memory: ${result.id}`);
|
|
416
|
+
return { success: true, data: result };
|
|
417
|
+
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (error instanceof MCPClientError) {
|
|
420
|
+
output.printError(error.message);
|
|
421
|
+
}
|
|
422
|
+
return { success: false, exitCode: 1 };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Testing
|
|
429
|
+
|
|
430
|
+
### Unit Testing MCP Client
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
import { callMCPTool, MCPClientError, hasTool } from '../mcp-client.js';
|
|
434
|
+
|
|
435
|
+
describe('MCP Client', () => {
|
|
436
|
+
it('should call agent/spawn tool', async () => {
|
|
437
|
+
const result = await callMCPTool('agent/spawn', {
|
|
438
|
+
agentType: 'coder'
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
expect(result).toHaveProperty('agentId');
|
|
442
|
+
expect(result).toHaveProperty('agentType', 'coder');
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should throw MCPClientError for unknown tool', async () => {
|
|
446
|
+
await expect(
|
|
447
|
+
callMCPTool('unknown/tool', {})
|
|
448
|
+
).rejects.toThrow(MCPClientError);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should check if tool exists', () => {
|
|
452
|
+
expect(hasTool('agent/spawn')).toBe(true);
|
|
453
|
+
expect(hasTool('unknown/tool')).toBe(false);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Integration Testing CLI Commands
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { execute } from '../cli.js';
|
|
462
|
+
|
|
463
|
+
describe('Agent spawn command', () => {
|
|
464
|
+
it('should spawn agent via MCP tool', async () => {
|
|
465
|
+
const result = await execute(['agent', 'spawn', '--type', 'coder']);
|
|
466
|
+
|
|
467
|
+
expect(result.success).toBe(true);
|
|
468
|
+
expect(result.data).toHaveProperty('agentId');
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Best Practices
|
|
474
|
+
|
|
475
|
+
### 1. Type Safety
|
|
476
|
+
|
|
477
|
+
Always provide type parameters to `callMCPTool`:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// ✅ Good: Type-safe
|
|
481
|
+
const result = await callMCPTool<{ agentId: string }>('agent/spawn', { ... });
|
|
482
|
+
console.log(result.agentId); // TypeScript knows this exists
|
|
483
|
+
|
|
484
|
+
// ❌ Bad: No type safety
|
|
485
|
+
const result = await callMCPTool('agent/spawn', { ... });
|
|
486
|
+
console.log(result.agentId); // No type checking
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 2. Error Handling
|
|
490
|
+
|
|
491
|
+
Always handle `MCPClientError`:
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
// ✅ Good: Specific error handling
|
|
495
|
+
try {
|
|
496
|
+
const result = await callMCPTool(...);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
if (error instanceof MCPClientError) {
|
|
499
|
+
output.printError(`Tool failed: ${error.message}`);
|
|
500
|
+
} else {
|
|
501
|
+
output.printError(`Unexpected error: ${String(error)}`);
|
|
502
|
+
}
|
|
503
|
+
return { success: false, exitCode: 1 };
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ❌ Bad: Generic error handling
|
|
507
|
+
try {
|
|
508
|
+
const result = await callMCPTool(...);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.error(error); // User sees raw error
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### 3. Input Validation
|
|
515
|
+
|
|
516
|
+
Validate inputs before calling tools:
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// ✅ Good: Validate first
|
|
520
|
+
if (!agentId) {
|
|
521
|
+
output.printError('Agent ID is required');
|
|
522
|
+
return { success: false, exitCode: 1 };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const result = await callMCPTool('agent/status', { agentId });
|
|
526
|
+
|
|
527
|
+
// ❌ Bad: Let tool fail
|
|
528
|
+
const result = await callMCPTool('agent/status', { agentId }); // Might be undefined
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### 4. Output Formatting
|
|
532
|
+
|
|
533
|
+
Keep display logic in CLI, not in tool results:
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
// ✅ Good: CLI formats output
|
|
537
|
+
const result = await callMCPTool('agent/list', { ... });
|
|
538
|
+
const displayData = result.agents.map(agent => ({
|
|
539
|
+
id: agent.id,
|
|
540
|
+
type: agent.agentType,
|
|
541
|
+
created: new Date(agent.createdAt).toLocaleString() // Format in CLI
|
|
542
|
+
}));
|
|
543
|
+
output.printTable({ data: displayData });
|
|
544
|
+
|
|
545
|
+
// ❌ Bad: Expect pre-formatted data from tool
|
|
546
|
+
const result = await callMCPTool('agent/list', { ... });
|
|
547
|
+
output.printTable({ data: result.formattedAgents }); // Tool shouldn't format
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### 5. Progressive Enhancement
|
|
551
|
+
|
|
552
|
+
Use feature detection for optional capabilities:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
// Check if tool supports feature
|
|
556
|
+
const metadata = getToolMetadata('agent/status');
|
|
557
|
+
const supportsMetrics = metadata?.inputSchema.properties?.includeMetrics;
|
|
558
|
+
|
|
559
|
+
const result = await callMCPTool('agent/status', {
|
|
560
|
+
agentId,
|
|
561
|
+
includeMetrics: supportsMetrics ? true : undefined
|
|
562
|
+
});
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Troubleshooting
|
|
566
|
+
|
|
567
|
+
### Tool Not Found
|
|
568
|
+
|
|
569
|
+
**Problem:** `MCPClientError: MCP tool not found: xyz/abc`
|
|
570
|
+
|
|
571
|
+
**Solutions:**
|
|
572
|
+
1. Check tool name spelling
|
|
573
|
+
2. Verify tool is registered in `mcp-client.ts`
|
|
574
|
+
3. Import tool from correct tools file
|
|
575
|
+
|
|
576
|
+
### Type Errors
|
|
577
|
+
|
|
578
|
+
**Problem:** TypeScript errors when calling `callMCPTool`
|
|
579
|
+
|
|
580
|
+
**Solutions:**
|
|
581
|
+
1. Provide correct type parameter: `callMCPTool<ResultType>(...)`
|
|
582
|
+
2. Match input schema from tool definition
|
|
583
|
+
3. Check tool's TypeScript interfaces
|
|
584
|
+
|
|
585
|
+
### Validation Errors
|
|
586
|
+
|
|
587
|
+
**Problem:** Tool execution fails with validation error
|
|
588
|
+
|
|
589
|
+
**Solutions:**
|
|
590
|
+
1. Use `validateToolInput()` before calling
|
|
591
|
+
2. Check tool's input schema requirements
|
|
592
|
+
3. Provide all required parameters
|
|
593
|
+
|
|
594
|
+
## Contributing
|
|
595
|
+
|
|
596
|
+
When adding new CLI commands:
|
|
597
|
+
|
|
598
|
+
1. Import `callMCPTool` and `MCPClientError`
|
|
599
|
+
2. Follow the standard CLI command pattern
|
|
600
|
+
3. Keep business logic in MCP tools
|
|
601
|
+
4. Add error handling for `MCPClientError`
|
|
602
|
+
5. Format output in CLI, not in tool
|
|
603
|
+
6. Add TypeScript types for tool results
|
|
604
|
+
7. Update this guide with new examples
|
|
605
|
+
|
|
606
|
+
## Related Documentation
|
|
607
|
+
|
|
608
|
+
- [REFACTORING_SUMMARY.md](./REFACTORING_SUMMARY.md) - Overview of refactoring
|
|
609
|
+
- [ADR-005: MCP-First API Design](/workspaces/claude-flow/docs/adr/ADR-005-mcp-first-api.md) - Architecture decision
|
|
610
|
+
- [MCP Tool Implementations](/workspaces/claude-flow/v3/mcp/tools/) - Tool source code
|
|
611
|
+
|
|
612
|
+
## Summary
|
|
613
|
+
|
|
614
|
+
The MCP Client provides a clean, type-safe way for CLI commands to call MCP tools while maintaining proper separation of concerns:
|
|
615
|
+
|
|
616
|
+
- **CLI**: User interaction & display
|
|
617
|
+
- **MCP Client**: Tool routing & error handling
|
|
618
|
+
- **MCP Tools**: Business logic & data management
|
|
619
|
+
|
|
620
|
+
This architecture ensures maintainability, testability, and consistency across all interfaces to the claude-flow system.
|