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
- Overview
- Integration Methods
- Method 1: In-SDK MCP Server (Recommended)
- Method 2: External MCP Server via Stdio
- Method 3: External MCP Server via NPM Package
- Environment Variables
- Testing Your MCP Server
- 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:
- claude-flow-sdk (In-SDK) - 6 tools
- claude-flow (NPM) - 101 tools
- flow-nexus (NPM) - 96 tools
- agentic-payments (NPM) - 10 tools
Integration Methods
Method 1: In-SDK MCP Server (Recommended)
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
-
Start with In-SDK for Simple Tools
- Faster development iteration
- Better type safety
- Easier debugging
-
Use External for Complex Operations
- System commands
- Heavy computations
- External APIs
-
Use NPM Packages for Distribution
- Share with community
- Version management
- Easy updates
-
Always Add Logging
logger.info('Tool executed', { tool, params, result }); -
Test Standalone First
- Test MCP server independently
- Then integrate with agentic-flow
-
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.ts2. Register in claudeAgent.ts3. 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 command3. Enable env var |
Next Steps:
- See
src/mcp/claudeFlowSdkServer.tsfor in-SDK example - See
src/agents/claudeAgent.ts:134-169for external examples - Read MCP Specification for protocol details