mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-20 01:13:07 +00:00
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>
This commit is contained in:
150
src/utils/mcp-client.ts
Normal file
150
src/utils/mcp-client.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user