n8n-nodes-mcp-flex
Version:
Enhanced MCP nodes for n8n with flexible parameter handling and improved AI Agent integration
264 lines (263 loc) • 9.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.McpClient = void 0;
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
const n8n_workflow_1 = require("n8n-workflow");
class McpClient {
constructor() {
this.description = {
displayName: 'MCP Client Flex',
name: 'mcpClient',
icon: 'fa:exchange-alt',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Interact with Model Context Protocol (MCP) servers with flexible parameter handling',
defaults: {
name: 'MCP Client',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'mcpClientApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Call Tool',
value: 'callTool',
action: 'Call a tool',
},
],
default: 'callTool',
},
{
displayName: 'Tool Name',
name: 'toolName',
type: 'string',
required: true,
default: '',
description: 'Name of the tool to call',
},
{
displayName: 'Parameter Mode',
name: 'parameterMode',
type: 'options',
options: [
{
name: 'JSON String (Original)',
value: 'json',
},
{
name: 'Individual Fields',
value: 'fields',
},
{
name: 'Auto (Try Fields First)',
value: 'auto',
},
],
default: 'auto',
description: 'How to provide parameters to the tool',
},
{
displayName: 'Parameters (JSON)',
name: 'parameters',
type: 'json',
default: '{}',
displayOptions: {
show: {
parameterMode: ['json', 'auto'],
},
},
description: 'Parameters to pass to the tool as JSON object',
},
{
displayName: 'Calendar Href',
name: 'calendarHref',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'Calendar href parameter',
},
{
displayName: 'Summary/Title',
name: 'summary',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'Event summary or title',
},
{
displayName: 'Start DateTime',
name: 'startDateTime',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'Start date and time (ISO format)',
},
{
displayName: 'End DateTime',
name: 'endDateTime',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'End date and time (ISO format)',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'Event description',
},
{
displayName: 'Location',
name: 'location',
type: 'string',
default: '',
displayOptions: {
show: {
parameterMode: ['fields', 'auto'],
},
},
description: 'Event location',
},
],
};
}
buildToolParameters(context, itemIndex) {
const mode = context.getNodeParameter('parameterMode', itemIndex, 'auto');
// Individual Fields Mode
if (mode === 'fields') {
const result = {};
const fieldMapping = {
'calendarHref': 'calendar_href',
'summary': 'summary',
'startDateTime': 'start',
'endDateTime': 'end',
'description': 'description',
'location': 'location'
};
Object.keys(fieldMapping).forEach(key => {
const value = context.getNodeParameter(key, itemIndex, '');
if (value && value.trim() !== '') {
result[fieldMapping[key]] = value;
}
});
return result;
}
// JSON Mode
if (mode === 'json') {
return context.getNodeParameter('parameters', itemIndex, {});
}
// Auto Mode - Try individual fields first, fallback to JSON
const individualParams = this.buildToolParameters(context, itemIndex);
if (Object.keys(individualParams).length > 0) {
return individualParams;
}
return context.getNodeParameter('parameters', itemIndex, {});
}
async createMcpClient(credentials) {
const client = new index_js_1.Client({
name: 'n8n-mcp-client-flex',
version: '0.1.0'
}, {
capabilities: {}
});
let transport;
if (credentials.serverUrl) {
// HTTP Transport
transport = {
start: async () => { },
close: async () => { },
send: async (request) => {
const response = await fetch(credentials.serverUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(credentials.apiKey && { 'Authorization': `Bearer ${credentials.apiKey}` })
},
body: JSON.stringify(request)
});
return response.json();
}
};
}
else {
throw new n8n_workflow_1.NodeOperationError({}, 'No valid transport configuration found');
}
await client.connect(transport);
return client;
}
async execute() {
const context = this;
const items = context.getInputData();
const returnData = [];
const credentials = await context.getCredentials('mcpClientApi');
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
try {
const toolName = context.getNodeParameter('toolName', itemIndex);
const toolParameters = this.buildToolParameters(context, itemIndex);
const client = await this.createMcpClient(credentials);
const result = await client.callTool({
name: toolName,
arguments: toolParameters
});
await client.close();
returnData.push({
json: {
toolName,
parameters: toolParameters,
result: result.content || {}
}
});
}
catch (error) {
if (context.continueOnFail()) {
returnData.push({
json: {
error: error.message
},
pairedItem: {
item: itemIndex
}
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.McpClient = McpClient;