Skip to main content

MCP Integration

The AG-Kit MCP integration provides seamless bidirectional conversion between AG-Kit’s tool system and the Model Context Protocol (MCP). This allows you to:
  • Connect to standard MCP servers and use their tools as AG-Kit BaseTool instances
  • Expose AG-Kit tools as MCP servers for external clients to consume
  • Support multiple transport protocols including stdio, HTTP, SSE, in-memory etc.

Overview

The MCP integration consists of several key components:
  • MCPClientTool: Wraps external MCP tools to work within AG-Kit
  • MCPToolkit: High-level toolkit for managing MCP tools
  • AGKitMCPServer: Exposes AG-Kit tools as a standard MCP server
  • MCPClientManager: Manages connections to multiple MCP servers

Quick Start

Using External MCP Tools in AG-Kit

import { MCPClientManager, MCPClientTool } from '@ag-kit/tools/mcp';

// Create a client manager
const clientManager = new MCPClientManager();

// Connect to an external MCP server
await clientManager.addServer('math-server', {
  name: 'math-client',
  version: '1.0.0',
  transport: {
    type: 'stdio',
    command: 'python',
    args: ['-m', 'math_mcp_server']
  }
});

// Create AG-Kit tools from MCP tools
const clientTools = clientManager.createClientTools('math-server');

// Use the tools in your AG-Kit application
for (const tool of clientTools) {
  const result = await tool.invoke({ a: 5, b: 3 });
  console.log(result.data);
}

Exposing AG-Kit Tools as MCP Server

import { AGKitMCPServer } from '@ag-kit/tools/mcp';
import { MyCustomTool } from './my-tools';

// Create your AG-Kit tools
const calculatorTool = new MyCustomTool();

// Create MCP server
const server = new AGKitMCPServer({
  name: 'ag-kit-mcp-server',
  version: '1.0.0',
  description: 'AG-Kit tools exposed via MCP'
});

// Register tools
server.registerTool(calculatorTool);

// Start server with stdio transport
await server.run({ type: 'stdio' });

Transport Protocols

Stdio Transport

The most common transport for MCP servers, using standard input/output.

Server Configuration

// Start server with stdio
await server.run({ type: 'stdio' });

Client Configuration

await clientManager.addServer('my-server', {
  name: 'my-client',
  version: '1.0.0',
  transport: {
    type: 'stdio',
    command: 'python',
    args: ['-m', 'my_mcp_server'],
    timeout: 10000
  }
});

HTTP Transport (StreamableHTTP)

For web-based MCP servers and clients.

Server Configuration

import express from 'express';

const app = express();

await server.run({
  type: 'streamableHttp',
  streamableHttpSetup: async (server, createTransport) => {
    app.post('/mcp', async (req, res) => {
      const transport = await createTransport({
        enableJsonResponse: true,
        sessionIdGenerator: () => crypto.randomUUID()
      });
      
      await transport.handleRequest(req, res, req.body);
    });
  }
});

app.listen(3000, () => {
  console.log('MCP server running on http://localhost:3000/mcp');
});

Client Configuration

await clientManager.addServer('http-server', {
  name: 'http-client',
  version: '1.0.0',
  transport: {
    type: 'streamableHttp',
    url: 'http://localhost:3000/mcp',
    timeout: 15000
  }
});

SSE Transport

Server-Sent Events transport for real-time communication.

Server Configuration

import express from 'express';

const app = express();

await server.run({
  type: 'sse',
  sseSetup: async (server, createTransport) => {
    let transport
    app.get('/mcp/sse', async (req, res) => {
      transport = await createTransport('/mcp/sse', res, {
        enableDnsRebindingProtection: false
      });
    });

    // 处理认证的 POST 请求
    app.post('/mcp/sse', async (req, res) => {
      if (transport) {
        await transport.handlePostMessage(req, res, req.body);
      } else {
        res.status(404).json({ error: 'No active SSE connection' });
      }
    });
    app.listen(3000);
  }
});

Client Configuration

await clientManager.addServer('sse-server', {
  name: 'sse-client',
  version: '1.0.0',
  transport: {
    type: 'sse',
    url: 'http://localhost:3000/mcp/sse'
  }
});

