tasq/node_modules/agentic-flow/docs/guides/ADDING-MCP-SERVERS.md

15 KiB

Adding Custom MCP Servers to Agentic Flow

This guide shows you how to integrate custom MCP (Model Context Protocol) servers into agentic-flow.


Table of Contents

  1. Overview
  2. Integration Methods
  3. Method 1: In-SDK MCP Server (Recommended)
  4. Method 2: External MCP Server via Stdio
  5. Method 3: External MCP Server via NPM Package
  6. Environment Variables
  7. Testing Your MCP Server
  8. Troubleshooting

Overview

Agentic Flow supports three ways to add custom MCP servers:

Method Type Performance Use Case
In-SDK In-process Fastest (<1ms) Custom tools, simple operations
Stdio Subprocess Fast (10-50ms) Local MCP servers, CLI tools
NPM Package Subprocess Fast (10-50ms) Published MCP packages

Current MCP Servers in Agentic Flow:

  1. claude-flow-sdk (In-SDK) - 6 tools
  2. claude-flow (NPM) - 101 tools
  3. flow-nexus (NPM) - 96 tools
  4. agentic-payments (NPM) - 10 tools

Integration Methods

Best for: Custom tools, simple operations, fastest performance

In-SDK servers run directly in the agent process without spawning subprocesses.

Step 1: Create MCP Server File

Create src/mcp/myCustomServer.ts:

import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';
import { logger } from '../utils/logger.js';

/**
 * Create an in-SDK MCP server with custom tools
 */
export const myCustomServer = createSdkMcpServer({
  name: 'my-custom-server',
  version: '1.0.0',

  tools: [
    // Tool 1: Simple calculator
    tool(
      'calculate',
      'Perform mathematical calculations',
      {
        operation: z.enum(['add', 'subtract', 'multiply', 'divide'])
          .describe('Mathematical operation'),
        a: z.number().describe('First number'),
        b: z.number().describe('Second number')
      },
      async ({ operation, a, b }) => {
        logger.info('Executing calculation', { operation, a, b });

        let result: number;
        switch (operation) {
          case 'add': result = a + b; break;
          case 'subtract': result = a - b; break;
          case 'multiply': result = a * b; break;
          case 'divide':
            if (b === 0) throw new Error('Division by zero');
            result = a / b;
            break;
        }

        return {
          operation,
          inputs: { a, b },
          result,
          message: `${a} ${operation} ${b} = ${result}`
        };
      }
    ),

    // Tool 2: Data storage
    tool(
      'store_data',
      'Store key-value data in memory',
      {
        key: z.string().describe('Storage key'),
        value: z.string().describe('Value to store'),
        namespace: z.string().optional().describe('Optional namespace')
      },
      async ({ key, value, namespace }) => {
        // Your storage logic here
        const fullKey = namespace ? `${namespace}:${key}` : key;

        // Store in memory, database, or filesystem
        logger.info('Storing data', { fullKey, value });

        return {
          success: true,
          key: fullKey,
          stored: value
        };
      }
    ),

    // Tool 3: API call wrapper
    tool(
      'fetch_data',
      'Fetch data from external API',
      {
        url: z.string().url().describe('API endpoint URL'),
        method: z.enum(['GET', 'POST']).default('GET').describe('HTTP method')
      },
      async ({ url, method }) => {
        logger.info('Fetching data', { url, method });

        try {
          const response = await fetch(url, { method });
          const data = await response.json();

          return {
            success: true,
            status: response.status,
            data
          };
        } catch (error: any) {
          return {
            success: false,
            error: error.message
          };
        }
      }
    )
  ]
});

Step 2: Register in Claude Agent

Edit src/agents/claudeAgent.ts:

// Add import at top
import { myCustomServer } from '../mcp/myCustomServer.js';

// Inside claudeAgent function, around line 128:
if (process.env.ENABLE_MY_CUSTOM_MCP === 'true') {
  mcpServers['my-custom-server'] = myCustomServer;
}

Step 3: Enable and Use

# Enable your custom MCP server
export ENABLE_MY_CUSTOM_MCP=true

# Run agent with your custom tools
npx agentic-flow --agent coder --task "Calculate 42 + 58 using the calculate tool"

Pros:

  • Fastest performance (in-process, <1ms latency)
  • No subprocess overhead
  • Full TypeScript type safety
  • Easy to debug

Cons:

  • ⚠️ Requires rebuild (npm run build)
  • ⚠️ Can't use external CLI tools directly

Method 2: External MCP Server via Stdio

Best for: Local MCP servers, custom CLI tools, existing MCP implementations

External stdio servers run as subprocesses and communicate via standard input/output.

Step 1: Create Standalone MCP Server

Create my-mcp-server/index.js:

#!/usr/bin/env node
// Standalone MCP server using FastMCP or @modelcontextprotocol/sdk

import { FastMCP } from 'fastmcp';

const server = new FastMCP({
  name: 'my-external-server',
  version: '1.0.0'
});

