n8n-mcp 2.8.1 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/data/nodes.db +0 -0
- package/dist/http-server-single-session.d.ts +21 -1
- package/dist/http-server-single-session.d.ts.map +1 -1
- package/dist/http-server-single-session.js +515 -44
- package/dist/http-server-single-session.js.map +1 -1
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +5 -2
- package/dist/http-server.js.map +1 -1
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +382 -17
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools-n8n-friendly.d.ts +6 -0
- package/dist/mcp/tools-n8n-friendly.d.ts.map +1 -0
- package/dist/mcp/tools-n8n-friendly.js +131 -0
- package/dist/mcp/tools-n8n-friendly.js.map +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +187 -11
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/workflow-examples.d.ts +76 -0
- package/dist/mcp/workflow-examples.d.ts.map +1 -0
- package/dist/mcp/workflow-examples.js +111 -0
- package/dist/mcp/workflow-examples.js.map +1 -0
- package/dist/scripts/test-protocol-negotiation.d.ts +3 -0
- package/dist/scripts/test-protocol-negotiation.d.ts.map +1 -0
- package/dist/scripts/test-protocol-negotiation.js +154 -0
- package/dist/scripts/test-protocol-negotiation.js.map +1 -0
- package/dist/services/enhanced-config-validator.d.ts +4 -0
- package/dist/services/enhanced-config-validator.d.ts.map +1 -1
- package/dist/services/enhanced-config-validator.js +86 -1
- package/dist/services/enhanced-config-validator.js.map +1 -1
- package/dist/services/n8n-validation.d.ts +2 -2
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/fixed-collection-validator.d.ts +35 -0
- package/dist/utils/fixed-collection-validator.d.ts.map +1 -0
- package/dist/utils/fixed-collection-validator.js +358 -0
- package/dist/utils/fixed-collection-validator.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/protocol-version.d.ts +19 -0
- package/dist/utils/protocol-version.d.ts.map +1 -0
- package/dist/utils/protocol-version.js +95 -0
- package/dist/utils/protocol-version.js.map +1 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -44,6 +44,8 @@ const fs_1 = require("fs");
|
|
|
44
44
|
const path_1 = __importDefault(require("path"));
|
|
45
45
|
const tools_1 = require("./tools");
|
|
46
46
|
const tools_n8n_manager_1 = require("./tools-n8n-manager");
|
|
47
|
+
const tools_n8n_friendly_1 = require("./tools-n8n-friendly");
|
|
48
|
+
const workflow_examples_1 = require("./workflow-examples");
|
|
47
49
|
const logger_1 = require("../utils/logger");
|
|
48
50
|
const node_repository_1 = require("../database/node-repository");
|
|
49
51
|
const database_adapter_1 = require("../database/database-adapter");
|
|
@@ -60,12 +62,14 @@ const handlers_workflow_diff_1 = require("./handlers-workflow-diff");
|
|
|
60
62
|
const tools_documentation_1 = require("./tools-documentation");
|
|
61
63
|
const version_1 = require("../utils/version");
|
|
62
64
|
const node_utils_1 = require("../utils/node-utils");
|
|
65
|
+
const protocol_version_1 = require("../utils/protocol-version");
|
|
63
66
|
class N8NDocumentationMCPServer {
|
|
64
67
|
constructor() {
|
|
65
68
|
this.db = null;
|
|
66
69
|
this.repository = null;
|
|
67
70
|
this.templateService = null;
|
|
68
71
|
this.cache = new simple_cache_1.SimpleCache();
|
|
72
|
+
this.clientInfo = null;
|
|
69
73
|
const envDbPath = process.env.NODE_DB_PATH;
|
|
70
74
|
let dbPath = null;
|
|
71
75
|
let possiblePaths = [];
|
|
@@ -140,9 +144,25 @@ class N8NDocumentationMCPServer {
|
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
146
|
setupHandlers() {
|
|
143
|
-
this.server.setRequestHandler(types_js_1.InitializeRequestSchema, async () => {
|
|
147
|
+
this.server.setRequestHandler(types_js_1.InitializeRequestSchema, async (request) => {
|
|
148
|
+
const clientVersion = request.params.protocolVersion;
|
|
149
|
+
const clientCapabilities = request.params.capabilities;
|
|
150
|
+
const clientInfo = request.params.clientInfo;
|
|
151
|
+
logger_1.logger.info('MCP Initialize request received', {
|
|
152
|
+
clientVersion,
|
|
153
|
+
clientCapabilities,
|
|
154
|
+
clientInfo
|
|
155
|
+
});
|
|
156
|
+
this.clientInfo = clientInfo;
|
|
157
|
+
const negotiationResult = (0, protocol_version_1.negotiateProtocolVersion)(clientVersion, clientInfo, undefined, undefined);
|
|
158
|
+
(0, protocol_version_1.logProtocolNegotiation)(negotiationResult, logger_1.logger, 'MCP_INITIALIZE');
|
|
159
|
+
if (clientVersion && clientVersion !== negotiationResult.version) {
|
|
160
|
+
logger_1.logger.warn(`Protocol version negotiated: client requested ${clientVersion}, server will use ${negotiationResult.version}`, {
|
|
161
|
+
reasoning: negotiationResult.reasoning
|
|
162
|
+
});
|
|
163
|
+
}
|
|
144
164
|
const response = {
|
|
145
|
-
protocolVersion:
|
|
165
|
+
protocolVersion: negotiationResult.version,
|
|
146
166
|
capabilities: {
|
|
147
167
|
tools: {},
|
|
148
168
|
},
|
|
@@ -151,13 +171,11 @@ class N8NDocumentationMCPServer {
|
|
|
151
171
|
version: version_1.PROJECT_VERSION,
|
|
152
172
|
},
|
|
153
173
|
};
|
|
154
|
-
|
|
155
|
-
logger_1.logger.debug('Initialize handler called', { response });
|
|
156
|
-
}
|
|
174
|
+
logger_1.logger.info('MCP Initialize response', { response });
|
|
157
175
|
return response;
|
|
158
176
|
});
|
|
159
|
-
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
160
|
-
|
|
177
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async (request) => {
|
|
178
|
+
let tools = [...tools_1.n8nDocumentationToolsFinal];
|
|
161
179
|
const isConfigured = (0, n8n_api_1.isN8nApiConfigured)();
|
|
162
180
|
if (isConfigured) {
|
|
163
181
|
tools.push(...tools_n8n_manager_1.n8nManagementTools);
|
|
@@ -166,30 +184,124 @@ class N8NDocumentationMCPServer {
|
|
|
166
184
|
else {
|
|
167
185
|
logger_1.logger.debug(`Tool listing: ${tools.length} tools available (documentation only)`);
|
|
168
186
|
}
|
|
187
|
+
const clientInfo = this.clientInfo;
|
|
188
|
+
const isN8nClient = clientInfo?.name?.includes('n8n') ||
|
|
189
|
+
clientInfo?.name?.includes('langchain');
|
|
190
|
+
if (isN8nClient) {
|
|
191
|
+
logger_1.logger.info('Detected n8n client, using n8n-friendly tool descriptions');
|
|
192
|
+
tools = (0, tools_n8n_friendly_1.makeToolsN8nFriendly)(tools);
|
|
193
|
+
}
|
|
194
|
+
const validationTools = tools.filter(t => t.name.startsWith('validate_'));
|
|
195
|
+
validationTools.forEach(tool => {
|
|
196
|
+
logger_1.logger.info('Validation tool schema', {
|
|
197
|
+
toolName: tool.name,
|
|
198
|
+
inputSchema: JSON.stringify(tool.inputSchema, null, 2),
|
|
199
|
+
hasOutputSchema: !!tool.outputSchema,
|
|
200
|
+
description: tool.description
|
|
201
|
+
});
|
|
202
|
+
});
|
|
169
203
|
return { tools };
|
|
170
204
|
});
|
|
171
205
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
172
206
|
const { name, arguments: args } = request.params;
|
|
207
|
+
logger_1.logger.info('Tool call received - DETAILED DEBUG', {
|
|
208
|
+
toolName: name,
|
|
209
|
+
arguments: JSON.stringify(args, null, 2),
|
|
210
|
+
argumentsType: typeof args,
|
|
211
|
+
argumentsKeys: args ? Object.keys(args) : [],
|
|
212
|
+
hasNodeType: args && 'nodeType' in args,
|
|
213
|
+
hasConfig: args && 'config' in args,
|
|
214
|
+
configType: args && args.config ? typeof args.config : 'N/A',
|
|
215
|
+
rawRequest: JSON.stringify(request.params)
|
|
216
|
+
});
|
|
217
|
+
let processedArgs = args;
|
|
218
|
+
if (args && typeof args === 'object' && 'output' in args) {
|
|
219
|
+
try {
|
|
220
|
+
const possibleNestedData = args.output;
|
|
221
|
+
if (typeof possibleNestedData === 'string' && possibleNestedData.trim().startsWith('{')) {
|
|
222
|
+
const parsed = JSON.parse(possibleNestedData);
|
|
223
|
+
if (parsed && typeof parsed === 'object') {
|
|
224
|
+
logger_1.logger.warn('Detected n8n nested output bug, attempting to extract actual arguments', {
|
|
225
|
+
originalArgs: args,
|
|
226
|
+
extractedArgs: parsed
|
|
227
|
+
});
|
|
228
|
+
if (this.validateExtractedArgs(name, parsed)) {
|
|
229
|
+
processedArgs = parsed;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
logger_1.logger.warn('Extracted arguments failed validation, using original args', {
|
|
233
|
+
toolName: name,
|
|
234
|
+
extractedArgs: parsed
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (parseError) {
|
|
241
|
+
logger_1.logger.debug('Failed to parse nested output, continuing with original args', {
|
|
242
|
+
error: parseError instanceof Error ? parseError.message : String(parseError)
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
173
246
|
try {
|
|
174
|
-
logger_1.logger.debug(`Executing tool: ${name}`, { args });
|
|
175
|
-
const result = await this.executeTool(name,
|
|
247
|
+
logger_1.logger.debug(`Executing tool: ${name}`, { args: processedArgs });
|
|
248
|
+
const result = await this.executeTool(name, processedArgs);
|
|
176
249
|
logger_1.logger.debug(`Tool ${name} executed successfully`);
|
|
177
|
-
|
|
250
|
+
let responseText;
|
|
251
|
+
let structuredContent = null;
|
|
252
|
+
try {
|
|
253
|
+
if (name.startsWith('validate_') && typeof result === 'object' && result !== null) {
|
|
254
|
+
const cleanResult = this.sanitizeValidationResult(result, name);
|
|
255
|
+
structuredContent = cleanResult;
|
|
256
|
+
responseText = JSON.stringify(cleanResult, null, 2);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
responseText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (jsonError) {
|
|
263
|
+
logger_1.logger.warn(`Failed to stringify tool result for ${name}:`, jsonError);
|
|
264
|
+
responseText = String(result);
|
|
265
|
+
}
|
|
266
|
+
if (responseText.length > 1000000) {
|
|
267
|
+
logger_1.logger.warn(`Tool ${name} response is very large (${responseText.length} chars), truncating`);
|
|
268
|
+
responseText = responseText.substring(0, 999000) + '\n\n[Response truncated due to size limits]';
|
|
269
|
+
structuredContent = null;
|
|
270
|
+
}
|
|
271
|
+
const mcpResponse = {
|
|
178
272
|
content: [
|
|
179
273
|
{
|
|
180
274
|
type: 'text',
|
|
181
|
-
text:
|
|
275
|
+
text: responseText,
|
|
182
276
|
},
|
|
183
277
|
],
|
|
184
278
|
};
|
|
279
|
+
if (name.startsWith('validate_') && structuredContent !== null) {
|
|
280
|
+
mcpResponse.structuredContent = structuredContent;
|
|
281
|
+
}
|
|
282
|
+
return mcpResponse;
|
|
185
283
|
}
|
|
186
284
|
catch (error) {
|
|
187
285
|
logger_1.logger.error(`Error executing tool ${name}`, error);
|
|
286
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
287
|
+
let helpfulMessage = `Error executing tool ${name}: ${errorMessage}`;
|
|
288
|
+
if (errorMessage.includes('required') || errorMessage.includes('missing')) {
|
|
289
|
+
helpfulMessage += '\n\nNote: This error often occurs when the AI agent sends incomplete or incorrectly formatted parameters. Please ensure all required fields are provided with the correct types.';
|
|
290
|
+
}
|
|
291
|
+
else if (errorMessage.includes('type') || errorMessage.includes('expected')) {
|
|
292
|
+
helpfulMessage += '\n\nNote: This error indicates a type mismatch. The AI agent may be sending data in the wrong format (e.g., string instead of object).';
|
|
293
|
+
}
|
|
294
|
+
else if (errorMessage.includes('Unknown category') || errorMessage.includes('not found')) {
|
|
295
|
+
helpfulMessage += '\n\nNote: The requested resource or category was not found. Please check the available options.';
|
|
296
|
+
}
|
|
297
|
+
if (name.startsWith('validate_') && (errorMessage.includes('config') || errorMessage.includes('nodeType'))) {
|
|
298
|
+
helpfulMessage += '\n\nFor validation tools:\n- nodeType should be a string (e.g., "nodes-base.webhook")\n- config should be an object (e.g., {})';
|
|
299
|
+
}
|
|
188
300
|
return {
|
|
189
301
|
content: [
|
|
190
302
|
{
|
|
191
303
|
type: 'text',
|
|
192
|
-
text:
|
|
304
|
+
text: helpfulMessage,
|
|
193
305
|
},
|
|
194
306
|
],
|
|
195
307
|
isError: true,
|
|
@@ -197,82 +309,291 @@ class N8NDocumentationMCPServer {
|
|
|
197
309
|
}
|
|
198
310
|
});
|
|
199
311
|
}
|
|
312
|
+
sanitizeValidationResult(result, toolName) {
|
|
313
|
+
if (!result || typeof result !== 'object') {
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
const sanitized = { ...result };
|
|
317
|
+
if (toolName === 'validate_node_minimal') {
|
|
318
|
+
const filtered = {
|
|
319
|
+
nodeType: String(sanitized.nodeType || ''),
|
|
320
|
+
displayName: String(sanitized.displayName || ''),
|
|
321
|
+
valid: Boolean(sanitized.valid),
|
|
322
|
+
missingRequiredFields: Array.isArray(sanitized.missingRequiredFields)
|
|
323
|
+
? sanitized.missingRequiredFields.map(String)
|
|
324
|
+
: []
|
|
325
|
+
};
|
|
326
|
+
return filtered;
|
|
327
|
+
}
|
|
328
|
+
else if (toolName === 'validate_node_operation') {
|
|
329
|
+
let summary = sanitized.summary;
|
|
330
|
+
if (!summary || typeof summary !== 'object') {
|
|
331
|
+
summary = {
|
|
332
|
+
hasErrors: Array.isArray(sanitized.errors) ? sanitized.errors.length > 0 : false,
|
|
333
|
+
errorCount: Array.isArray(sanitized.errors) ? sanitized.errors.length : 0,
|
|
334
|
+
warningCount: Array.isArray(sanitized.warnings) ? sanitized.warnings.length : 0,
|
|
335
|
+
suggestionCount: Array.isArray(sanitized.suggestions) ? sanitized.suggestions.length : 0
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
const filtered = {
|
|
339
|
+
nodeType: String(sanitized.nodeType || ''),
|
|
340
|
+
workflowNodeType: String(sanitized.workflowNodeType || sanitized.nodeType || ''),
|
|
341
|
+
displayName: String(sanitized.displayName || ''),
|
|
342
|
+
valid: Boolean(sanitized.valid),
|
|
343
|
+
errors: Array.isArray(sanitized.errors) ? sanitized.errors : [],
|
|
344
|
+
warnings: Array.isArray(sanitized.warnings) ? sanitized.warnings : [],
|
|
345
|
+
suggestions: Array.isArray(sanitized.suggestions) ? sanitized.suggestions : [],
|
|
346
|
+
summary: summary
|
|
347
|
+
};
|
|
348
|
+
return filtered;
|
|
349
|
+
}
|
|
350
|
+
else if (toolName.startsWith('validate_workflow')) {
|
|
351
|
+
sanitized.valid = Boolean(sanitized.valid);
|
|
352
|
+
sanitized.errors = Array.isArray(sanitized.errors) ? sanitized.errors : [];
|
|
353
|
+
sanitized.warnings = Array.isArray(sanitized.warnings) ? sanitized.warnings : [];
|
|
354
|
+
if (toolName === 'validate_workflow') {
|
|
355
|
+
if (!sanitized.summary || typeof sanitized.summary !== 'object') {
|
|
356
|
+
sanitized.summary = {
|
|
357
|
+
totalNodes: 0,
|
|
358
|
+
enabledNodes: 0,
|
|
359
|
+
triggerNodes: 0,
|
|
360
|
+
validConnections: 0,
|
|
361
|
+
invalidConnections: 0,
|
|
362
|
+
expressionsValidated: 0,
|
|
363
|
+
errorCount: sanitized.errors.length,
|
|
364
|
+
warningCount: sanitized.warnings.length
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
if (!sanitized.statistics || typeof sanitized.statistics !== 'object') {
|
|
370
|
+
sanitized.statistics = {
|
|
371
|
+
totalNodes: 0,
|
|
372
|
+
triggerNodes: 0,
|
|
373
|
+
validConnections: 0,
|
|
374
|
+
invalidConnections: 0,
|
|
375
|
+
expressionsValidated: 0
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return JSON.parse(JSON.stringify(sanitized));
|
|
381
|
+
}
|
|
382
|
+
validateToolParams(toolName, args, requiredParams) {
|
|
383
|
+
const missing = [];
|
|
384
|
+
for (const param of requiredParams) {
|
|
385
|
+
if (!(param in args) || args[param] === undefined || args[param] === null) {
|
|
386
|
+
missing.push(param);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (missing.length > 0) {
|
|
390
|
+
throw new Error(`Missing required parameters for ${toolName}: ${missing.join(', ')}. Please provide the required parameters to use this tool.`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
validateExtractedArgs(toolName, args) {
|
|
394
|
+
if (!args || typeof args !== 'object') {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const allTools = [...tools_1.n8nDocumentationToolsFinal, ...tools_n8n_manager_1.n8nManagementTools];
|
|
398
|
+
const tool = allTools.find(t => t.name === toolName);
|
|
399
|
+
if (!tool || !tool.inputSchema) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
const schema = tool.inputSchema;
|
|
403
|
+
const required = schema.required || [];
|
|
404
|
+
const properties = schema.properties || {};
|
|
405
|
+
for (const requiredField of required) {
|
|
406
|
+
if (!(requiredField in args)) {
|
|
407
|
+
logger_1.logger.debug(`Extracted args missing required field: ${requiredField}`, {
|
|
408
|
+
toolName,
|
|
409
|
+
extractedArgs: args,
|
|
410
|
+
required
|
|
411
|
+
});
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const [fieldName, fieldValue] of Object.entries(args)) {
|
|
416
|
+
if (properties[fieldName]) {
|
|
417
|
+
const expectedType = properties[fieldName].type;
|
|
418
|
+
const actualType = Array.isArray(fieldValue) ? 'array' : typeof fieldValue;
|
|
419
|
+
if (expectedType && expectedType !== actualType) {
|
|
420
|
+
if (expectedType === 'number' && actualType === 'string' && !isNaN(Number(fieldValue))) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
logger_1.logger.debug(`Extracted args field type mismatch: ${fieldName}`, {
|
|
424
|
+
toolName,
|
|
425
|
+
expectedType,
|
|
426
|
+
actualType,
|
|
427
|
+
fieldValue
|
|
428
|
+
});
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (schema.additionalProperties === false) {
|
|
434
|
+
const allowedFields = Object.keys(properties);
|
|
435
|
+
const extraFields = Object.keys(args).filter(field => !allowedFields.includes(field));
|
|
436
|
+
if (extraFields.length > 0) {
|
|
437
|
+
logger_1.logger.debug(`Extracted args have extra fields`, {
|
|
438
|
+
toolName,
|
|
439
|
+
extraFields,
|
|
440
|
+
allowedFields
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
200
446
|
async executeTool(name, args) {
|
|
447
|
+
args = args || {};
|
|
448
|
+
logger_1.logger.info(`Tool execution: ${name}`, {
|
|
449
|
+
args: typeof args === 'object' ? JSON.stringify(args) : args,
|
|
450
|
+
argsType: typeof args,
|
|
451
|
+
argsKeys: typeof args === 'object' ? Object.keys(args) : 'not-object'
|
|
452
|
+
});
|
|
453
|
+
if (typeof args !== 'object' || args === null) {
|
|
454
|
+
throw new Error(`Invalid arguments for tool ${name}: expected object, got ${typeof args}`);
|
|
455
|
+
}
|
|
201
456
|
switch (name) {
|
|
202
457
|
case 'tools_documentation':
|
|
203
458
|
return this.getToolsDocumentation(args.topic, args.depth);
|
|
204
459
|
case 'list_nodes':
|
|
205
460
|
return this.listNodes(args);
|
|
206
461
|
case 'get_node_info':
|
|
462
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
207
463
|
return this.getNodeInfo(args.nodeType);
|
|
208
464
|
case 'search_nodes':
|
|
209
|
-
|
|
465
|
+
this.validateToolParams(name, args, ['query']);
|
|
466
|
+
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
|
467
|
+
return this.searchNodes(args.query, limit, { mode: args.mode });
|
|
210
468
|
case 'list_ai_tools':
|
|
211
469
|
return this.listAITools();
|
|
212
470
|
case 'get_node_documentation':
|
|
471
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
213
472
|
return this.getNodeDocumentation(args.nodeType);
|
|
214
473
|
case 'get_database_statistics':
|
|
215
474
|
return this.getDatabaseStatistics();
|
|
216
475
|
case 'get_node_essentials':
|
|
476
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
217
477
|
return this.getNodeEssentials(args.nodeType);
|
|
218
478
|
case 'search_node_properties':
|
|
219
|
-
|
|
479
|
+
this.validateToolParams(name, args, ['nodeType', 'query']);
|
|
480
|
+
const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
|
|
481
|
+
return this.searchNodeProperties(args.nodeType, args.query, maxResults);
|
|
220
482
|
case 'get_node_for_task':
|
|
483
|
+
this.validateToolParams(name, args, ['task']);
|
|
221
484
|
return this.getNodeForTask(args.task);
|
|
222
485
|
case 'list_tasks':
|
|
223
486
|
return this.listTasks(args.category);
|
|
224
487
|
case 'validate_node_operation':
|
|
488
|
+
this.validateToolParams(name, args, ['nodeType', 'config']);
|
|
489
|
+
if (typeof args.config !== 'object' || args.config === null) {
|
|
490
|
+
logger_1.logger.warn(`validate_node_operation called with invalid config type: ${typeof args.config}`);
|
|
491
|
+
return {
|
|
492
|
+
nodeType: args.nodeType || 'unknown',
|
|
493
|
+
workflowNodeType: args.nodeType || 'unknown',
|
|
494
|
+
displayName: 'Unknown Node',
|
|
495
|
+
valid: false,
|
|
496
|
+
errors: [{
|
|
497
|
+
type: 'config',
|
|
498
|
+
property: 'config',
|
|
499
|
+
message: 'Invalid config format - expected object',
|
|
500
|
+
fix: 'Provide config as an object with node properties'
|
|
501
|
+
}],
|
|
502
|
+
warnings: [],
|
|
503
|
+
suggestions: [],
|
|
504
|
+
summary: {
|
|
505
|
+
hasErrors: true,
|
|
506
|
+
errorCount: 1,
|
|
507
|
+
warningCount: 0,
|
|
508
|
+
suggestionCount: 0
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
225
512
|
return this.validateNodeConfig(args.nodeType, args.config, 'operation', args.profile);
|
|
226
513
|
case 'validate_node_minimal':
|
|
514
|
+
this.validateToolParams(name, args, ['nodeType', 'config']);
|
|
515
|
+
if (typeof args.config !== 'object' || args.config === null) {
|
|
516
|
+
logger_1.logger.warn(`validate_node_minimal called with invalid config type: ${typeof args.config}`);
|
|
517
|
+
return {
|
|
518
|
+
nodeType: args.nodeType || 'unknown',
|
|
519
|
+
displayName: 'Unknown Node',
|
|
520
|
+
valid: false,
|
|
521
|
+
missingRequiredFields: ['Invalid config format - expected object']
|
|
522
|
+
};
|
|
523
|
+
}
|
|
227
524
|
return this.validateNodeMinimal(args.nodeType, args.config);
|
|
228
525
|
case 'get_property_dependencies':
|
|
526
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
229
527
|
return this.getPropertyDependencies(args.nodeType, args.config);
|
|
230
528
|
case 'get_node_as_tool_info':
|
|
529
|
+
this.validateToolParams(name, args, ['nodeType']);
|
|
231
530
|
return this.getNodeAsToolInfo(args.nodeType);
|
|
232
531
|
case 'list_node_templates':
|
|
233
|
-
|
|
532
|
+
this.validateToolParams(name, args, ['nodeTypes']);
|
|
533
|
+
const templateLimit = args.limit !== undefined ? Number(args.limit) || 10 : 10;
|
|
534
|
+
return this.listNodeTemplates(args.nodeTypes, templateLimit);
|
|
234
535
|
case 'get_template':
|
|
235
|
-
|
|
536
|
+
this.validateToolParams(name, args, ['templateId']);
|
|
537
|
+
const templateId = Number(args.templateId);
|
|
538
|
+
return this.getTemplate(templateId);
|
|
236
539
|
case 'search_templates':
|
|
237
|
-
|
|
540
|
+
this.validateToolParams(name, args, ['query']);
|
|
541
|
+
const searchLimit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
|
542
|
+
return this.searchTemplates(args.query, searchLimit);
|
|
238
543
|
case 'get_templates_for_task':
|
|
544
|
+
this.validateToolParams(name, args, ['task']);
|
|
239
545
|
return this.getTemplatesForTask(args.task);
|
|
240
546
|
case 'validate_workflow':
|
|
547
|
+
this.validateToolParams(name, args, ['workflow']);
|
|
241
548
|
return this.validateWorkflow(args.workflow, args.options);
|
|
242
549
|
case 'validate_workflow_connections':
|
|
550
|
+
this.validateToolParams(name, args, ['workflow']);
|
|
243
551
|
return this.validateWorkflowConnections(args.workflow);
|
|
244
552
|
case 'validate_workflow_expressions':
|
|
553
|
+
this.validateToolParams(name, args, ['workflow']);
|
|
245
554
|
return this.validateWorkflowExpressions(args.workflow);
|
|
246
555
|
case 'n8n_create_workflow':
|
|
556
|
+
this.validateToolParams(name, args, ['name', 'nodes', 'connections']);
|
|
247
557
|
return n8nHandlers.handleCreateWorkflow(args);
|
|
248
558
|
case 'n8n_get_workflow':
|
|
559
|
+
this.validateToolParams(name, args, ['id']);
|
|
249
560
|
return n8nHandlers.handleGetWorkflow(args);
|
|
250
561
|
case 'n8n_get_workflow_details':
|
|
562
|
+
this.validateToolParams(name, args, ['id']);
|
|
251
563
|
return n8nHandlers.handleGetWorkflowDetails(args);
|
|
252
564
|
case 'n8n_get_workflow_structure':
|
|
565
|
+
this.validateToolParams(name, args, ['id']);
|
|
253
566
|
return n8nHandlers.handleGetWorkflowStructure(args);
|
|
254
567
|
case 'n8n_get_workflow_minimal':
|
|
568
|
+
this.validateToolParams(name, args, ['id']);
|
|
255
569
|
return n8nHandlers.handleGetWorkflowMinimal(args);
|
|
256
570
|
case 'n8n_update_full_workflow':
|
|
571
|
+
this.validateToolParams(name, args, ['id']);
|
|
257
572
|
return n8nHandlers.handleUpdateWorkflow(args);
|
|
258
573
|
case 'n8n_update_partial_workflow':
|
|
574
|
+
this.validateToolParams(name, args, ['id', 'operations']);
|
|
259
575
|
return (0, handlers_workflow_diff_1.handleUpdatePartialWorkflow)(args);
|
|
260
576
|
case 'n8n_delete_workflow':
|
|
577
|
+
this.validateToolParams(name, args, ['id']);
|
|
261
578
|
return n8nHandlers.handleDeleteWorkflow(args);
|
|
262
579
|
case 'n8n_list_workflows':
|
|
263
580
|
return n8nHandlers.handleListWorkflows(args);
|
|
264
581
|
case 'n8n_validate_workflow':
|
|
582
|
+
this.validateToolParams(name, args, ['id']);
|
|
265
583
|
await this.ensureInitialized();
|
|
266
584
|
if (!this.repository)
|
|
267
585
|
throw new Error('Repository not initialized');
|
|
268
586
|
return n8nHandlers.handleValidateWorkflow(args, this.repository);
|
|
269
587
|
case 'n8n_trigger_webhook_workflow':
|
|
588
|
+
this.validateToolParams(name, args, ['webhookUrl']);
|
|
270
589
|
return n8nHandlers.handleTriggerWebhookWorkflow(args);
|
|
271
590
|
case 'n8n_get_execution':
|
|
591
|
+
this.validateToolParams(name, args, ['id']);
|
|
272
592
|
return n8nHandlers.handleGetExecution(args);
|
|
273
593
|
case 'n8n_list_executions':
|
|
274
594
|
return n8nHandlers.handleListExecutions(args);
|
|
275
595
|
case 'n8n_delete_execution':
|
|
596
|
+
this.validateToolParams(name, args, ['id']);
|
|
276
597
|
return n8nHandlers.handleDeleteExecution(args);
|
|
277
598
|
case 'n8n_health_check':
|
|
278
599
|
return n8nHandlers.handleHealthCheck();
|
|
@@ -1463,6 +1784,50 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1463
1784
|
await this.ensureInitialized();
|
|
1464
1785
|
if (!this.repository)
|
|
1465
1786
|
throw new Error('Repository not initialized');
|
|
1787
|
+
logger_1.logger.info('Workflow validation requested', {
|
|
1788
|
+
hasWorkflow: !!workflow,
|
|
1789
|
+
workflowType: typeof workflow,
|
|
1790
|
+
hasNodes: workflow?.nodes !== undefined,
|
|
1791
|
+
nodesType: workflow?.nodes ? typeof workflow.nodes : 'undefined',
|
|
1792
|
+
nodesIsArray: Array.isArray(workflow?.nodes),
|
|
1793
|
+
nodesCount: Array.isArray(workflow?.nodes) ? workflow.nodes.length : 0,
|
|
1794
|
+
hasConnections: workflow?.connections !== undefined,
|
|
1795
|
+
connectionsType: workflow?.connections ? typeof workflow.connections : 'undefined',
|
|
1796
|
+
options: options
|
|
1797
|
+
});
|
|
1798
|
+
if (!workflow || typeof workflow !== 'object') {
|
|
1799
|
+
return {
|
|
1800
|
+
valid: false,
|
|
1801
|
+
errors: [{
|
|
1802
|
+
node: 'workflow',
|
|
1803
|
+
message: 'Workflow must be an object with nodes and connections',
|
|
1804
|
+
details: 'Expected format: ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
1805
|
+
}],
|
|
1806
|
+
summary: { errorCount: 1 }
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
if (!workflow.nodes || !Array.isArray(workflow.nodes)) {
|
|
1810
|
+
return {
|
|
1811
|
+
valid: false,
|
|
1812
|
+
errors: [{
|
|
1813
|
+
node: 'workflow',
|
|
1814
|
+
message: 'Workflow must have a nodes array',
|
|
1815
|
+
details: 'Expected: workflow.nodes = [array of node objects]. ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
1816
|
+
}],
|
|
1817
|
+
summary: { errorCount: 1 }
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
if (!workflow.connections || typeof workflow.connections !== 'object') {
|
|
1821
|
+
return {
|
|
1822
|
+
valid: false,
|
|
1823
|
+
errors: [{
|
|
1824
|
+
node: 'workflow',
|
|
1825
|
+
message: 'Workflow must have a connections object',
|
|
1826
|
+
details: 'Expected: workflow.connections = {} (can be empty object). ' + (0, workflow_examples_1.getWorkflowExampleString)()
|
|
1827
|
+
}],
|
|
1828
|
+
summary: { errorCount: 1 }
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1466
1831
|
const validator = new workflow_validator_1.WorkflowValidator(this.repository, enhanced_config_validator_1.EnhancedConfigValidator);
|
|
1467
1832
|
try {
|
|
1468
1833
|
const result = await validator.validateWorkflow(workflow, options);
|