In-Memory Transport

For testing and same-process communication.

Server Configuration

const memoryId = 'my-memory-server';

await server.run({
  type: 'memory',
  memoryId
});

Client Configuration

await clientManager.addServer('memory-server', {
  name: 'memory-client',
  version: '1.0.0',
  transport: {
    type: 'memory',
    memoryId: 'my-memory-server'
  }
});

Core Examples

Example 1: Creating and Running an MCP Server

Expose AG-Kit tools as a standard MCP server:
import { AGKitMCPServer } from '@ag-kit/tools/mcp';
import { MyExistingTool } from './my-tools';

// Create MCP server instance
const server = new AGKitMCPServer({
  name: 'my-agkit-server',
  version: '1.0.0',
  description: 'AG-Kit tools exposed via MCP'
});

// Register existing AG-Kit tools
const myTool = new MyExistingTool();
server.registerTool(myTool);

// Register multiple tools with shared configuration
server.registerTools([tool1, tool2, tool3], {
  namePrefix: 'agkit_'  // All tools will be prefixed with 'agkit_'
});

// Start server with different transports
// Option 1: Stdio (most common)
await server.run({ type: 'stdio' });

// Option 2: HTTP
await server.run({
  type: 'streamableHttp',
  streamableHttpSetup: async (server, createTransport) => {
    app.post('/mcp', async (req, res) => {
      const transport = await createTransport();
      await transport.handleRequest(req, res, req.body);
    });
  }
});

// Option 3: Memory (for testing)
await server.run({ 
  type: 'memory', 
  memoryId: 'test-server' 
});

Example 2: Connecting to External MCP Servers

Use external MCP servers as AG-Kit tools:
import { MCPClientManager } from '@ag-kit/tools/mcp';

// Create client manager
const clientManager = new MCPClientManager();

// Connect to different types of MCP servers
// Stdio server
await clientManager.addServer('filesystem-server', {
  name: 'fs-client',
  version: '1.0.0',
  transport: {
    type: 'stdio',
    command: 'npx',
    args: ['@modelcontextprotocol/server-filesystem', './workspace']
  }
});

// HTTP server
await clientManager.addServer('api-server', {
  name: 'api-client',
  version: '1.0.0',
  transport: {
    type: 'streamableHttp',
    url: 'https://api.example.com/mcp'
  }
});

// Create AG-Kit tools from MCP tools
const allClientTools = clientManager.createClientTools();

// Create specific tool with custom name
const specificTool = clientManager.createClientTool(
  'filesystem-server',
  'read_file',
  'custom_file_reader'
);

// Use the tools in AG-Kit
const result = await specificTool.invoke({ path: 'README.md' });
console.log(result.data);

Example 3: Using MCPToolkit for Simplified Management

High-level toolkit approach for managing multiple MCP connections:
import { MCPToolkit, createMCPToolkit } from '@ag-kit/tools/mcp';

// Method 1: Create toolkit and add servers dynamically
const toolkit = new MCPToolkit('my-toolkit');

await toolkit.addServer('filesystem', {
  name: 'filesystem',
  version: '1.0.0',
  transport: { type: 'stdio', command: 'fs-server' }
});

await toolkit.addServer('database', {
  name: 'db-client',
  version: '1.0.0',
  transport: { type: 'streamableHttp', url: 'http://db-server/mcp' }
});

// Method 2: Create toolkit with object mapping (recommended)
// Matches standard MCP config format (e.g., Claude Desktop)
const toolkit2 = await createMCPToolkit({
  filesystem: {
    command: 'fs-server'  // Direct transport config
  },
  database: {
    url: 'http://db-server/mcp'  // Direct transport config
  }
}, 'multi-server-toolkit');

// Method 3: Create toolkit with array format (backward compatible)
const toolkit3 = await createMCPToolkit([
  {
    id: 'filesystem',
    config: {
      name: 'fs-client',
      version: '1.0.0',
      transport: { type: 'stdio', command: 'fs-server' }
    }
  },
  {
    id: 'database',
    config: {
      name: 'db-client',
      version: '1.0.0',
      transport: { type: 'streamableHttp', url: 'http://db-server/mcp' }
    }
  }
], 'multi-server-toolkit');