// Add tools
server.addTool({
  name: 'custom_search',
  description: 'Search custom database',
  parameters: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'Search query' },
      limit: { type: 'number', description: 'Max results' }
    },
    required: ['query']
  },
  execute: async ({ query, limit = 10 }) => {
    // Your search logic
    return {
      results: [
        { id: 1, title: 'Result 1', score: 0.95 },
        { id: 2, title: 'Result 2', score: 0.87 }
      ],
      count: 2
    };
  }
});

server.addTool({
  name: 'send_notification',
  description: 'Send notification via custom service',
  parameters: {
    type: 'object',
    properties: {
      message: { type: 'string' },
      channel: { type: 'string', enum: ['email', 'slack', 'sms'] }
    },
    required: ['message', 'channel']
  },
  execute: async ({ message, channel }) => {
    // Send notification
    console.error(`Sending ${channel} notification: ${message}`);
    return { success: true, channel, message };
  }
});

// Start stdio transport
server.listen({ transport: 'stdio' });

Make it executable:

chmod +x my-mcp-server/index.js

Step 2: Register in Claude Agent

Edit src/agents/claudeAgent.ts:

// Inside claudeAgent function, around line 169:
if (process.env.ENABLE_MY_EXTERNAL_MCP === 'true') {
  mcpServers['my-external-server'] = {
    type: 'stdio',
    command: 'node',
    args: ['/path/to/my-mcp-server/index.js'],
    env: {
      ...process.env,
      MY_SERVER_CONFIG: 'value'
    }
  };
}

Step 3: Enable and Use

# Enable external MCP server
export ENABLE_MY_EXTERNAL_MCP=true

# Run agent
npx agentic-flow --agent researcher --task "Search for 'AI trends' using custom_search"

Pros:

  • No rebuild required
  • Can use any language (Node, Python, Go, etc.)
  • Full access to CLI tools and system commands
  • Isolated process

Cons:

  • ⚠️ Subprocess overhead (10-50ms startup)
  • ⚠️ Requires absolute path or PATH setup
  • ⚠️ More complex debugging

Method 3: External MCP Server via NPM Package

Best for: Published MCP packages, shared tools, community servers

If your MCP server is published as an NPM package, integrate it like existing servers.

Step 1: Publish MCP Package

Create package.json for your MCP server:

{
  "name": "my-mcp-package",
  "version": "1.0.0",
  "bin": {
    "my-mcp": "./dist/index.js"
  },
  "main": "./dist/index.js",
  "scripts": {
    "mcp": "node dist/index.js"
  }
}

Publish to NPM:

npm publish my-mcp-package

Step 2: Register in Claude Agent

Edit src/agents/claudeAgent.ts:

// Inside claudeAgent function, around line 169:
if (process.env.ENABLE_MY_MCP_PACKAGE === 'true') {
  mcpServers['my-mcp-package'] = {
    type: 'stdio',
    command: 'npx',
    args: ['my-mcp-package@latest', 'mcp'],  // Or just 'my-mcp' if using bin
    env: {
      ...process.env,
      MY_MCP_API_KEY: process.env.MY_MCP_API_KEY || ''
    }
  };
}

Step 3: Enable and Use

# Enable MCP package
export ENABLE_MY_MCP_PACKAGE=true
export MY_MCP_API_KEY=your-api-key

# Run agent (will auto-install via npx)
npx agentic-flow --agent coder --task "Use tools from my-mcp-package"

Pros:

  • Easy distribution via NPM
  • Version management
  • Auto-installation via npx
  • Community sharing

Cons:

  • ⚠️ Requires NPM publishing
  • ⚠️ Network dependency for first install
  • ⚠️ Subprocess overhead

Environment Variables

Create environment variables to enable/disable your MCP servers:

# In-SDK servers
export ENABLE_CLAUDE_FLOW_SDK=true        # Built-in (6 tools)
export ENABLE_MY_CUSTOM_MCP=true          # Your custom in-SDK server

# External stdio servers
export ENABLE_CLAUDE_FLOW_MCP=true        # claude-flow (101 tools)
export ENABLE_FLOW_NEXUS_MCP=true         # flow-nexus (96 tools)
export ENABLE_AGENTIC_PAYMENTS_MCP=true   # agentic-payments (10 tools)
export ENABLE_MY_EXTERNAL_MCP=true        # Your external server
export ENABLE_MY_MCP_PACKAGE=true         # Your NPM package

Convention:

  • In-SDK: ENABLE_<NAME>_MCP=true
  • External: ENABLE_<NAME>_MCP=true
  • Config: <NAME>_API_KEY=xxx, <NAME>_CONFIG=xxx

Testing Your MCP Server

Test In-SDK Server

// Create test file: src/mcp/__tests__/myCustomServer.test.ts
import { myCustomServer } from '../myCustomServer.js';

describe('MyCustomServer', () => {
  it('should calculate correctly', async () => {
    const tools = myCustomServer.tools;
    const calculateTool = tools.find(t => t.name === 'calculate');

    const result = await calculateTool?.execute({
      operation: 'add',
      a: 10,
      b: 20
    });

    expect(result.result).toBe(30);
  });
});

