Files
n8n-mcp/src/utils/mcp-client.ts
czlonkowski 1f8140c45c Implement n8n-MCP integration
This commit adds a complete integration between n8n workflow automation and the Model Context Protocol (MCP):

Features:
- MCP server that exposes n8n workflows as tools, resources, and prompts
- Custom n8n node for connecting to MCP servers from workflows
- Bidirectional bridge for data format conversion
- Token-based authentication and credential management
- Comprehensive error handling and logging
- Full test coverage for core components

Infrastructure:
- TypeScript/Node.js project setup with proper build configuration
- Docker support with multi-stage builds
- Development and production docker-compose configurations
- Installation script for n8n custom node deployment

Documentation:
- Detailed README with usage examples and API reference
- Environment configuration templates
- Troubleshooting guide

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-07 15:43:02 +00:00

150 lines
3.5 KiB
TypeScript

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
import {
CallToolRequest,
ListToolsRequest,
ListResourcesRequest,
ReadResourceRequest,
ListPromptsRequest,
GetPromptRequest,
CallToolResultSchema,
ListToolsResultSchema,
ListResourcesResultSchema,
ReadResourceResultSchema,
ListPromptsResultSchema,
GetPromptResultSchema,
} from '@modelcontextprotocol/sdk/types.js';
export interface MCPClientConfig {
serverUrl: string;
authToken?: string;
connectionType: 'http' | 'websocket' | 'stdio';
}
export class MCPClient {
private client: Client;
private config: MCPClientConfig;
private connected: boolean = false;
constructor(config: MCPClientConfig) {
this.config = config;
this.client = new Client(
{
name: 'n8n-mcp-client',
version: '1.0.0',
},
{
capabilities: {},
}
);
}
async connect(): Promise<void> {
if (this.connected) {
return;
}
let transport;
switch (this.config.connectionType) {
case 'websocket':
const wsUrl = this.config.serverUrl.replace(/^http/, 'ws');
transport = new WebSocketClientTransport(new URL(wsUrl));
break;
case 'stdio':
// For stdio, the serverUrl should be the command to execute
const [command, ...args] = this.config.serverUrl.split(' ');
transport = new StdioClientTransport({
command,
args,
});
break;
default:
throw new Error(`HTTP transport is not yet supported for MCP clients`);
}
await this.client.connect(transport);
this.connected = true;
}
async disconnect(): Promise<void> {
if (this.connected) {
await this.client.close();
this.connected = false;
}
}
async listTools(): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{ method: 'tools/list' } as ListToolsRequest,
ListToolsResultSchema
);
}
async callTool(name: string, args: any): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{
method: 'tools/call',
params: {
name,
arguments: args,
},
} as CallToolRequest,
CallToolResultSchema
);
}
async listResources(): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{ method: 'resources/list' } as ListResourcesRequest,
ListResourcesResultSchema
);
}
async readResource(uri: string): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{
method: 'resources/read',
params: {
uri,
},
} as ReadResourceRequest,
ReadResourceResultSchema
);
}
async listPrompts(): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{ method: 'prompts/list' } as ListPromptsRequest,
ListPromptsResultSchema
);
}
async getPrompt(name: string, args?: any): Promise<any> {
await this.ensureConnected();
return await this.client.request(
{
method: 'prompts/get',
params: {
name,
arguments: args,
},
} as GetPromptRequest,
GetPromptResultSchema
);
}
private async ensureConnected(): Promise<void> {
if (!this.connected) {
await this.connect();
}
}
}