// Access tools by server
const fsTools = toolkit.getServerTools('filesystem');
const dbTools = toolkit.getServerTools('database');

// Access all tools
const allTools = toolkit.getTools();

// Refresh connections
await toolkit.refresh();

// Cleanup when done
await toolkit.cleanup();

Example 4: Memory Transport for Testing

Use memory transport for testing MCP integrations:
import { AGKitMCPServer, MCPClientManager } from '@ag-kit/tools/mcp';

// Test setup
const memoryId = 'test-memory-transport';
const serverInfo ={
  name: 'test-server',
  version: '1.0.0'
};

// Create server
const server = new AGKitMCPServer(serverInfo);

server.registerTool(new MyTestTool());
await server.run({ type: 'memory', memoryId });

// Create client
const clientManager = new MCPClientManager();
await clientManager.addServer(serverInfo.name, {
  name: 'test-client',
  version: '1.0.0',
  transport: { type: 'memory', memoryId }
});

// Test tool execution
const tools = clientManager.createClientTools();
const testTool = tools[0];
const result = await testTool.invoke({ test: 'data' });

// Cleanup
await clientManager.disconnectAll();
await server.stop();

Example 5: Advanced Connection Management

Handle connection options and error scenarios:
import { MCPClientManager } from '@ag-kit/tools/mcp';

const clientManager = new MCPClientManager();

// Add server with connection options
await clientManager.addServer('reliable-server', {
  name: 'reliable-client',
  version: '1.0.0',
  transport: {
    type: 'stdio',
    command: 'external-server',
    timeout: 10000
  }
}, {
  autoReconnect: true,
  reconnectDelay: 5000,
  maxReconnectAttempts: 3,
  heartbeatInterval: 30000
});

// Handle pre-connected clients
const preConnectedClient = new Client(/* ... */);
await preConnectedClient.connect(transport);

await clientManager.addServer('pre-connected', {
  name: 'pre-connected-client',
  version: '1.0.0',
  client: preConnectedClient,
  onReconnectNeeded: async (serverId, config) => {
    // Custom reconnection logic
    const newClient = new Client(/* ... */);
    await newClient.connect(/* new transport */);
    return newClient;
  }
});

// Monitor connection status
setInterval(() => {
  const isConnected = clientManager.isServerConnected('reliable-server');
  console.log(`Server connected: ${isConnected}`);
}, 5000);

Example 6: Event Handling and Monitoring

Monitor MCP operations with event listeners:
import { AGKitMCPServer, MCPClientManager } from '@ag-kit/tools/mcp';

// Server event monitoring
const server = new AGKitMCPServer({
  name: 'monitored-server',
  version: '1.0.0'
});

server.addEventListener((event) => {
  switch (event.type) {
    case 'connected':
      console.log(`Client connected: ${event.clientName}`);
      break;
    case 'tool_called':
      console.log(`Tool ${event.toolName} called with:`, event.arguments);
      break;
    case 'tool_result':
      console.log(`Tool ${event.toolName} returned:`, event.result);
      break;
    case 'error':
      console.error(`Server error in ${event.context}:`, event.error);
      break;
  }
});

// Client event monitoring
const clientManager = new MCPClientManager();

clientManager.addEventListener((event) => {
  switch (event.type) {
    case 'connected':
      console.log(`Connected to server: ${event.clientName}`);
      break;
    case 'tool_discovered':
      console.log(`Discovered tool: ${event.tool.name}`);
      break;
    case 'disconnected':
      console.log(`Disconnected from: ${event.clientName}`);
      if (event.reason) {
        console.log(`Reason: ${event.reason}`);
      }
      break;
    case 'error':
      console.error(`Client error:`, event.error);
      break;
  }
});

Advanced Configuration

Tool Configuration

Customize how AG-Kit tools are exposed via MCP:
// Register tool with custom configuration
server.registerTool(calculatorTool, {
  namePrefix: 'math_',           // Prefix tool name
  description: 'Custom calculator' // Override description
});

// Register multiple tools with shared config
server.registerTools([tool1, tool2, tool3], {
  namePrefix: 'utils_'
});

