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.
Files changed (47) hide show
  1. package/README.md +9 -1
  2. package/data/nodes.db +0 -0
  3. package/dist/http-server-single-session.d.ts +21 -1
  4. package/dist/http-server-single-session.d.ts.map +1 -1
  5. package/dist/http-server-single-session.js +515 -44
  6. package/dist/http-server-single-session.js.map +1 -1
  7. package/dist/http-server.d.ts.map +1 -1
  8. package/dist/http-server.js +5 -2
  9. package/dist/http-server.js.map +1 -1
  10. package/dist/mcp/server.d.ts +4 -0
  11. package/dist/mcp/server.d.ts.map +1 -1
  12. package/dist/mcp/server.js +382 -17
  13. package/dist/mcp/server.js.map +1 -1
  14. package/dist/mcp/tools-n8n-friendly.d.ts +6 -0
  15. package/dist/mcp/tools-n8n-friendly.d.ts.map +1 -0
  16. package/dist/mcp/tools-n8n-friendly.js +131 -0
  17. package/dist/mcp/tools-n8n-friendly.js.map +1 -0
  18. package/dist/mcp/tools.d.ts.map +1 -1
  19. package/dist/mcp/tools.js +187 -11
  20. package/dist/mcp/tools.js.map +1 -1
  21. package/dist/mcp/workflow-examples.d.ts +76 -0
  22. package/dist/mcp/workflow-examples.d.ts.map +1 -0
  23. package/dist/mcp/workflow-examples.js +111 -0
  24. package/dist/mcp/workflow-examples.js.map +1 -0
  25. package/dist/scripts/test-protocol-negotiation.d.ts +3 -0
  26. package/dist/scripts/test-protocol-negotiation.d.ts.map +1 -0
  27. package/dist/scripts/test-protocol-negotiation.js +154 -0
  28. package/dist/scripts/test-protocol-negotiation.js.map +1 -0
  29. package/dist/services/enhanced-config-validator.d.ts +4 -0
  30. package/dist/services/enhanced-config-validator.d.ts.map +1 -1
  31. package/dist/services/enhanced-config-validator.js +86 -1
  32. package/dist/services/enhanced-config-validator.js.map +1 -1
  33. package/dist/services/n8n-validation.d.ts +2 -2
  34. package/dist/types/index.d.ts +6 -0
  35. package/dist/types/index.d.ts.map +1 -1
  36. package/dist/utils/fixed-collection-validator.d.ts +35 -0
  37. package/dist/utils/fixed-collection-validator.d.ts.map +1 -0
  38. package/dist/utils/fixed-collection-validator.js +358 -0
  39. package/dist/utils/fixed-collection-validator.js.map +1 -0
  40. package/dist/utils/logger.d.ts.map +1 -1
  41. package/dist/utils/logger.js +6 -3
  42. package/dist/utils/logger.js.map +1 -1
  43. package/dist/utils/protocol-version.d.ts +19 -0
  44. package/dist/utils/protocol-version.d.ts.map +1 -0
  45. package/dist/utils/protocol-version.js +95 -0
  46. package/dist/utils/protocol-version.js.map +1 -0
  47. package/package.json +1 -1
@@ -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: '2024-11-05',
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
- if (process.env.DEBUG_MCP === 'true') {
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
- const tools = [...tools_1.n8nDocumentationToolsFinal];
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, args);
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
- return {
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: JSON.stringify(result, null, 2),
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: `Error executing tool ${name}: ${error instanceof Error ? error.message : 'Unknown error'}`,
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
- return this.searchNodes(args.query, args.limit, { mode: args.mode });
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
- return this.searchNodeProperties(args.nodeType, args.query, args.maxResults);
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
- return this.listNodeTemplates(args.nodeTypes, args.limit);
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
- return this.getTemplate(args.templateId);
536
+ this.validateToolParams(name, args, ['templateId']);
537
+ const templateId = Number(args.templateId);
538
+ return this.getTemplate(templateId);
236
539
  case 'search_templates':
237
- return this.searchTemplates(args.query, args.limit);
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);