tasq/node_modules/agentic-flow/dist/mcp/fastmcp/servers/http-sse.js

382 lines
16 KiB
JavaScript

#!/usr/bin/env node
// FastMCP server with HTTP/SSE transport - All agentic-flow tools
// Accessible via HTTP at http://localhost:8080/mcp
// SSE endpoint at http://localhost:8080/sse
import { FastMCP } from 'fastmcp';
import { z } from 'zod';
import { execSync } from 'child_process';
console.error('🚀 Starting Agentic-Flow MCP Server (HTTP/SSE transport)...');
console.error('📦 Loading agentic-flow tools');
const server = new FastMCP({
name: 'agentic-flow-http',
version: '1.1.12'
});
// Tool: Run agentic-flow agent
server.addTool({
name: 'agentic_flow_agent',
description: 'Execute an agentic-flow agent with a specific task. Supports multiple providers (Anthropic, Gemini, OpenRouter, ONNX) and comprehensive configuration options.',
parameters: z.object({
agent: z.string().describe('Agent type (coder, researcher, analyst, etc.) - Use agentic_flow_list_agents to see all 66+ available agents'),
task: z.string().describe('Task description for the agent to execute'),
// Provider Configuration
model: z.string().optional().describe('Model to use (e.g., "claude-sonnet-4-5-20250929" for Anthropic, "meta-llama/llama-3.1-8b-instruct" for OpenRouter, or "Xenova/gpt2" for ONNX local models)'),
provider: z.enum(['anthropic', 'openrouter', 'onnx', 'gemini']).optional().describe('LLM provider: "anthropic" (default, highest quality, requires ANTHROPIC_API_KEY), "gemini" (free tier, requires GOOGLE_GEMINI_API_KEY), "openrouter" (99% cost savings, requires OPENROUTER_API_KEY), "onnx" (free local inference, no API key needed)'),
// API Configuration
anthropicApiKey: z.string().optional().describe('Anthropic API key (sk-ant-...) - overrides ANTHROPIC_API_KEY environment variable'),
openrouterApiKey: z.string().optional().describe('OpenRouter API key (sk-or-...) - overrides OPENROUTER_API_KEY environment variable'),
// Agent Behavior
stream: z.boolean().optional().default(false).describe('Enable streaming output (real-time response chunks)'),
temperature: z.number().min(0).max(1).optional().describe('Sampling temperature (0.0-1.0): lower = more focused/deterministic, higher = more creative/random. Default varies by agent.'),
maxTokens: z.number().positive().optional().describe('Maximum tokens in response (default: 4096). Controls output length.'),
// Directory Configuration
agentsDir: z.string().optional().describe('Custom agents directory path (default: .claude/agents) - for using custom agent definitions'),
// Output Options
outputFormat: z.enum(['text', 'json', 'markdown']).optional().describe('Output format: "text" (default, human-readable), "json" (structured data), "markdown" (formatted docs)'),
verbose: z.boolean().optional().default(false).describe('Enable verbose logging for debugging'),
// Execution Control
timeout: z.number().positive().optional().describe('Execution timeout in milliseconds (default: 300000 = 5 minutes)'),
retryOnError: z.boolean().optional().default(false).describe('Automatically retry on transient errors (rate limits, network issues)')
}),
execute: async ({ agent, task, model, provider, anthropicApiKey, openrouterApiKey, stream, temperature, maxTokens, agentsDir, outputFormat, verbose, timeout, retryOnError }) => {
try {
// Build command with all parameters
let cmd = `npx --yes agentic-flow --agent "${agent}" --task "${task}"`;
// Provider & Model
if (model)
cmd += ` --model "${model}"`;
if (provider)
cmd += ` --provider ${provider}`;
// API Keys (set as env vars)
const env = { ...process.env };
if (anthropicApiKey)
env.ANTHROPIC_API_KEY = anthropicApiKey;
if (openrouterApiKey)
env.OPENROUTER_API_KEY = openrouterApiKey;
// Agent Behavior
if (stream)
cmd += ' --stream';
if (temperature !== undefined)
cmd += ` --temperature ${temperature}`;
if (maxTokens)
cmd += ` --max-tokens ${maxTokens}`;
// Directories
if (agentsDir)
cmd += ` --agents-dir "${agentsDir}"`;
// Output
if (outputFormat)
cmd += ` --output ${outputFormat}`;
if (verbose)
cmd += ' --verbose';
// Execution
if (timeout)
cmd += ` --timeout ${timeout}`;
if (retryOnError)
cmd += ' --retry';
const result = execSync(cmd, {
encoding: 'utf-8',
maxBuffer: 10 * 1024 * 1024,
timeout: timeout || 300000,
env
});
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
agent,
provider: provider || 'anthropic',
output: result,
timestamp: new Date().toISOString()
}, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
agent,
error: error.message,
stderr: error.stderr?.toString(),
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
// Tool: List all agents
server.addTool({
name: 'agentic_flow_list_agents',
description: 'List all available agentic-flow agents (66+ total)',
parameters: z.object({
format: z.enum(['summary', 'detailed', 'json']).optional().default('summary')
}),
execute: async ({ format }) => {
try {
const result = execSync('npx --yes agentic-flow --list', {
encoding: 'utf-8',
maxBuffer: 5 * 1024 * 1024,
timeout: 30000
});
if (format === 'detailed') {
return {
content: [{
type: 'text',
text: result
}]
};
}
// Parse agent list
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 {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
count: agents.length,
agents: format === 'json' ? agents : agents.map(a => a.name),
timestamp: new Date().toISOString()
}, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
// Tool: Create custom agent
server.addTool({
name: 'agentic_flow_create_agent',
description: 'Create a new custom agent with specified name, description, and system prompt',
parameters: z.object({
name: z.string().describe('Agent name (will be converted to kebab-case, e.g., my-custom-agent)'),
description: z.string().describe('Agent description (what this agent does)'),
systemPrompt: z.string().describe('System prompt that defines the agent behavior and personality'),
category: z.string().optional().default('custom').describe('Category/folder to organize the agent (default: custom)'),
tools: z.array(z.string()).optional().describe('Optional list of tools this agent can use (e.g., ["web-search", "code-execution"])')
}),
execute: async ({ name, description, systemPrompt, category, tools }) => {
try {
const { writeFileSync, existsSync, mkdirSync } = await import('fs');
const { join } = await import('path');
// Convert to kebab-case
const agentName = name.toLowerCase().replace(/\s+/g, '-');
const agentsDir = join(process.cwd(), '.claude', 'agents', category || 'custom');
if (!existsSync(agentsDir)) {
mkdirSync(agentsDir, { recursive: true });
}
const markdown = `# ${name}
## Description
${description}
## System Prompt
${systemPrompt}
${tools && tools.length > 0 ? `## Tools\n${tools.map(t => `- ${t}`).join('\n')}\n` : ''}
## Usage
\`\`\`bash
npx agentic-flow --agent ${agentName} --task "Your task"
\`\`\`
---
*Created: ${new Date().toISOString()}*
`;
const filePath = join(agentsDir, `${agentName}.md`);
if (existsSync(filePath)) {
throw new Error(`Agent '${agentName}' already exists at ${filePath}`);
}
writeFileSync(filePath, markdown, 'utf8');
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
agent: agentName,
category: category || 'custom',
filePath,
message: `Agent '${agentName}' created successfully`,
timestamp: new Date().toISOString()
}, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
// Tool: Get agent info
server.addTool({
name: 'agentic_flow_agent_info',
description: 'Get detailed information about a specific agent including source, description, and system prompt preview',
parameters: z.object({
name: z.string().describe('Agent name to get information about')
}),
execute: async ({ name }) => {
try {
const result = execSync(`npx --yes agentic-flow agent info ${name}`, {
encoding: 'utf-8',
maxBuffer: 5 * 1024 * 1024,
timeout: 30000
});
return {
content: [{
type: 'text',
text: result
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
// Tool: Check for agent conflicts
server.addTool({
name: 'agentic_flow_check_conflicts',
description: 'Check for conflicts between package agents and local custom agents',
parameters: z.object({}),
execute: async () => {
try {
const result = execSync('npx --yes agentic-flow agent conflicts', {
encoding: 'utf-8',
maxBuffer: 5 * 1024 * 1024,
timeout: 30000
});
return {
content: [{
type: 'text',
text: result
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
// Tool: Optimize model selection
server.addTool({
name: 'agentic_flow_optimize_model',
description: 'Automatically select the optimal model for an agent and task based on priorities (quality, cost, speed, privacy)',
parameters: z.object({
agent: z.string().describe('Agent type (e.g., coder, researcher, reviewer)'),
task: z.string().describe('Task description'),
priority: z.enum(['quality', 'balanced', 'cost', 'speed', 'privacy']).optional().default('balanced')
.describe('Optimization priority: quality (best results), balanced (cost/quality), cost (cheapest), speed (fastest), privacy (local only)'),
max_cost: z.number().positive().optional().describe('Maximum cost per task in dollars (optional budget cap)')
}),
execute: async ({ agent, task, priority, max_cost }) => {
try {
let cmd = `npx --yes agentic-flow --agent ${agent} --task "${task}" --optimize --priority ${priority}`;
if (max_cost)
cmd += ` --max-cost ${max_cost}`;
const result = execSync(cmd, {
encoding: 'utf-8',
maxBuffer: 10 * 1024 * 1024,
timeout: 60000
});
return {
content: [{
type: 'text',
text: result
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: error.message,
timestamp: new Date().toISOString()
}, null, 2)
}],
isError: true
};
}
}
});
console.error('✅ Registered 6 tools successfully');
console.error('🌐 Starting HTTP/SSE transport...');
// Start with HTTP/SSE transport
server.start({
transportType: 'httpStream',
httpStream: {
port: 8080
}
}).then(() => {
console.error('✅ Agentic-Flow MCP Server running!');
console.error('📡 HTTP endpoint: http://localhost:8080/mcp');
console.error('📡 SSE endpoint: http://localhost:8080/sse');
console.error('💊 Health check: http://localhost:8080/health');
}).catch((error) => {
console.error('❌ Failed to start HTTP/SSE server:', error);
process.exit(1);
});
//# sourceMappingURL=http-sse.js.map