Client Tool Configuration

Customize MCP client tools:
const clientTool = new MCPClientTool(
  mcpClient,
  toolMetadata,
  {
    name: 'custom_tool_name',     // Custom AG-Kit name
    timeout: 30000,               // Call timeout
    retries: 3                    // Retry attempts
  },
  {
    includeMetadata: true,        // Include execution metadata
    transformInput: (input) => {  // Transform input before MCP call
      return { ...input, timestamp: Date.now() };
    },
    transformOutput: (output) => { // Transform output after MCP call
      return { result: output, processed: true };
    },
    errorHandler: (error) => {    // Custom error handling
      return new ToolResult({
        success: false,
        error: `Custom error: ${error.message}`
      });
    }
  }
);

Connection Management

Advanced connection options:
await clientManager.addServer('reliable-server', config, {
  autoReconnect: true,           // Auto-reconnect on failure
  reconnectDelay: 5000,          // Delay between reconnect attempts
  maxReconnectAttempts: 5,       // Maximum reconnect attempts
  heartbeatInterval: 30000       // Heartbeat interval
});

// Handle pre-connected clients
const preConnectedClient = new Client(/* ... */);
await preConnectedClient.connect(transport);

await clientManager.addServer('pre-connected', {
  name: 'pre-connected-client',
  version: '1.0.0',
  client: preConnectedClient,
  onReconnectNeeded: async (serverId, config) => {
    // Handle reconnection for pre-connected clients
    const newClient = new Client(/* ... */);
    await newClient.connect(/* new transport */);
    return newClient;
  }
});

Event Handling

Monitor MCP operations:
// Server events
server.addEventListener((event) => {
  switch (event.type) {
    case 'connected':
      console.log(`Client connected: ${event.clientName}`);
      break;
    case 'tool_called':
      console.log(`Tool called: ${event.toolName}`, event.arguments);
      break;
    case 'tool_result':
      console.log(`Tool result: ${event.toolName}`, event.result);
      break;
    case 'error':
      console.error(`Error in ${event.context}:`, event.error);
      break;
  }
});

// Client events
clientManager.addEventListener((event) => {
  switch (event.type) {
    case 'connected':
      console.log(`Connected to server: ${event.clientName}`);
      break;
    case 'tool_discovered':
      console.log(`Tool discovered: ${event.tool.name}`);
      break;
    case 'disconnected':
      console.log(`Disconnected from server: ${event.clientName}`);
      break;
  }
});

Schema Conversion

AG-Kit automatically converts between Zod schemas and MCP JSON schemas:

Zod to MCP Schema

import { z } from 'zod';
import { zodSchemaToMCPSchema } from '@ag-kit/tools/mcp';

const zodSchema = z.object({
  name: z.string().describe('User name'),
  age: z.number().min(0).max(150),
  email: z.string().email().optional(),
  tags: z.array(z.string()),
  preferences: z.record(z.string(), z.any())
});

const mcpSchema = zodSchemaToMCPSchema(zodSchema);
// Results in MCP-compatible JSON schema

MCP to Zod Schema

// MCPClientTool automatically converts MCP schemas to Zod
const mcpToolMetadata = {
  name: 'user_tool',
  inputSchema: {
    type: 'object',
    properties: {
      name: { type: 'string' },
      age: { type: 'number' }
    },
    required: ['name', 'age']
  }
};

const clientTool = new MCPClientTool(client, mcpToolMetadata);
// Tool now has Zod schema for AG-Kit compatibility

Error Handling

Server Error Handling

const server = new AGKitMCPServer({
  name: 'error-handling-server',
  version: '1.0.0',
  errorHandling: 'return_error', // or 'throw'
  transformOutput: (output) => {
    // Transform output before sending
    return output;
  }
});

// Handle tool errors gracefully
class SafeTool extends BaseTool {
  protected async _invoke(input: any): Promise<ToolResult> {
    try {
      // Tool logic here
      return new ToolResult({ success: true, data: result });
    } catch (error) {
      return new ToolResult({
        success: false,
        error: `Tool execution failed: ${error.message}`
      });
    }
  }
}

Client Error Handling

