v3/@claude-flow/cli/docs/IMPLEMENTATION_COMPLETE.md
Successfully refactored CLI commands in /workspaces/claude-flow/v3/@claude-flow/cli/src/commands/ to call MCP tools instead of containing hardcoded business logic, implementing ADR-005: MCP-First API Design.
/workspaces/claude-flow/v3/@claude-flow/cli/src/mcp-client.ts
MCPClientError/workspaces/claude-flow/v3/@claude-flow/cli/REFACTORING_SUMMARY.md
/workspaces/claude-flow/v3/@claude-flow/cli/MCP_CLIENT_GUIDE.md
/workspaces/claude-flow/v3/@claude-flow/cli/IMPLEMENTATION_COMPLETE.md (this file)
/workspaces/claude-flow/v3/@claude-flow/cli/src/commands/agent.ts
spawn command → calls agent/spawnlist command → calls agent/liststatus command → calls agent/statusstop command → calls agent/terminate/workspaces/claude-flow/v3/@claude-flow/cli/src/commands/swarm.ts
init command → calls swarm/init/workspaces/claude-flow/v3/@claude-flow/cli/src/commands/memory.ts
/workspaces/claude-flow/v3/@claude-flow/cli/src/commands/config.ts
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ - User interaction (prompts, confirmations) │
│ - Input validation & formatting │
│ - Output display (tables, colors, formatting) │
│ - Error messages & user feedback │
└──────────────────────────┬──────────────────────────────────┘
│ callMCPTool()
┌──────────────────────────▼──────────────────────────────────┐
│ MCP Client Layer │
│ - Tool registry & routing │
│ - Type-safe tool calling │
│ - Error wrapping (MCPClientError) │
│ - Input validation against schemas │
└──────────────────────────┬──────────────────────────────────┘
│ tool.handler()
┌──────────────────────────▼──────────────────────────────────┐
│ MCP Tools Layer │
│ - Business logic & data validation │
│ - Resource management (agents, swarms, memory) │
│ - State changes & persistence │
│ - External API calls │
└─────────────────────────────────────────────────────────────┘
// Core function
export async function callMCPTool<T = unknown>(
toolName: string,
input: Record<string, unknown> = {},
context?: Record<string, unknown>
): Promise<T>
// Utility functions
export function getToolMetadata(toolName: string)
export function listMCPTools(category?: string)
export function hasTool(toolName: string): boolean
export function getToolCategories(): string[]
export function validateToolInput(toolName: string, input: any)
// Error class
export class MCPClientError extends Error {
constructor(message: string, toolName: string, cause?: Error)
}
import { callMCPTool, MCPClientError } from '../mcp-client.js';
const command: Command = {
name: 'my-command',
action: async (ctx: CommandContext) => {
// 1. Gather input
const param = ctx.flags.param || await prompt();
// 2. Validate
if (!param) {
output.printError('Required');
return { success: false, exitCode: 1 };
}
// 3. Call MCP tool
try {
const result = await callMCPTool<ResultType>('tool/name', {
param
});
// 4. Display output
output.printSuccess('Done');
return { success: true, data: result };
} catch (error) {
// 5. Handle errors
if (error instanceof MCPClientError) {
output.printError(error.message);
}
return { success: false, exitCode: 1 };
}
}
};
agent spawn → agent/spawnagent list → agent/listagent status <id> → agent/statusagent stop <id> → agent/terminateswarm init → swarm/initswarm status → swarm/status (TODO)swarm scale → swarm/scale (TODO)memory store → memory/store (TODO)memory search → memory/search (TODO)memory list → memory/list (TODO)config load → config/load (TODO)config save → config/save (TODO)config validate → config/validate (TODO)MCPClientError class// Hardcoded business logic in CLI
const agentConfig = {
id: `agent-${Date.now()}`,
type: agentType,
status: 'initializing',
// ...lots of hardcoded logic
};
// Direct state mutation
agents.push(agentConfig);
return { success: true, data: agentConfig };
// Clean separation - call MCP tool
try {
const result = await callMCPTool('agent/spawn', {
agentType,
priority: 'normal'
});
output.printSuccess(`Spawned: ${result.agentId}`);
return { success: true, data: result };
} catch (error) {
if (error instanceof MCPClientError) {
output.printError(error.message);
}
return { success: false, exitCode: 1 };
}
describe('MCP Client', () => {
it('should call tools by name', async () => {
const result = await callMCPTool('agent/spawn', {
agentType: 'coder'
});
expect(result).toHaveProperty('agentId');
});
it('should throw for unknown tools', async () => {
await expect(
callMCPTool('unknown/tool', {})
).rejects.toThrow(MCPClientError);
});
it('should validate inputs', () => {
const validation = validateToolInput('agent/spawn', {});
expect(validation.valid).toBe(false);
expect(validation.errors).toContain('Missing required field: agentType');
});
});
describe('Agent commands', () => {
it('should spawn agent via MCP', async () => {
const result = await execute(['agent', 'spawn', '-t', 'coder']);
expect(result.success).toBe(true);
expect(result.data.agentType).toBe('coder');
});
});
status, scale, stopconst myCommand: Command = {
name: 'my-command',
description: '...',
options: [...]
};
import { callMCPTool, MCPClientError } from '../mcp-client.js';
action: async (ctx: CommandContext) => {
// Gather input
// Validate input
// Call MCP tool
// Display output
// Handle errors
}
try {
const result = await callMCPTool(...);
} catch (error) {
if (error instanceof MCPClientError) {
output.printError(error.message);
}
return { success: false, exitCode: 1 };
}
callMCPTool()MCPClientError| Component | Before | After | Change |
|---|---|---|---|
| agent.ts | ~542 | ~450 | -92 (17% reduction) |
| swarm.ts | ~624 | ~600 | -24 (4% reduction) |
| memory.ts | ~601 | ~601 | 0 (ready for refactor) |
| config.ts | ~452 | ~452 | 0 (ready for refactor) |
| mcp-client.ts | 0 | ~290 | +290 (new) |
The CLI refactoring successfully implements ADR-005 by creating a clean separation between:
This architecture provides:
All refactored commands follow a consistent pattern, making it easy for developers to add new commands or modify existing ones while keeping business logic centralized in MCP tools.
Status: Phase 1 Complete (Agent & Swarm Init commands refactored) Next: Phase 2 - Complete remaining swarm, memory, and config commands Date: 2026-01-04