claude-flow
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
733 lines (645 loc) âĸ 22.4 kB
text/typescript
import { getErrorMessage } from '../utils/error-handler.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
CallToolResult,
TextContent,
ImageContent,
EmbeddedResource,
} from '@modelcontextprotocol/sdk/types.js';
import * as fs from 'fs/promises';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { SparcMode, loadSparcModes } from './sparc-modes.js';
// Simple ID generation
function generateId(): string {
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
interface SparcContext {
memoryKey?: string;
parallel?: boolean;
timeout?: number;
workingDirectory?: string;
}
interface SwarmAgent {
id: string;
mode: string;
task: string;
status: 'pending' | 'active' | 'completed' | 'failed';
result?: any;
}
interface SwarmExecution {
id: string;
objective: string;
strategy: string;
mode: string;
agents: SwarmAgent[];
startTime: Date;
endTime?: Date;
status: 'active' | 'completed' | 'failed';
}
export class ClaudeCodeMCPWrapper {
private server: Server;
private sparcModes: Map<string, SparcMode> = new Map();
private swarmExecutions: Map<string, SwarmExecution> = new Map();
private claudeCodeMCP: any; // Reference to Claude Code MCP client
constructor() {
this.server = new Server({
name: 'claude-flow-wrapper',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
this.setupHandlers();
this.loadSparcModes();
}
private async loadSparcModes() {
try {
const modes = await loadSparcModes();
modes.forEach(mode => {
this.sparcModes.set(mode.name, mode);
});
} catch (error) {
console.error('Failed to load SPARC modes:', error);
}
}
private setupHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: await this.getTools(),
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) =>
this.handleToolCall(request.params.name, request.params.arguments || {})
);
}
private async getTools(): Promise<Tool[]> {
const tools: Tool[] = [];
// Add SPARC mode tools
for (const [name, mode] of this.sparcModes) {
tools.push({
name: `sparc_${name}`,
description: `Execute SPARC ${name} mode: ${mode.description}`,
inputSchema: {
type: 'object',
properties: {
task: {
type: 'string',
description: 'The task description for the SPARC mode to execute',
},
context: {
type: 'object',
description: 'Optional context or parameters for the task',
properties: {
memoryKey: {
type: 'string',
description: 'Memory key to store results',
},
parallel: {
type: 'boolean',
description: 'Enable parallel execution',
},
},
},
},
required: ['task'],
},
});
}
// Add meta tools
tools.push(
{
name: 'sparc_list',
description: 'List all available SPARC modes',
inputSchema: {
type: 'object',
properties: {
verbose: {
type: 'boolean',
description: 'Include detailed information',
},
},
},
},
{
name: 'sparc_swarm',
description: 'Coordinate multiple SPARC agents in a swarm',
inputSchema: {
type: 'object',
properties: {
objective: {
type: 'string',
description: 'The swarm objective',
},
strategy: {
type: 'string',
enum: ['research', 'development', 'analysis', 'testing', 'optimization', 'maintenance'],
description: 'Swarm execution strategy',
},
mode: {
type: 'string',
enum: ['centralized', 'distributed', 'hierarchical', 'mesh', 'hybrid'],
description: 'Coordination mode',
},
maxAgents: {
type: 'number',
description: 'Maximum number of agents',
default: 5,
},
},
required: ['objective', 'strategy'],
},
},
{
name: 'sparc_swarm_status',
description: 'Check status of running swarms and list created files',
inputSchema: {
type: 'object',
properties: {
swarmId: {
type: 'string',
description: 'Optional swarm ID to check specific swarm',
},
},
},
}
);
return tools;
}
private async handleToolCall(toolName: string, args: any): Promise<CallToolResult> {
try {
if (toolName.startsWith('sparc_')) {
return await this.handleSparcTool(toolName, args);
}
// Pass through to Claude Code MCP
return this.forwardToClaudeCode(toolName, args);
} catch (error) {
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
}
private async handleSparcTool(toolName: string, args: any): Promise<CallToolResult> {
const mode = toolName.replace('sparc_', '');
// Handle special tools
if (mode === 'list') {
return this.listModes(args.verbose);
}
if (mode === 'swarm') {
return this.handleSwarm(args);
}
if (mode === 'swarm_status') {
return this.getSwarmStatus(args.swarmId);
}
// Standard SPARC mode execution
const sparcMode = this.sparcModes.get(mode);
if (!sparcMode) {
throw new Error(`Unknown SPARC mode: ${mode}`);
}
// Execute the SPARC mode directly
try {
// Import the execution function dynamically to avoid circular dependencies
// const { executeSparcMode } = await import('../cli/mcp-stdio-server.js');
// TODO: Implement proper SPARC mode execution or fix import path
const executeSparcMode = (mode: string, task: string, tools: any[], context: any) => {
throw new Error('SPARC mode execution not yet implemented in wrapper');
};
const result = await executeSparcMode(
mode,
args.task,
sparcMode.tools || [],
args.context || {}
);
return {
content: [{
type: 'text',
text: result.output,
}],
};
} catch (error) {
return {
content: [{
type: 'text',
text: `Error executing SPARC ${mode}: ${error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
}
private buildEnhancedPrompt(mode: SparcMode, task: string, context?: SparcContext): string {
const parts: string[] = [];
// Add SPARC mode header
parts.push(`SPARC: ${mode.name}\n`);
parts.push(`## Mode Description\n${mode.description}\n`);
// Add available tools
if (mode.tools && mode.tools.length > 0) {
parts.push(`## Available Tools`);
mode.tools.forEach(tool => {
parts.push(`- **${tool}**: ${this.getToolDescription(tool)}`);
});
parts.push('');
}
// Add usage pattern
if (mode.usagePattern) {
parts.push(`## Usage Pattern\n\`\`\`javascript\n${mode.usagePattern}\n\`\`\`\n`);
}
// Add best practices
if (mode.bestPractices) {
parts.push(`## Best Practices`);
mode.bestPractices.forEach(practice => {
parts.push(`- ${practice}`);
});
parts.push('');
}
// Add integration capabilities
if (mode.integrationCapabilities) {
parts.push(`## Integration Capabilities\nThis mode integrates with:`);
mode.integrationCapabilities.forEach(capability => {
parts.push(`- ${capability}`);
});
parts.push('');
}
// Add instructions
if (mode.instructions) {
parts.push(`## Instructions\n${mode.instructions}\n`);
}
// Add the actual task
parts.push(`## TASK: ${task}\n`);
// Add SPARC methodology
parts.push(this.getSparcMethodology(mode.name, task, context));
// Add context if provided
if (context) {
if (context.memoryKey) {
parts.push(`**Memory Key:** \`${context.memoryKey}\``);
}
if (context.parallel) {
parts.push(`**Parallel Execution:** Enabled`);
}
if (context.workingDirectory) {
parts.push(`**Working Directory:** ${context.workingDirectory}`);
}
}
return parts.join('\n');
}
private getToolDescription(tool: string): string {
const descriptions: Record<string, string> = {
'TodoWrite': 'Create and manage task coordination',
'TodoRead': 'Monitor task progress and status',
'Task': 'Spawn and manage specialized agents',
'Memory': 'Store and retrieve coordination data',
'Bash': 'Execute system commands',
'Read': 'Read file contents',
'Write': 'Write files',
'Edit': 'Edit existing files',
'MultiEdit': 'Make multiple edits to a file',
'Glob': 'Search for files by pattern',
'Grep': 'Search file contents',
'WebSearch': 'Search the web',
'WebFetch': 'Fetch web content',
};
return descriptions[tool] || `${tool} tool`;
}
private getSparcMethodology(mode: string, task: string, context?: SparcContext): string {
return `
# đ¯ SPARC METHODOLOGY EXECUTION FRAMEWORK
You are operating in **SPARC ${mode} mode**. Follow the SPARC Workflow precisely:
## SPARC Workflow Steps
### 1ī¸âŖ SPECIFICATION - Clarify goals, scope, constraints
**Your Task:** ${task}
**Analysis Required:**
- Break down into clear, measurable objectives
- Identify all requirements and constraints
- Define acceptance criteria
- Never hard-code environment variables
**Use TodoWrite to capture specifications:**
\`\`\`javascript
TodoWrite([
{
id: "specification",
content: "Clarify goals, scope, and constraints for: ${task}",
status: "pending",
priority: "high"
},
{
id: "acceptance_criteria",
content: "Define clear acceptance criteria and success metrics",
status: "pending",
priority: "high"
}
]);
\`\`\`
### 2ī¸âŖ PSEUDOCODE - High-level logic with TDD anchors
**Design Approach:**
- Identify core functions and data structures
- Create TDD test anchors before implementation
- Map out component interactions
### 3ī¸âŖ ARCHITECTURE - Design extensible systems
**Architecture Requirements:**
- Clear service boundaries
- Define interfaces between components
- Design for extensibility and maintainability
- Mode-specific architecture: ${this.getModeSpecificArchitecture(mode)}
### 4ī¸âŖ REFINEMENT - Iterate with TDD and security
**Refinement Process:**
- TDD implementation cycles
- Security vulnerability checks (injection, XSS, CSRF)
- Performance optimization
- Code review and refactoring
- All files must be ⤠500 lines
### 5ī¸âŖ COMPLETION - Integrate and verify
**Completion Checklist:**
- [ ] All acceptance criteria met
- [ ] Tests passing (comprehensive test suite)
- [ ] Security review completed
- [ ] Documentation updated
- [ ] Results stored in Memory: \`sparc_${mode}_${Date.now()}\`
- [ ] No hard-coded secrets or env vars
- [ ] Proper error handling in all code paths
## đ Execution Configuration
**Mode:** ${mode}
**Strategy:** ${this.getModeStrategy(mode)}
**Memory Key:** \`sparc_${mode}_${Date.now()}\`
**Batch Operations:** ${context?.parallel ? 'Enabled' : 'Standard operations'}
**Primary Tools:** ${this.sparcModes.get(mode)?.tools?.join(', ') || 'Standard tools'}
## đ Must Block (Non-negotiable)
- Every file ⤠500 lines
- No hard-coded secrets or env vars
- All user inputs validated
- No security vulnerabilities
- Proper error handling in all paths
- Each subtask ends with completion check
## đ¯ IMMEDIATE ACTION REQUIRED
**START NOW with SPARC Step 1 - SPECIFICATION:**
1. Create comprehensive TodoWrite task breakdown following SPARC workflow
2. Set "specification" task to "in_progress"
3. Analyze requirements and define acceptance criteria
4. Store initial analysis in Memory: \`sparc_${mode}_${Date.now()}\`
**Remember:** You're in **${mode}** mode. Follow the SPARC workflow systematically:
Specification â Pseudocode â Architecture â Refinement â Completion
Use the appropriate tools for each phase and maintain progress in TodoWrite.`;
}
private getModeSpecificArchitecture(mode: string): string {
const architectures: Record<string, string> = {
'orchestrator': 'Design for parallel agent coordination with clear task boundaries',
'coder': 'Focus on clean code architecture with proper abstractions',
'researcher': 'Structure for data collection and analysis pipelines',
'tdd': 'Test-first design with comprehensive test coverage',
'architect': 'System-wide design patterns and component interactions',
'reviewer': 'Code quality gates and review checkpoints',
'debugger': 'Diagnostic and monitoring integration points',
'tester': 'Test framework integration and coverage analysis',
};
return architectures[mode] || 'Design for the specific mode requirements';
}
private getModeStrategy(mode: string): string {
const strategies: Record<string, string> = {
'orchestrator': 'Parallel coordination',
'coder': 'Iterative development',
'researcher': 'Deep analysis',
'tdd': 'Test-driven cycles',
'architect': 'System design',
'reviewer': 'Quality assurance',
'debugger': 'Systematic debugging',
'tester': 'Comprehensive validation',
};
return strategies[mode] || 'Mode-specific execution';
}
private listModes(verbose: boolean): CallToolResult {
const modes = Array.from(this.sparcModes.values());
if (verbose) {
const content = modes.map(mode => ({
name: mode.name,
description: mode.description,
tools: mode.tools,
bestPractices: mode.bestPractices,
}));
return {
content: [{
type: 'text',
text: JSON.stringify(content, null, 2),
}],
};
}
const list = modes.map(m => `- **${m.name}**: ${m.description}`).join('\n');
return {
content: [{
type: 'text',
text: `Available SPARC modes:\n\n${list}`,
}],
};
}
private async handleSwarm(args: any): Promise<CallToolResult> {
const { objective, strategy, mode = 'distributed', maxAgents = 5 } = args;
const swarmId = generateId();
// Plan swarm agents
const agents = this.planSwarmAgents(objective, strategy, maxAgents);
// Create swarm execution record
const execution: SwarmExecution = {
id: swarmId,
objective,
strategy,
mode,
agents,
startTime: new Date(),
status: 'active',
};
this.swarmExecutions.set(swarmId, execution);
// Launch agents based on coordination mode
if (mode === 'distributed' || mode === 'mesh') {
// Parallel execution
await Promise.all(agents.map(agent => this.launchSwarmAgent(agent, execution)));
} else {
// Sequential execution
for (const agent of agents) {
await this.launchSwarmAgent(agent, execution);
}
}
execution.status = 'completed';
execution.endTime = new Date();
return {
content: [{
type: 'text',
text: JSON.stringify({
swarmId,
objective,
strategy,
mode,
agentCount: agents.length,
status: 'launched',
message: 'Swarm coordination initiated',
}, null, 2),
}],
};
}
private planSwarmAgents(objective: string, strategy: string, maxAgents: number): SwarmAgent[] {
const agents: SwarmAgent[] = [];
// Strategy-based agent planning
switch (strategy) {
case 'research':
agents.push(
{ id: generateId(), mode: 'researcher', task: `Research: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'analyst', task: `Analyze findings for: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'documenter', task: `Document research results: ${objective}`, status: 'pending' }
);
break;
case 'development':
agents.push(
{ id: generateId(), mode: 'architect', task: `Design architecture: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'coder', task: `Implement: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'tester', task: `Test implementation: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'reviewer', task: `Review code: ${objective}`, status: 'pending' }
);
break;
case 'analysis':
agents.push(
{ id: generateId(), mode: 'analyst', task: `Analyze: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'optimizer', task: `Optimize based on analysis: ${objective}`, status: 'pending' }
);
break;
case 'testing':
agents.push(
{ id: generateId(), mode: 'tester', task: `Create test suite: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'debugger', task: `Debug issues: ${objective}`, status: 'pending' }
);
break;
case 'optimization':
agents.push(
{ id: generateId(), mode: 'analyst', task: `Performance analysis: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'optimizer', task: `Optimize: ${objective}`, status: 'pending' }
);
break;
case 'maintenance':
agents.push(
{ id: generateId(), mode: 'reviewer', task: `Code review: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'debugger', task: `Fix issues: ${objective}`, status: 'pending' },
{ id: generateId(), mode: 'documenter', task: `Update documentation: ${objective}`, status: 'pending' }
);
break;
}
// Limit to maxAgents
return agents.slice(0, maxAgents);
}
private async launchSwarmAgent(agent: SwarmAgent, execution: SwarmExecution): Promise<void> {
agent.status = 'active';
try {
// Use the SPARC mode handler
const result = await this.handleSparcTool(`sparc_${agent.mode}`, {
task: agent.task,
context: {
memoryKey: `swarm_${execution.id}_${agent.id}`,
parallel: execution.mode === 'distributed',
},
});
agent.status = 'completed';
agent.result = result;
} catch (error) {
agent.status = 'failed';
agent.result = { error: error instanceof Error ? error.message : String(error) };
}
}
private getSwarmStatus(swarmId?: string): CallToolResult {
if (swarmId) {
const execution = this.swarmExecutions.get(swarmId);
if (!execution) {
return {
content: [{
type: 'text',
text: `No swarm found with ID: ${swarmId}`,
}],
};
}
return {
content: [{
type: 'text',
text: JSON.stringify(execution, null, 2),
}],
};
}
// Return all swarms
const swarms = Array.from(this.swarmExecutions.values()).map(e => ({
id: e.id,
objective: e.objective,
status: e.status,
agentCount: e.agents.length,
startTime: e.startTime,
endTime: e.endTime,
}));
return {
content: [{
type: 'text',
text: JSON.stringify(swarms, null, 2),
}],
};
}
private async forwardToClaudeCode(toolName: string, args: any): Promise<CallToolResult> {
// For SPARC tools that were already handled, this shouldn't be called
// For other tools, we execute them using the existing logic
if (toolName === 'Task') {
// This is a SPARC task that's been enhanced with prompts
// Extract the mode from the description if possible
const modeMatch = args.description?.match(/SPARC (\w+)/);
if (modeMatch) {
const modeName = modeMatch[1];
const mode = this.sparcModes.get(modeName);
if (mode) {
// Execute using the existing SPARC execution logic
try {
const result = await executeSparcMode(
modeName,
args.prompt || '',
mode.tools || [],
{}
);
return {
content: [{
type: 'text',
text: result.output,
}],
};
} catch (error) {
return {
content: [{
type: 'text',
text: `Error executing SPARC ${modeName}: ${error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
}
}
}
// For non-SPARC tools, return a message
return {
content: [{
type: 'text',
text: `Tool ${toolName} is not available in this MCP server.`,
}],
isError: true,
};
}
async run() {
const transport = new StdioServerTransport();
// Log startup message
console.error('đ Claude-Flow MCP Server (Wrapper Mode)');
console.error('đĻ Using Claude Code MCP pass-through with SPARC prompt injection');
console.error('đ§ All SPARC tools available with enhanced AI capabilities');
console.error('âšī¸ To use legacy mode, set CLAUDE_FLOW_LEGACY_MCP=true');
console.error('');
await this.server.connect(transport);
}
}
// Run the server if this is the main module
if (import.meta.url === `file://${process.argv[1]}`) {
const wrapper = new ClaudeCodeMCPWrapper();
wrapper.run().catch(console.error);
}