const clientTool = new MCPClientTool(
  client,
  metadata,
  { retries: 3, timeout: 10000 },
  {
    errorHandler: (error) => {
      // Custom error handling
      if (error.message.includes('timeout')) {
        return new ToolResult({
          success: false,
          error: 'Operation timed out, please try again'
        });
      }
      
      return new ToolResult({
        success: false,
        error: `MCP tool error: ${error.message}`
      });
    }
  }
);

API Reference

For complete API documentation including all interfaces, types, and detailed method signatures, see the MCP API Reference. AGKitMCPServer - Exposes AG-Kit tools as a standard MCP server
  • registerTool(tool: BaseTool, config?: MCPToolConfig): MCPToolMetadata - Register a single tool
  • registerTools(tools: BaseTool[], config?: MCPToolConfig): MCPToolMetadata[] - Register multiple tools
  • unregisterTool(name: string): boolean - Remove a tool from the server
  • run(transportConfig: MCPTransportConfig): Promise<void> - Start the server with specified transport
  • stop(): Promise<void> - Stop the server and cleanup resources
  • callTool(name: string, args: Record<string, any>): Promise<CallToolResult> - Execute a tool directly
  • listTools() - Get list of all registered tools
  • isServerRunning(): boolean - Check if server is running
  • getStats(): object - Get server statistics
MCPClientManager - Manages connections to external MCP servers
  • addServer(serverId: string, config: MCPClientConfig, options?: MCPConnectionOptions): Promise<void> - Connect to an MCP server
  • disconnectServer(serverId: string): Promise<void> - Disconnect from a specific server
  • disconnectAll(): Promise<void> - Disconnect from all servers
  • createClientTools(serverId?: string): MCPClientTool[] - Create AG-Kit tool wrappers
  • createClientTool(serverId: string, toolName: string, agKitToolName?: string): MCPClientTool - Create specific tool wrapper
  • callTool(serverId: string, toolName: string, args: any): Promise<any> - Call an MCP tool directly
  • isServerConnected(serverId: string): boolean - Check connection status
  • getStats(): object - Get client manager statistics
MCPClientTool - Wraps external MCP tools for AG-Kit compatibility
  • invoke(input: any, context?: ToolExecutionContext): Promise<ToolResult> - Execute the MCP tool
  • getMCPMetadata(): MCPToolMetadata - Get original MCP tool metadata
  • isConnected(): boolean - Check if underlying client is connected
  • updateConfig(newConfig: Partial<MCPToolConfig>): void - Update tool configuration
MCPToolkit - High-level toolkit for managing MCP tools
  • addServer(serverId: string, config: MCPClientConfig): Promise<void> - Add an MCP server
  • removeServer(serverId: string): Promise<void> - Remove a server
  • getConnectedServers(): string[] - Get list of connected servers
  • getServerTools(serverId: string): MCPClientTool[] - Get tools from specific server
  • refresh(): Promise<void> - Refresh all connections
  • cleanup(): Promise<void> - Cleanup all resources
  • getClientManager(): MCPClientManager - Get underlying client manager
See the complete API documentation for detailed interface definitions, type information, utility functions, and advanced configuration options.

Best Practices

  1. Use appropriate transports: stdio for CLI tools, HTTP for web services, memory for testing
  2. Handle errors gracefully: Implement proper error handling and retry logic
  3. Monitor connections: Use event listeners to track connection status
  4. Clean up resources: Always call cleanup methods when done
  5. Test thoroughly: Use memory transport for comprehensive testing
  6. Schema validation: Ensure your schemas are compatible between Zod and MCP JSON Schema
  7. Connection management: Use connection options for reliable production deployments

Troubleshooting

Common Issues

  1. Connection timeouts: Increase timeout values in transport configuration
  2. Schema conversion errors: Ensure Zod schemas use supported types
  3. Tool not found: Check tool registration and naming
  4. Transport errors: Verify transport configuration and server availability
  5. Memory leaks: Always clean up connections and event listeners

Debug Mode

Enable logging for debugging:
const server = new AGKitMCPServer({
  name: 'debug-server',
  version: '1.0.0',
  enableLogging: true // Enable debug logging
});
For more detailed examples and advanced usage patterns, see the MCP API Reference section.