Test External Server

# Test standalone server
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node my-mcp-server/index.js

# Test with agentic-flow
export ENABLE_MY_EXTERNAL_MCP=true
npx agentic-flow --agent coder --task "List all available tools"

Integration Test

# Enable your server
export ENABLE_MY_CUSTOM_MCP=true

# Run with verbose logging
npx agentic-flow --agent coder --task "Test my custom tools" --verbose

# Check logs
# Should see: "Registered MCP server: my-custom-server"

Code Reference: Existing Integrations

In-SDK Example: claude-flow-sdk

File: src/mcp/claudeFlowSdkServer.ts

export const claudeFlowSdkServer = createSdkMcpServer({
  name: 'claude-flow-sdk',
  version: '1.0.0',
  tools: [
    tool('memory_store', 'Store value in memory', {...}),
    tool('memory_retrieve', 'Retrieve value from memory', {...}),
    tool('swarm_init', 'Initialize swarm', {...}),
    // ... 6 tools total
  ]
});

Registration: src/agents/claudeAgent.ts:128-130

if (process.env.ENABLE_CLAUDE_FLOW_SDK === 'true') {
  mcpServers['claude-flow-sdk'] = claudeFlowSdkServer;
}

External Example: claude-flow

Registration: src/agents/claudeAgent.ts:134-145

if (process.env.ENABLE_CLAUDE_FLOW_MCP === 'true') {
  mcpServers['claude-flow'] = {
    type: 'stdio',
    command: 'npx',
    args: ['claude-flow@alpha', 'mcp', 'start'],
    env: {
      ...process.env,
      MCP_AUTO_START: 'true',
      PROVIDER: provider
    }
  };
}

External Example: flow-nexus

Registration: src/agents/claudeAgent.ts:147-157

if (process.env.ENABLE_FLOW_NEXUS_MCP === 'true') {
  mcpServers['flow-nexus'] = {
    type: 'stdio',
    command: 'npx',
    args: ['flow-nexus@latest', 'mcp', 'start'],
    env: {
      ...process.env,
      FLOW_NEXUS_AUTO_START: 'true'
    }
  };
}

Troubleshooting

MCP Server Not Loading

Problem: Agent doesn't see your MCP tools

Solutions:

# 1. Check environment variable
echo $ENABLE_MY_CUSTOM_MCP  # Should be "true"

# 2. Rebuild if in-SDK
npm run build

# 3. Check logs
npx agentic-flow --agent coder --task "test" --verbose
# Should see: "Registered MCP server: my-custom-server"

# 4. Test server directly
node my-mcp-server/index.js  # For external servers

Tools Not Executing

Problem: Tools registered but don't execute

Solutions:

# 1. Check tool name in task
# Correct: "Use the calculate tool to add 5 and 10"
# Wrong: "Calculate 5 + 10" (doesn't reference tool)

# 2. Check Zod schema validation
# Ensure parameters match schema exactly

# 3. Add logging
logger.info('Tool executed', { toolName, params, result });

Subprocess Failures

Problem: External MCP server crashes or times out

Solutions:

# 1. Test server standalone
node my-mcp-server/index.js
# Type: {"jsonrpc":"2.0","method":"tools/list","id":1}

# 2. Check command/args
# Ensure absolute path or npx for NPM packages

# 3. Increase timeout
mcpServers['my-server'] = {
  type: 'stdio',
  command: 'node',
  args: ['/path/to/server.js'],
  timeout: 60000  // 60 seconds
};

Type Errors

Problem: TypeScript errors with In-SDK tools

Solutions:

// Ensure correct imports
import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
import { z } from 'zod';

// Rebuild after changes
npm run build

// Check types
npx tsc --noEmit

Best Practices

  1. Start with In-SDK for Simple Tools

    • Faster development iteration
    • Better type safety
    • Easier debugging
  2. Use External for Complex Operations

    • System commands
    • Heavy computations
    • External APIs
  3. Use NPM Packages for Distribution

    • Share with community
    • Version management
    • Easy updates
  4. Always Add Logging

    logger.info('Tool executed', { tool, params, result });
    
  5. Test Standalone First

    • Test MCP server independently
    • Then integrate with agentic-flow
  6. Document Your Tools

    • Clear descriptions
    • Parameter documentation
    • Example usage

Summary

Quick Reference:

Need Method Steps
Simple custom tool In-SDK 1. Create src/mcp/myServer.ts
2. Register in claudeAgent.ts
3. npm run build
Local MCP server Stdio 1. Create standalone server
2. Register with command/args
3. Enable env var
Published package NPM 1. Publish to NPM
2. Register with npx command
3. Enable env var

Next Steps:

  • See src/mcp/claudeFlowSdkServer.ts for in-SDK example
  • See src/agents/claudeAgent.ts:134-169 for external examples
  • Read MCP Specification for protocol details

Need help? Open an issue on GitHub or check the MCP docs.