| layout | default |
|---|---|
| title | Chapter 2: The N8NMCPEngine - Core Integration Interface |
| parent | n8n-MCP Tutorial |
| nav_order | 2 |
Welcome back! In Chapter 1, we explored how the Model Context Protocol enables AI assistants to communicate with external tools. Now let's dive into the heart of n8n-MCP: the N8NMCPEngine class.
Think of the N8NMCPEngine as the main entrance to a grand library. It doesn't contain the books (that's the MCP server and tools), but it provides the doors, handles security, manages the crowds, and ensures everything runs smoothly. It's designed for developers who want to integrate n8n-MCP into larger applications.
Imagine you're building a SaaS platform where users can interact with AI assistants to manage their n8n workflows. Without the N8NMCPEngine, you'd have to:
- Handle HTTP request parsing
- Manage sessions manually
- Deal with authentication and multi-tenancy
- Implement health checks and monitoring
- Handle graceful shutdowns
The N8NMCPEngine abstracts all this complexity, letting you focus on your application's business logic.
The N8NMCPEngine provides just three main methods, but they're incredibly powerful:
class N8NMCPEngine {
constructor(options?: EngineOptions);
processRequest(req: Request, res: Response, instanceContext?: InstanceContext): Promise<void>;
healthCheck(): Promise<EngineHealth>;
shutdown(): Promise<void>;
}That's it! Three methods handle the entire integration. Let's explore each one.
This is where the magic happens. Every MCP request from an AI assistant flows through this method:
async processRequest(
req: Request,
res: Response,
instanceContext?: InstanceContext
): Promise<void>- Receives HTTP requests from AI assistants (via MCP clients)
- Delegates to the HTTP server for actual processing
- Supports instance context for multi-tenant scenarios
- Handles all MCP protocol communication
import { N8NMCPEngine, InstanceContext } from 'n8n-mcp';
import express from 'express';
const app = express();
const engine = new N8NMCPEngine();
// Basic usage (works with environment variables)
app.post('/mcp', async (req, res) => {
await engine.processRequest(req, res);
});
// With instance context (multi-tenant)
app.post('/instances/:instanceId/mcp', async (req, res) => {
// Get instance configuration from your database
const instance = await getInstanceConfig(req.params.instanceId);
const context: InstanceContext = {
n8nApiUrl: instance.n8nUrl,
n8nApiKey: instance.apiKey,
instanceId: instance.id,
metadata: { userId: req.userId }
};
await engine.processRequest(req, res, context);
});Production systems need monitoring. The healthCheck() method provides comprehensive system status:
async healthCheck(): Promise<EngineHealth>interface EngineHealth {
status: 'healthy' | 'unhealthy';
uptime: number; // Seconds since startup
sessionActive: boolean; // Is there an active MCP session?
memoryUsage: {
used: number; // MB of heap used
total: number; // MB of heap allocated
unit: string; // Always 'MB'
};
version: string; // n8n-MCP version
}// Health endpoint for load balancers and monitoring
app.get('/health', async (req, res) => {
try {
const health = await engine.healthCheck();
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
} catch (error) {
res.status(503).json({ status: 'unhealthy', error: error.message });
}
});
// Prometheus metrics endpoint
app.get('/metrics', async (req, res) => {
const health = await engine.healthCheck();
res.set('Content-Type', 'text/plain');
res.send(`
# HELP n8n_mcp_uptime_seconds Time since n8n-MCP started
# TYPE n8n_mcp_uptime_seconds gauge
n8n_mcp_uptime_seconds ${health.uptime}
# HELP n8n_mcp_memory_used_mb Memory used by n8n-MCP
# TYPE n8n_mcp_memory_used_mb gauge
n8n_mcp_memory_used_mb ${health.memoryUsage.used}
`);
});Proper shutdown is crucial for production systems. This method ensures clean resource cleanup:
async shutdown(): Promise<void>- Prevents data loss - Active operations complete
- Maintains session state - For stateful recovery
- Frees resources - Database connections, network sockets
- Enables zero-downtime deployments
// Handle termination signals
process.on('SIGTERM', async () => {
console.log('Received SIGTERM, shutting down gracefully...');
await engine.shutdown();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('Received SIGINT, shutting down gracefully...');
await engine.shutdown();
process.exit(0);
});The N8NMCPEngine is a thin wrapper around the SingleSessionHTTPServer. Let's trace through what happens when a request arrives:
flowchart TD
A[HTTP Request] --> B[N8NMCPEngine.processRequest]
B --> C[SingleSessionHTTPServer.handleRequest]
C --> D[Parse MCP Message]
D --> E[Route to Appropriate Handler]
E --> F[MCP Tools Execution]
F --> G[Database Queries]
F --> H[n8n API Calls]
G --> I[SQLite Response]
H --> J[n8n API Response]
I --> K[Format MCP Response]
J --> K
K --> L[Send HTTP Response]
L --> M[Client Receives Response]
- Single Responsibility: Engine handles integration, server handles MCP protocol
- Stateless Design: Each request is independent (session state managed separately)
- Flexible Configuration: Environment variables OR instance context
- Production Ready: Health checks, graceful shutdown, error handling
The engine accepts optional configuration for fine-tuning:
interface EngineOptions {
sessionTimeout?: number; // How long sessions can be idle
logLevel?: 'error' | 'warn' | 'info' | 'debug';
}These options are passed to underlying components, giving you control over behavior without complexity.
For platforms serving multiple customers:
class MultiTenantN8nMCP {
private engines = new Map<string, N8NMCPEngine>();
async getEngineForTenant(tenantId: string): Promise<N8NMCPEngine> {
if (!this.engines.has(tenantId)) {
// Create engine with tenant-specific configuration
const engine = new N8NMCPEngine();
this.engines.set(tenantId, engine);
}
return this.engines.get(tenantId)!;
}
async handleRequest(tenantId: string, req: Request, res: Response) {
const engine = await this.getEngineForTenant(tenantId);
// Get tenant's n8n configuration
const context = await this.getTenantContext(tenantId);
return engine.processRequest(req, res, context);
}
}For high-traffic scenarios:
class LoadBalancedN8nMCP {
private engines: N8NMCPEngine[] = [];
private currentIndex = 0;
constructor(poolSize = 4) {
for (let i = 0; i < poolSize; i++) {
this.engines.push(new N8NMCPEngine());
}
}
getNextEngine(): N8NMCPEngine {
const engine = this.engines[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.engines.length;
return engine;
}
async handleRequest(req: Request, res: Response, context?: InstanceContext) {
const engine = this.getNextEngine();
return engine.processRequest(req, res, context);
}
}The engine includes comprehensive error handling:
- Request Validation: Invalid requests are rejected with clear error messages
- Circuit Breaking: Failed n8n API calls don't crash the engine
- Logging: Structured logging for debugging and monitoring
- Graceful Degradation: Engine continues working even if some features fail
- Startup Time: < 100ms (minimal initialization)
- Memory Footprint: ~50MB base + session data
- Request Latency: 10-500ms depending on operation
- Concurrent Requests: Handles 100+ simultaneous connections
- Throughput: 1000+ requests/minute on modest hardware
const n8nMCPMiddleware = (engine: N8NMCPEngine) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
await engine.processRequest(req, res);
} catch (error) {
next(error);
}
};
};const n8nMCPPlugin = async (fastify: FastifyInstance, options: any) => {
const engine = new N8NMCPEngine(options);
fastify.post('/mcp', async (request, reply) => {
// Convert Fastify request to Express-like format
const req = request.raw as any;
const res = reply.raw as any;
await engine.processRequest(req, res);
});
};export async function handler(event: APIGatewayEvent) {
const engine = new N8NMCPEngine();
// Convert API Gateway event to Express-like objects
const req = createRequestFromEvent(event);
const res = createResponseFromEvent(event);
await engine.processRequest(req, res);
return formatResponseForAPIGateway(res);
}The engine is designed to be easily testable:
import { N8NMCPEngine } from 'n8n-mcp';
describe('N8NMCPEngine', () => {
let engine: N8NMCPEngine;
beforeEach(() => {
engine = new N8NMCPEngine();
});
it('should handle health checks', async () => {
const health = await engine.healthCheck();
expect(health.status).toBe('healthy');
expect(health.uptime).toBeGreaterThan(0);
});
it('should process MCP requests', async () => {
const req = createMockRequest();
const res = createMockResponse();
await engine.processRequest(req, res);
expect(res.statusCode).toBe(200);
});
});Congratulations! You now understand how the N8NMCPEngine serves as the clean integration point for n8n-MCP. It's deceptively simple on the surface but handles enormous complexity underneath.
In the next chapter, we'll explore the session management and HTTP server that makes this all possible.