422 lines
17 KiB
JavaScript
422 lines
17 KiB
JavaScript
#!/usr/bin/env node
|
|
// Full FastMCP server with stdio transport - All 11 claude-flow-sdk tools
|
|
import { FastMCP } from 'fastmcp';
|
|
import { z } from 'zod';
|
|
import { execSync } from 'child_process';
|
|
console.error('🚀 Starting FastMCP Full Server (stdio transport)...');
|
|
console.error('📦 Loading 11 tools: memory (3), swarm (3), agent (5)');
|
|
// Create server
|
|
const server = new FastMCP({
|
|
name: 'fastmcp-stdio-full',
|
|
version: '1.0.0'
|
|
});
|
|
// Tool 1: Memory Store
|
|
server.addTool({
|
|
name: 'memory_store',
|
|
description: 'Store a value in persistent memory with optional namespace and TTL',
|
|
parameters: z.object({
|
|
key: z.string().min(1).describe('Memory key'),
|
|
value: z.string().describe('Value to store'),
|
|
namespace: z.string().optional().default('default').describe('Memory namespace'),
|
|
ttl: z.number().positive().optional().describe('Time-to-live in seconds')
|
|
}),
|
|
execute: async ({ key, value, namespace, ttl }) => {
|
|
try {
|
|
const cmd = [
|
|
'npx claude-flow@alpha memory store',
|
|
`"${key}"`,
|
|
`"${value}"`,
|
|
`--namespace "${namespace}"`,
|
|
ttl ? `--ttl ${ttl}` : ''
|
|
].filter(Boolean).join(' ');
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
key,
|
|
namespace,
|
|
size: value.length,
|
|
ttl,
|
|
timestamp: new Date().toISOString(),
|
|
message: 'Memory stored successfully'
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to store memory: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 2: Memory Retrieve
|
|
server.addTool({
|
|
name: 'memory_retrieve',
|
|
description: 'Retrieve a value from persistent memory',
|
|
parameters: z.object({
|
|
key: z.string().min(1).describe('Memory key'),
|
|
namespace: z.string().optional().default('default').describe('Memory namespace')
|
|
}),
|
|
execute: async ({ key, namespace }) => {
|
|
try {
|
|
const cmd = `npx claude-flow@alpha memory retrieve "${key}" --namespace "${namespace}"`;
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
key,
|
|
namespace,
|
|
value: result.trim(),
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to retrieve memory: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 3: Memory Search
|
|
server.addTool({
|
|
name: 'memory_search',
|
|
description: 'Search for keys matching a pattern in memory with wildcard support',
|
|
parameters: z.object({
|
|
pattern: z.string().min(1).describe('Search pattern (supports wildcards like * and ?)'),
|
|
namespace: z.string().optional().describe('Memory namespace to search in (searches all if not specified)'),
|
|
limit: z.number().positive().optional().default(10).describe('Maximum number of results to return (1-100)')
|
|
.refine((val) => val >= 1 && val <= 100, { message: 'Limit must be between 1 and 100' })
|
|
}),
|
|
execute: async ({ pattern, namespace, limit }) => {
|
|
try {
|
|
const cmd = [
|
|
'npx claude-flow@alpha memory search',
|
|
`"${pattern}"`,
|
|
namespace ? `--namespace "${namespace}"` : '',
|
|
`--limit ${limit}`
|
|
].filter(Boolean).join(' ');
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
pattern,
|
|
namespace: namespace || 'all',
|
|
limit,
|
|
results: result.trim(),
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to search memory: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 4: Swarm Init
|
|
server.addTool({
|
|
name: 'swarm_init',
|
|
description: 'Initialize a multi-agent swarm with specified topology and strategy',
|
|
parameters: z.object({
|
|
topology: z.enum(['mesh', 'hierarchical', 'ring', 'star'])
|
|
.describe('Swarm topology: mesh (peer-to-peer), hierarchical (tree), ring (circular), star (centralized)'),
|
|
maxAgents: z.number().positive().optional().default(8).describe('Maximum number of agents in the swarm (1-100)')
|
|
.refine((val) => val >= 1 && val <= 100, { message: 'maxAgents must be between 1 and 100' }),
|
|
strategy: z.enum(['balanced', 'specialized', 'adaptive']).optional().default('balanced')
|
|
.describe('Agent distribution strategy: balanced (equal), specialized (role-based), adaptive (dynamic)')
|
|
}),
|
|
execute: async ({ topology, maxAgents, strategy }) => {
|
|
try {
|
|
const cmd = `npx claude-flow@alpha swarm init --topology ${topology} --max-agents ${maxAgents} --strategy ${strategy}`;
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
topology,
|
|
maxAgents,
|
|
strategy,
|
|
message: 'Swarm initialized successfully',
|
|
details: result.trim(),
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to initialize swarm: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 5: Agent Spawn
|
|
server.addTool({
|
|
name: 'agent_spawn',
|
|
description: 'Spawn a new agent in the swarm with specified type and capabilities',
|
|
parameters: z.object({
|
|
type: z.enum(['researcher', 'coder', 'analyst', 'optimizer', 'coordinator'])
|
|
.describe('Agent type: researcher (data gathering), coder (implementation), analyst (analysis), optimizer (performance), coordinator (orchestration)'),
|
|
capabilities: z.array(z.string()).optional()
|
|
.describe('Specific capabilities for the agent (e.g., ["python", "testing", "documentation"])'),
|
|
name: z.string().optional().describe('Custom agent name/identifier')
|
|
}),
|
|
execute: async ({ type, capabilities, name }) => {
|
|
try {
|
|
const capStr = capabilities ? ` --capabilities "${capabilities.join(',')}"` : '';
|
|
const nameStr = name ? ` --name "${name}"` : '';
|
|
const cmd = `npx claude-flow@alpha agent spawn --type ${type}${capStr}${nameStr}`;
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
type,
|
|
capabilities: capabilities || [],
|
|
name: name || `${type}-${Date.now()}`,
|
|
message: 'Agent spawned successfully',
|
|
details: result.trim(),
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to spawn agent: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 6: Task Orchestrate
|
|
server.addTool({
|
|
name: 'task_orchestrate',
|
|
description: 'Orchestrate a complex task across the swarm with specified strategy and priority',
|
|
parameters: z.object({
|
|
task: z.string().min(1).describe('Task description or instructions for the swarm to execute'),
|
|
strategy: z.enum(['parallel', 'sequential', 'adaptive']).optional().default('adaptive')
|
|
.describe('Execution strategy: parallel (simultaneous), sequential (ordered), adaptive (dynamic based on task)'),
|
|
priority: z.enum(['low', 'medium', 'high', 'critical']).optional().default('medium')
|
|
.describe('Task priority level: low, medium, high, or critical'),
|
|
maxAgents: z.number().positive().optional().describe('Maximum number of agents to use for this task (1-10)')
|
|
.refine((val) => !val || (val >= 1 && val <= 10), { message: 'maxAgents must be between 1 and 10' })
|
|
}),
|
|
execute: async ({ task, strategy, priority, maxAgents }) => {
|
|
try {
|
|
const maxStr = maxAgents ? ` --max-agents ${maxAgents}` : '';
|
|
const cmd = `npx claude-flow@alpha task orchestrate "${task}" --strategy ${strategy} --priority ${priority}${maxStr}`;
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 300000 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
task,
|
|
strategy,
|
|
priority,
|
|
maxAgents: maxAgents || 'auto',
|
|
message: 'Task orchestrated successfully',
|
|
details: result.trim(),
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to orchestrate task: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 7: Agent Execute
|
|
server.addTool({
|
|
name: 'agent_execute',
|
|
description: 'Execute a specific agent with a task (equivalent to --agent CLI command)',
|
|
parameters: z.object({
|
|
agent: z.string().describe('Agent name to execute'),
|
|
task: z.string().describe('Task description'),
|
|
stream: z.boolean().optional().default(false).describe('Enable streaming output')
|
|
}),
|
|
execute: async ({ agent, task, stream }) => {
|
|
try {
|
|
const streamFlag = stream ? '--stream' : '';
|
|
const cmd = `npx agentic-flow --agent "${agent}" --task "${task}" ${streamFlag}`.trim();
|
|
const result = execSync(cmd, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 300000 });
|
|
return JSON.stringify({
|
|
success: true,
|
|
agent,
|
|
task: task.substring(0, 100),
|
|
output: result,
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to execute agent: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 8: Agent Parallel
|
|
server.addTool({
|
|
name: 'agent_parallel',
|
|
description: 'Run parallel mode with 3 agents (research, code review, data analysis)',
|
|
parameters: z.object({
|
|
topic: z.string().optional().describe('Research topic'),
|
|
diff: z.string().optional().describe('Code diff for review'),
|
|
dataset: z.string().optional().describe('Dataset hint'),
|
|
streaming: z.boolean().optional().default(false).describe('Enable streaming')
|
|
}),
|
|
execute: async ({ topic, diff, dataset, streaming }) => {
|
|
try {
|
|
const env = {
|
|
...process.env,
|
|
...(topic && { TOPIC: topic }),
|
|
...(diff && { DIFF: diff }),
|
|
...(dataset && { DATASET: dataset }),
|
|
...(streaming && { ENABLE_STREAMING: 'true' })
|
|
};
|
|
const result = execSync('npx agentic-flow', { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 300000, env });
|
|
return JSON.stringify({
|
|
success: true,
|
|
mode: 'parallel',
|
|
agents: ['research', 'code_review', 'data'],
|
|
output: result,
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to run parallel mode: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 9: Agent List
|
|
server.addTool({
|
|
name: 'agent_list',
|
|
description: 'List all available agents',
|
|
parameters: z.object({
|
|
format: z.enum(['summary', 'detailed', 'json']).optional().default('summary')
|
|
}),
|
|
execute: async ({ format }) => {
|
|
try {
|
|
const result = execSync('npx agentic-flow --list', { encoding: 'utf-8', maxBuffer: 5 * 1024 * 1024, timeout: 30000 });
|
|
if (format === 'detailed') {
|
|
return result;
|
|
}
|
|
const agents = [];
|
|
const lines = result.split('\n');
|
|
let currentCategory = '';
|
|
for (const line of lines) {
|
|
if (line.includes(':') && line.trim().endsWith(':')) {
|
|
currentCategory = line.replace(':', '').trim();
|
|
}
|
|
else if (line.trim().startsWith('•') || /^\s{2,}\w/.test(line)) {
|
|
const match = line.match(/^\s*[•\s]*(\S+)\s+(.+)$/);
|
|
if (match) {
|
|
agents.push({ name: match[1], description: match[2].trim(), category: currentCategory });
|
|
}
|
|
}
|
|
}
|
|
return JSON.stringify({
|
|
success: true,
|
|
count: agents.length,
|
|
agents,
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to list agents: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 10: Add Custom Agent
|
|
server.addTool({
|
|
name: 'agent_add',
|
|
description: 'Add a new custom agent defined in markdown',
|
|
parameters: z.object({
|
|
name: z.string().describe('Agent name (kebab-case)'),
|
|
description: z.string().describe('Agent description'),
|
|
systemPrompt: z.string().describe('System prompt'),
|
|
category: z.string().optional().default('custom').describe('Category'),
|
|
capabilities: z.array(z.string()).optional().describe('Capabilities')
|
|
}),
|
|
execute: async ({ name, description, systemPrompt, category, capabilities }) => {
|
|
try {
|
|
const { writeFileSync, existsSync, mkdirSync } = await import('fs');
|
|
const { join } = await import('path');
|
|
const agentsDir = join(process.cwd(), '.claude', 'agents', category || 'custom');
|
|
if (!existsSync(agentsDir))
|
|
mkdirSync(agentsDir, { recursive: true });
|
|
const markdown = `# ${name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
|
|
## Description
|
|
${description}
|
|
|
|
## System Prompt
|
|
${systemPrompt}
|
|
|
|
${capabilities && capabilities.length > 0 ? `## Capabilities\n${capabilities.map(c => `- ${c}`).join('\n')}\n` : ''}
|
|
|
|
## Usage
|
|
\`\`\`bash
|
|
npx agentic-flow --agent ${name} --task "Your task"
|
|
\`\`\`
|
|
|
|
---
|
|
*Generated: ${new Date().toISOString()}*
|
|
`;
|
|
const filePath = join(agentsDir, `${name}.md`);
|
|
if (existsSync(filePath))
|
|
throw new Error(`Agent '${name}' already exists`);
|
|
writeFileSync(filePath, markdown, 'utf8');
|
|
return JSON.stringify({
|
|
success: true,
|
|
agent: name,
|
|
category: category || 'custom',
|
|
filePath,
|
|
message: `Agent '${name}' created successfully`,
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to add agent: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
// Tool 11: Add Custom Command
|
|
server.addTool({
|
|
name: 'command_add',
|
|
description: 'Add a new custom command defined in markdown',
|
|
parameters: z.object({
|
|
name: z.string().describe('Command name (kebab-case)'),
|
|
description: z.string().describe('Command description'),
|
|
usage: z.string().describe('Usage example'),
|
|
parameters: z.array(z.object({
|
|
name: z.string(),
|
|
type: z.string(),
|
|
required: z.boolean(),
|
|
description: z.string()
|
|
})).optional().describe('Parameters'),
|
|
examples: z.array(z.string()).optional().describe('Examples')
|
|
}),
|
|
execute: async ({ name, description, usage, parameters, examples }) => {
|
|
try {
|
|
const { writeFileSync, existsSync, mkdirSync } = await import('fs');
|
|
const { join } = await import('path');
|
|
const commandsDir = join(process.cwd(), '.claude', 'commands');
|
|
if (!existsSync(commandsDir))
|
|
mkdirSync(commandsDir, { recursive: true });
|
|
const markdown = `# ${name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} Command
|
|
|
|
## Description
|
|
${description}
|
|
|
|
## Usage
|
|
\`\`\`bash
|
|
${usage}
|
|
\`\`\`
|
|
|
|
${parameters && parameters.length > 0 ? `## Parameters\n| Name | Type | Required | Description |\n|------|------|----------|-------------|\n${parameters.map(p => `| \`${p.name}\` | ${p.type} | ${p.required ? 'Yes' : 'No'} | ${p.description} |`).join('\n')}\n` : ''}
|
|
|
|
${examples && examples.length > 0 ? `## Examples\n\n${examples.map((ex, i) => `### Example ${i + 1}\n\`\`\`bash\n${ex}\n\`\`\`\n`).join('\n')}` : ''}
|
|
|
|
---
|
|
*Generated: ${new Date().toISOString()}*
|
|
`;
|
|
const filePath = join(commandsDir, `${name}.md`);
|
|
if (existsSync(filePath))
|
|
throw new Error(`Command '${name}' already exists`);
|
|
writeFileSync(filePath, markdown, 'utf8');
|
|
return JSON.stringify({
|
|
success: true,
|
|
command: name,
|
|
filePath,
|
|
message: `Command '${name}' created successfully`,
|
|
timestamp: new Date().toISOString()
|
|
}, null, 2);
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to add command: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
console.error('✅ Registered 11 tools successfully');
|
|
console.error('🔌 Starting stdio transport...');
|
|
// Start with stdio transport
|
|
server.start({ transportType: 'stdio' }).then(() => {
|
|
console.error('✅ FastMCP Full Server running on stdio');
|
|
}).catch((error) => {
|
|
console.error('❌ Failed to start server:', error);
|
|
process.exit(1);
|
|
});
|
|
//# sourceMappingURL=http-streaming-updated.js.map
|