mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-23 03:47:00 +09:00
* feat(mcp): make HTTP and session rate limits configurable The per-session token bucket (50, refill 5/s), the server-wide bucket (100/s, burst 200) and the HTTP per-IP limit (120 per 60s) were hardcoded, and HttpTransportConfig.rateLimit was unreachable because MCPServerConfig had no field for it. Add rateLimit and sessionRateLimit to MCPServerConfig and pass them to the transport and rate limiter. Expose them on `mcp start` as --rate-limit-per-ip, --rate-limit-window-ms, --rate-limit-per-session, --rate-limit-global-rps and --rate-limit-global-burst (env RUFLO_MCP_RATE_LIMIT_*), with bounded integer validation. A session burst is still capped by the server-wide burst, so raising the session limit above it needs --rate-limit-global-burst too. Defaults are unchanged. The HTTP 429 body now carries the JSON-RPC id of the rejected request instead of null. The CLI leaves the limits unset unless a flag or env value is given, so the @claude-flow/mcp defaults stay the single source. Co-Authored-By: RuFlo <ruv@ruv.net> * fix(mcp): drop the request id from the HTTP 429 body main now parses the JSON body after the rate limiter and auth gate, so req.body is never set when the limiter rejects and the id echo always fell back to null. Restore the static 429 message rather than parse bodies for clients that are being throttled. Also stub resolveMcpHttpAuthToken in the mcp start test mock, which `mcp start` now calls. * fix(mcp): refuse raised rate limits on a non-loopback host without a token * refactor(mcp): keep raisedRateLimits module-private --------- Co-authored-by: RuFlo <ruv@ruv.net>
@claude-flow/mcp
MCP 2025-11-25 Compliant - Standalone Model Context Protocol server implementation with full Resources, Prompts, and Tasks support
Features
- MCP 2025-11-25 Compliant - Full specification support
- High-Performance Server - <400ms startup time
- Multiple Transports - stdio, HTTP, WebSocket, in-process
- Resources - list, read, subscribe with caching
- Prompts - templates with arguments and embedded resources
- Tasks - Async operations with progress and cancellation
- Pagination - Cursor-based pagination for large datasets
- Connection Pooling - Max 10 connections, configurable
- Fast Tool Registry - O(1) lookup, <10ms registration
- Session Management - Timeout handling, authentication
- Security - CORS, Helmet, auth tokens
- Zero Dependencies - No @claude-flow/* dependencies
Installation
npm install @claude-flow/mcp
Quick Start
import { quickStart, defineTool } from '@claude-flow/mcp';
// Create and start server
const server = await quickStart({
transport: 'stdio',
name: 'My MCP Server',
});
// Register custom tools
server.registerTool(defineTool(
'greet',
'Greet a user',
{
type: 'object',
properties: {
name: { type: 'string', description: 'User name' }
},
required: ['name']
},
async ({ name }) => ({ message: `Hello, ${name}!` })
));
// Start listening
await server.start();
Transports
Stdio (Default)
import { createMCPServer } from '@claude-flow/mcp';
const server = createMCPServer({
transport: 'stdio',
name: 'Stdio Server',
}, logger);
await server.start();
HTTP
const server = createMCPServer({
transport: 'http',
host: 'localhost',
port: 3000,
corsEnabled: true,
corsOrigins: ['http://localhost:8080'],
auth: {
enabled: true,
method: 'token',
tokens: ['secret-token'],
},
}, logger);
await server.start();
// Server at http://localhost:3000
// WebSocket at ws://localhost:3000/ws
WebSocket
const server = createMCPServer({
transport: 'websocket',
host: 'localhost',
port: 3001,
maxConnections: 100,
heartbeatInterval: 30000,
}, logger);
await server.start();
// WebSocket at ws://localhost:3001/ws
Tool Registry
import { createToolRegistry, defineTool } from '@claude-flow/mcp';
const registry = createToolRegistry(logger);
// Register tool
registry.register({
name: 'calculate',
description: 'Perform calculations',
inputSchema: {
type: 'object',
properties: {
expression: { type: 'string' }
}
},
handler: async ({ expression }) => {
return { result: eval(expression) };
},
category: 'math',
tags: ['calculator'],
});
// Execute tool
const result = await registry.execute('calculate', { expression: '2 + 2' });
// Search tools
const mathTools = registry.getByCategory('math');
const stats = registry.getStats();
Session Management
import { createSessionManager } from '@claude-flow/mcp';
const sessions = createSessionManager(logger, {
maxSessions: 100,
sessionTimeout: 30 * 60 * 1000, // 30 minutes
});
// Create session
const session = sessions.createSession('http');
// Initialize with client info
sessions.initializeSession(session.id, {
protocolVersion: '2024-11-05', // MCP spec: YYYY-MM-DD string
capabilities: { tools: { listChanged: true } },
clientInfo: { name: 'Claude', version: '1.0' },
});
// Get metrics
const metrics = sessions.getSessionMetrics();
// { total: 1, active: 1, authenticated: 0, expired: 0 }
Connection Pool
import { createConnectionPool } from '@claude-flow/mcp';
const pool = createConnectionPool({
maxConnections: 10,
minConnections: 2,
idleTimeout: 30000,
acquireTimeout: 5000,
}, logger, 'http');
// Acquire connection
const conn = await pool.acquire();
// Use connection...
// Release back to pool
pool.release(conn);
// Get stats
const stats = pool.getStats();
// { totalConnections: 5, idleConnections: 4, busyConnections: 1, ... }
API Reference
Server
interface IMCPServer {
start(): Promise<void>;
stop(): Promise<void>;
registerTool(tool: MCPTool): boolean;
registerTools(tools: MCPTool[]): { registered: number; failed: string[] };
getHealthStatus(): Promise<{ healthy: boolean; error?: string; metrics?: Record<string, number> }>;
getMetrics(): MCPServerMetrics;
getSessions(): MCPSession[];
getSession(sessionId: string): MCPSession | undefined;
terminateSession(sessionId: string): boolean;
}
Tool
interface MCPTool<TInput = unknown, TOutput = unknown> {
name: string;
description: string;
inputSchema: JSONSchema;
handler: (input: TInput, context?: ToolContext) => Promise<TOutput>;
category?: string;
tags?: string[];
cacheable?: boolean;
cacheTTL?: number;
timeout?: number;
}
Transport
interface ITransport {
readonly type: TransportType;
start(): Promise<void>;
stop(): Promise<void>;
onRequest(handler: RequestHandler): void;
onNotification(handler: NotificationHandler): void;
sendNotification?(notification: MCPNotification): Promise<void>;
getHealthStatus(): Promise<TransportHealthStatus>;
}
Built-in Tools
The server automatically registers these system tools:
| Tool | Description |
|---|---|
system/info |
Get server information |
system/health |
Get health status |
system/metrics |
Get server metrics |
tools/list-detailed |
List all tools with details |
Resources (MCP 2025-11-25)
import { createMCPServer, createTextResource } from '@claude-flow/mcp';
const server = createMCPServer({ transport: 'stdio' }, logger);
const resourceRegistry = server.getResourceRegistry();
// Register a text resource
const { resource, handler } = createTextResource(
'file://readme.txt',
'README',
'Welcome to the application!',
{ mimeType: 'text/plain' }
);
resourceRegistry.registerResource(resource, handler);
// Register with custom handler
resourceRegistry.registerResource(
{
uri: 'db://users/{id}',
name: 'User Data',
description: 'Get user by ID',
mimeType: 'application/json',
},
async (uri) => {
const id = uri.split('/').pop();
const user = await fetchUser(id);
return [{ uri, mimeType: 'application/json', text: JSON.stringify(user) }];
}
);
// Subscribe to updates
resourceRegistry.subscribe('file://readme.txt', (uri, content) => {
console.log('Resource updated:', uri);
});
Prompts (MCP 2025-11-25)
import { createMCPServer, definePrompt, textMessage, interpolate } from '@claude-flow/mcp';
const server = createMCPServer({ transport: 'stdio' }, logger);
const promptRegistry = server.getPromptRegistry();
// Define a prompt with arguments
const codeReviewPrompt = definePrompt(
'code_review',
'Review code for quality and best practices',
async (args) => [
textMessage('user', `Please review this ${args.language} code:\n\n${args.code}`),
],
{
title: 'Code Review',
arguments: [
{ name: 'code', description: 'The code to review', required: true },
{ name: 'language', description: 'Programming language', required: false },
],
}
);
promptRegistry.register(codeReviewPrompt);
// Get prompt with arguments
const result = await promptRegistry.get('code_review', {
code: 'function hello() { return "world"; }',
language: 'JavaScript',
});
Tasks (MCP 2025-11-25)
import { createMCPServer } from '@claude-flow/mcp';
const server = createMCPServer({ transport: 'stdio' }, logger);
const taskManager = server.getTaskManager();
// Create a long-running task
const taskId = taskManager.createTask(async (reportProgress, signal) => {
for (let i = 0; i <= 100; i += 10) {
if (signal.aborted) throw new Error('Task cancelled');
reportProgress({
progress: i,
total: 100,
message: `Processing... ${i}%`,
});
await new Promise(r => setTimeout(r, 100));
}
return { success: true, processedItems: 100 };
});
// Get task status
const status = taskManager.getTask(taskId);
// Wait for completion
const result = await taskManager.waitForTask(taskId);
// Cancel a task
taskManager.cancelTask(taskId, 'User requested cancellation');
MCP Methods Supported
| Method | Description |
|---|---|
initialize |
Initialize connection |
tools/list |
List available tools |
tools/call |
Execute a tool |
resources/list |
List resources with pagination |
resources/read |
Read resource content |
resources/subscribe |
Subscribe to updates |
resources/unsubscribe |
Unsubscribe from updates |
prompts/list |
List prompts with pagination |
prompts/get |
Get prompt with arguments |
tasks/status |
Get task status |
tasks/cancel |
Cancel a running task |
completion/complete |
Auto-complete arguments |
logging/setLevel |
Set log level |
ping |
Keep-alive |
Performance Targets
| Metric | Target |
|---|---|
| Server startup | <400ms |
| Tool registration | <10ms |
| Tool execution overhead | <50ms |
| Connection acquire | <5ms |
| Connection release | <1ms |
Security
Authentication
const server = createMCPServer({
auth: {
enabled: true,
method: 'token',
tokens: ['your-secret-token'],
},
}, logger);
CORS
const server = createMCPServer({
transport: 'http',
corsEnabled: true,
corsOrigins: ['https://your-domain.com'],
}, logger);
Error Codes
const ErrorCodes = {
PARSE_ERROR: -32700,
INVALID_REQUEST: -32600,
METHOD_NOT_FOUND: -32601,
INVALID_PARAMS: -32602,
INTERNAL_ERROR: -32603,
SERVER_NOT_INITIALIZED: -32002,
AUTHENTICATION_REQUIRED: -32001,
};
License
MIT