n8n-mcp 2.7.10 → 2.7.11

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 (80) hide show
  1. package/README.md +20 -42
  2. package/data/nodes.db +0 -0
  3. package/dist/database/database-adapter.d.ts +1 -0
  4. package/dist/database/database-adapter.d.ts.map +1 -1
  5. package/dist/database/database-adapter.js +20 -0
  6. package/dist/database/database-adapter.js.map +1 -1
  7. package/dist/mcp/server.d.ts +7 -0
  8. package/dist/mcp/server.d.ts.map +1 -1
  9. package/dist/mcp/server.js +334 -10
  10. package/dist/mcp/server.js.map +1 -1
  11. package/dist/mcp/tools-documentation.d.ts.map +1 -1
  12. package/dist/mcp/tools-documentation.js +832 -52
  13. package/dist/mcp/tools-documentation.js.map +1 -1
  14. package/dist/mcp/tools-n8n-manager.d.ts.map +1 -1
  15. package/dist/mcp/tools-n8n-manager.js +10 -112
  16. package/dist/mcp/tools-n8n-manager.js.map +1 -1
  17. package/dist/mcp/tools.d.ts.map +1 -1
  18. package/dist/mcp/tools.js +42 -36
  19. package/dist/mcp/tools.js.map +1 -1
  20. package/dist/scripts/fetch-templates.d.ts.map +1 -1
  21. package/dist/scripts/fetch-templates.js +37 -0
  22. package/dist/scripts/fetch-templates.js.map +1 -1
  23. package/dist/scripts/test-error-handling-validation.d.ts +3 -0
  24. package/dist/scripts/test-error-handling-validation.d.ts.map +1 -0
  25. package/dist/scripts/test-error-handling-validation.js +340 -0
  26. package/dist/scripts/test-error-handling-validation.js.map +1 -0
  27. package/dist/scripts/test-node-level-properties.d.ts +3 -0
  28. package/dist/scripts/test-node-level-properties.d.ts.map +1 -0
  29. package/dist/scripts/test-node-level-properties.js +196 -0
  30. package/dist/scripts/test-node-level-properties.js.map +1 -0
  31. package/dist/services/config-validator.d.ts +2 -2
  32. package/dist/services/config-validator.d.ts.map +1 -1
  33. package/dist/services/config-validator.js +123 -5
  34. package/dist/services/config-validator.js.map +1 -1
  35. package/dist/services/enhanced-config-validator.d.ts +2 -0
  36. package/dist/services/enhanced-config-validator.d.ts.map +1 -1
  37. package/dist/services/enhanced-config-validator.js +31 -1
  38. package/dist/services/enhanced-config-validator.js.map +1 -1
  39. package/dist/services/example-generator.d.ts.map +1 -1
  40. package/dist/services/example-generator.js +442 -28
  41. package/dist/services/example-generator.js.map +1 -1
  42. package/dist/services/n8n-validation.d.ts +10 -10
  43. package/dist/services/node-specific-validators.d.ts +8 -1
  44. package/dist/services/node-specific-validators.d.ts.map +1 -1
  45. package/dist/services/node-specific-validators.js +608 -59
  46. package/dist/services/node-specific-validators.js.map +1 -1
  47. package/dist/services/task-templates.d.ts +1 -0
  48. package/dist/services/task-templates.d.ts.map +1 -1
  49. package/dist/services/task-templates.js +858 -16
  50. package/dist/services/task-templates.js.map +1 -1
  51. package/dist/services/workflow-diff-engine.d.ts.map +1 -1
  52. package/dist/services/workflow-diff-engine.js +1 -0
  53. package/dist/services/workflow-diff-engine.js.map +1 -1
  54. package/dist/services/workflow-validator.d.ts +9 -0
  55. package/dist/services/workflow-validator.d.ts.map +1 -1
  56. package/dist/services/workflow-validator.js +270 -0
  57. package/dist/services/workflow-validator.js.map +1 -1
  58. package/dist/sse-server.d.ts +8 -0
  59. package/dist/sse-server.d.ts.map +1 -0
  60. package/dist/sse-server.js +652 -0
  61. package/dist/sse-server.js.map +1 -0
  62. package/dist/templates/template-repository.d.ts +4 -0
  63. package/dist/templates/template-repository.d.ts.map +1 -1
  64. package/dist/templates/template-repository.js +119 -7
  65. package/dist/templates/template-repository.js.map +1 -1
  66. package/dist/templates/template-service.d.ts.map +1 -1
  67. package/dist/templates/template-service.js +2 -0
  68. package/dist/templates/template-service.js.map +1 -1
  69. package/dist/types/n8n-api.d.ts +2 -1
  70. package/dist/types/n8n-api.d.ts.map +1 -1
  71. package/dist/types/n8n-api.js.map +1 -1
  72. package/dist/types/sse.d.ts +42 -0
  73. package/dist/types/sse.d.ts.map +1 -0
  74. package/dist/types/sse.js +3 -0
  75. package/dist/types/sse.js.map +1 -0
  76. package/dist/utils/sse-session-manager.d.ts +23 -0
  77. package/dist/utils/sse-session-manager.d.ts.map +1 -0
  78. package/dist/utils/sse-session-manager.js +178 -0
  79. package/dist/utils/sse-session-manager.js.map +1 -0
  80. package/package.json +1 -1
@@ -11,43 +11,48 @@ exports.toolsDocumentation = {
11
11
  name: 'search_nodes',
12
12
  category: 'discovery',
13
13
  essentials: {
14
- description: 'Search for n8n nodes by keyword across names, descriptions, and categories',
15
- keyParameters: ['query', 'limit'],
16
- example: 'search_nodes({query: "slack", limit: 10})',
17
- performance: 'Fast - uses indexed full-text search',
14
+ description: 'Search nodes. Primary nodes ranked first.',
15
+ keyParameters: ['query', 'limit', 'mode'],
16
+ example: 'search_nodes({query: "webhook"})',
17
+ performance: 'Fast - FTS5 when available',
18
18
  tips: [
19
- 'Uses OR logic - "send slack" finds nodes with ANY of these words',
20
- 'Single words are more precise than phrases'
19
+ 'Primary nodes first: webhook→Webhook, http→HTTP Request',
20
+ 'Modes: OR (any word), AND (all words), FUZZY (typos OK)'
21
21
  ]
22
22
  },
23
23
  full: {
24
- description: 'Performs full-text search across all n8n nodes using indexed search. Returns nodes matching ANY word in the query (OR logic). Searches through node names, display names, descriptions, and categories.',
24
+ description: 'Search n8n nodes using FTS5 full-text search (when available) with relevance ranking. Supports OR (default), AND, and FUZZY search modes. Results are sorted by relevance, ensuring primary nodes like Webhook and HTTP Request appear first.',
25
25
  parameters: {
26
- query: { type: 'string', description: 'Search terms (words are ORed together)', required: true },
27
- limit: { type: 'number', description: 'Maximum results to return (default: 20)', required: false }
26
+ query: { type: 'string', description: 'Search terms. Wrap in quotes for exact phrase matching', required: true },
27
+ limit: { type: 'number', description: 'Maximum results to return (default: 20)', required: false },
28
+ mode: { type: 'string', description: 'Search mode: OR (any word), AND (all words in ANY field), FUZZY (typo-tolerant using edit distance)', required: false }
28
29
  },
29
- returns: 'Array of nodes with nodeType, displayName, description, category, and relevance score',
30
+ returns: 'Array of nodes sorted by relevance with nodeType, displayName, description, category. AND mode includes searchInfo explaining the search scope.',
30
31
  examples: [
31
- 'search_nodes({query: "slack"}) - Find all Slack-related nodes',
32
- 'search_nodes({query: "webhook trigger", limit: 5}) - Find nodes with "webhook" OR "trigger"',
33
- 'search_nodes({query: "ai"}) - Find AI-related nodes'
32
+ 'search_nodes({query: "webhook"}) - Webhook node appears first',
33
+ 'search_nodes({query: "http call"}) - HTTP Request node appears first',
34
+ 'search_nodes({query: "send message", mode: "AND"}) - Nodes with both words anywhere in their data',
35
+ 'search_nodes({query: "slak", mode: "FUZZY"}) - Finds Slack using typo tolerance'
34
36
  ],
35
37
  useCases: [
36
- 'Finding nodes for specific integrations',
37
- 'Discovering available functionality',
38
- 'Exploring nodes by keyword when exact name unknown'
38
+ 'Finding primary nodes quickly (webhook, http, email)',
39
+ 'Discovering nodes with typo tolerance',
40
+ 'Precise searches with AND mode',
41
+ 'Exploratory searches with OR mode'
39
42
  ],
40
- performance: 'Very fast - uses SQLite FTS5 full-text index. Typically <50ms even for complex queries.',
43
+ performance: 'FTS5: <20ms for most queries. Falls back to optimized LIKE queries if FTS5 unavailable.',
41
44
  bestPractices: [
42
- 'Use single words for precise matches',
43
- 'Try different variations if first search fails',
44
- 'Use list_nodes for browsing by category',
45
- 'Remember it\'s OR logic, not AND'
45
+ 'Default OR mode is best for exploration',
46
+ 'Use AND mode when you need all terms present',
47
+ 'Use FUZZY mode if unsure of spelling',
48
+ 'Quotes force exact phrase matching',
49
+ 'Primary nodes are boosted in relevance'
46
50
  ],
47
51
  pitfalls: [
48
- 'Multi-word queries may return too many results',
49
- 'Doesn\'t search in node properties or operations',
50
- 'Case-insensitive but doesn\'t handle typos'
52
+ 'AND mode searches ALL fields (description, documentation, operations) not just names',
53
+ 'FUZZY mode uses edit distance - may return unexpected matches for very short queries',
54
+ 'Special characters are ignored in search',
55
+ 'FTS5 syntax errors fallback to basic LIKE search'
51
56
  ],
52
57
  relatedTools: ['list_nodes', 'get_node_essentials', 'get_node_info']
53
58
  }
@@ -56,13 +61,12 @@ exports.toolsDocumentation = {
56
61
  name: 'get_node_essentials',
57
62
  category: 'configuration',
58
63
  essentials: {
59
- description: 'Get only the most important 10-20 properties for a node with examples',
64
+ description: 'Get 10-20 key properties with examples',
60
65
  keyParameters: ['nodeType'],
61
- example: 'get_node_essentials("n8n-nodes-base.slack")',
62
- performance: 'Very fast - returns <5KB instead of 100KB+',
66
+ example: 'get_node_essentials("nodes-base.slack")',
67
+ performance: '<5KB vs 100KB+',
63
68
  tips: [
64
- 'Use this instead of get_node_info for 95% of cases',
65
- 'Includes working examples for common operations'
69
+ 'Use this first! Has examples.'
66
70
  ]
67
71
  },
68
72
  full: {
@@ -294,11 +298,12 @@ exports.toolsDocumentation = {
294
298
  performance: 'API call - depends on n8n instance',
295
299
  tips: [
296
300
  'ALWAYS use node names in connections, never IDs',
301
+ 'Error handling properties go at NODE level, not inside parameters!',
297
302
  'Requires N8N_API_URL and N8N_API_KEY configuration'
298
303
  ]
299
304
  },
300
305
  full: {
301
- description: 'Creates a new workflow in your n8n instance via API. Requires proper API configuration. Returns the created workflow with assigned ID.',
306
+ description: 'Creates a new workflow in your n8n instance via API. Requires proper API configuration. Returns the created workflow with assigned ID.\n\n⚠️ CRITICAL: Error handling properties (onError, retryOnFail, etc.) are NODE-LEVEL properties, not inside parameters!',
302
307
  parameters: {
303
308
  name: { type: 'string', description: 'Workflow name', required: true },
304
309
  nodes: { type: 'array', description: 'Array of node configurations', required: true },
@@ -308,14 +313,61 @@ exports.toolsDocumentation = {
308
313
  },
309
314
  returns: 'Created workflow object with id, name, nodes, connections, and metadata',
310
315
  examples: [
311
- `n8n_create_workflow({
312
- name: "Slack Notification",
316
+ `// Basic workflow with proper error handling
317
+ n8n_create_workflow({
318
+ name: "Slack Notification with Error Handling",
313
319
  nodes: [
314
- {id: "1", name: "Webhook", type: "n8n-nodes-base.webhook", position: [250, 300]},
315
- {id: "2", name: "Slack", type: "n8n-nodes-base.slack", position: [450, 300], parameters: {...}}
320
+ {
321
+ id: "1",
322
+ name: "Webhook",
323
+ type: "n8n-nodes-base.webhook",
324
+ typeVersion: 2,
325
+ position: [250, 300],
326
+ parameters: {
327
+ path: "/webhook",
328
+ method: "POST"
329
+ },
330
+ // ✅ CORRECT - Error handling at node level
331
+ onError: "continueRegularOutput"
332
+ },
333
+ {
334
+ id: "2",
335
+ name: "Database Query",
336
+ type: "n8n-nodes-base.postgres",
337
+ typeVersion: 2.4,
338
+ position: [450, 300],
339
+ parameters: {
340
+ operation: "executeQuery",
341
+ query: "SELECT * FROM users"
342
+ },
343
+ // ✅ CORRECT - Error handling at node level
344
+ onError: "continueErrorOutput",
345
+ retryOnFail: true,
346
+ maxTries: 3,
347
+ waitBetweenTries: 2000
348
+ },
349
+ {
350
+ id: "3",
351
+ name: "Error Handler",
352
+ type: "n8n-nodes-base.slack",
353
+ typeVersion: 2.2,
354
+ position: [650, 450],
355
+ parameters: {
356
+ resource: "message",
357
+ operation: "post",
358
+ channel: "#errors",
359
+ text: "Database query failed!"
360
+ }
361
+ }
316
362
  ],
317
363
  connections: {
318
- "Webhook": {main: [[{node: "Slack", type: "main", index: 0}]]}
364
+ "Webhook": {
365
+ main: [[{node: "Database Query", type: "main", index: 0}]]
366
+ },
367
+ "Database Query": {
368
+ main: [[{node: "Success Handler", type: "main", index: 0}]],
369
+ error: [[{node: "Error Handler", type: "main", index: 0}]] // Error output
370
+ }
319
371
  }
320
372
  })`
321
373
  ],
@@ -323,17 +375,20 @@ exports.toolsDocumentation = {
323
375
  'Deploying workflows programmatically',
324
376
  'Automating workflow creation',
325
377
  'Migrating workflows between instances',
326
- 'Creating workflows from templates'
378
+ 'Creating workflows from templates',
379
+ 'Building error-resilient workflows'
327
380
  ],
328
381
  performance: 'Depends on n8n instance and network. Typically 100-500ms.',
329
382
  bestPractices: [
330
383
  'CRITICAL: Use node NAMES in connections, not IDs',
384
+ 'CRITICAL: Place error handling at NODE level, not in parameters',
331
385
  'Validate workflow before creating',
332
386
  'Use meaningful workflow names',
333
- 'Check n8n_health_check before creating',
334
- 'Handle API errors gracefully'
387
+ 'Add error handling to external service nodes',
388
+ 'Check n8n_health_check before creating'
335
389
  ],
336
390
  pitfalls: [
391
+ 'Placing error handling properties inside parameters object',
337
392
  'Using node IDs in connections breaks UI display',
338
393
  'Workflow not automatically activated',
339
394
  'Tags must exist (use tag IDs not names)',
@@ -348,15 +403,16 @@ exports.toolsDocumentation = {
348
403
  essentials: {
349
404
  description: 'Update workflows using diff operations - only send changes, not entire workflow',
350
405
  keyParameters: ['id', 'operations'],
351
- example: 'n8n_update_partial_workflow({id: "123", operations: [{type: "updateNode", nodeId: "Slack", updates: {...}}]})',
406
+ example: 'n8n_update_partial_workflow({id: "123", operations: [{type: "updateNode", nodeName: "Slack", changes: {onError: "continueRegularOutput"}}]})',
352
407
  performance: '80-90% more efficient than full updates',
353
408
  tips: [
354
409
  'Maximum 5 operations per request',
355
- 'Can reference nodes by name or ID'
410
+ 'Can reference nodes by name or ID',
411
+ 'Error handling properties go at NODE level, not inside parameters!'
356
412
  ]
357
413
  },
358
414
  full: {
359
- description: 'Update existing workflows using diff operations. Much more efficient than full updates as it only sends the changes. Supports 13 different operation types.',
415
+ description: 'Update existing workflows using diff operations. Much more efficient than full updates as it only sends the changes. Supports 13 different operation types.\n\n⚠️ CRITICAL: Error handling properties (onError, retryOnFail, maxTries, etc.) are NODE-LEVEL properties, not parameters!',
360
416
  parameters: {
361
417
  id: { type: 'string', description: 'Workflow ID to update', required: true },
362
418
  operations: { type: 'array', description: 'Array of diff operations (max 5)', required: true },
@@ -364,29 +420,50 @@ exports.toolsDocumentation = {
364
420
  },
365
421
  returns: 'Updated workflow with applied changes and operation results',
366
422
  examples: [
367
- `// Update node parameters
423
+ `// Update node parameters (properties inside parameters object)
424
+ n8n_update_partial_workflow({
425
+ id: "123",
426
+ operations: [{
427
+ type: "updateNode",
428
+ nodeName: "Slack",
429
+ changes: {
430
+ "parameters.channel": "#general", // Nested property
431
+ "parameters.text": "Hello world" // Nested property
432
+ }
433
+ }]
434
+ })`,
435
+ `// Update error handling (NODE-LEVEL properties, NOT inside parameters!)
368
436
  n8n_update_partial_workflow({
369
437
  id: "123",
370
438
  operations: [{
371
439
  type: "updateNode",
372
- nodeId: "Slack",
373
- updates: {parameters: {channel: "general"}}
440
+ nodeName: "HTTP Request",
441
+ changes: {
442
+ onError: "continueErrorOutput", // ✅ Correct - node level
443
+ retryOnFail: true, // ✅ Correct - node level
444
+ maxTries: 3, // ✅ Correct - node level
445
+ waitBetweenTries: 2000 // ✅ Correct - node level
446
+ }
374
447
  }]
375
448
  })`,
376
- `// Add connection between nodes
449
+ `// WRONG - Don't put error handling inside parameters!
450
+ // ❌ BAD: changes: {"parameters.onError": "continueErrorOutput"}
451
+ // ✅ GOOD: changes: {onError: "continueErrorOutput"}`,
452
+ `// Add error connection between nodes
377
453
  n8n_update_partial_workflow({
378
454
  id: "123",
379
455
  operations: [{
380
456
  type: "addConnection",
381
- from: "HTTP Request",
382
- to: "Slack",
383
- fromOutput: "main",
384
- toInput: "main"
457
+ source: "Database Query",
458
+ target: "Error Handler",
459
+ sourceOutput: "error", // Error output
460
+ targetInput: "main"
385
461
  }]
386
462
  })`
387
463
  ],
388
464
  useCases: [
389
465
  'Updating node configurations',
466
+ 'Adding error handling to nodes',
390
467
  'Adding/removing connections',
391
468
  'Enabling/disabling nodes',
392
469
  'Moving nodes in canvas',
@@ -394,13 +471,14 @@ n8n_update_partial_workflow({
394
471
  ],
395
472
  performance: 'Very efficient - only sends changes. 80-90% less data than full updates.',
396
473
  bestPractices: [
474
+ 'Error handling properties (onError, retryOnFail, etc.) go at NODE level, not in parameters',
475
+ 'Use dot notation for nested properties: "parameters.url"',
397
476
  'Batch related operations together',
398
477
  'Use validateOnly:true to test first',
399
- 'Reference nodes by name for clarity',
400
- 'Keep under 5 operations per request',
401
- 'Check operation results for success'
478
+ 'Reference nodes by name for clarity'
402
479
  ],
403
480
  pitfalls: [
481
+ 'Placing error handling properties inside parameters (common mistake!)',
404
482
  'Maximum 5 operations per request',
405
483
  'Some operations have dependencies',
406
484
  'Node must exist for update operations',
@@ -408,6 +486,641 @@ n8n_update_partial_workflow({
408
486
  ],
409
487
  relatedTools: ['n8n_get_workflow', 'n8n_update_full_workflow', 'validate_workflow']
410
488
  }
489
+ },
490
+ code_node_guide: {
491
+ name: 'code_node_guide',
492
+ category: 'code_node',
493
+ essentials: {
494
+ description: 'Comprehensive guide for writing Code node JavaScript and Python',
495
+ keyParameters: ['topic'],
496
+ example: 'tools_documentation({topic: "code_node_guide"})',
497
+ performance: 'Instant - returns documentation',
498
+ tips: [
499
+ 'Essential reading before writing Code node scripts',
500
+ 'Covers all built-in variables and helpers',
501
+ 'Includes common patterns and error handling'
502
+ ]
503
+ },
504
+ full: {
505
+ description: `Complete reference for the n8n Code node, covering JavaScript and Python execution environments, built-in variables, helper functions, and best practices.
506
+
507
+ ## Code Node Basics
508
+
509
+ The Code node allows custom JavaScript or Python code execution within workflows. It runs in a sandboxed environment with access to n8n-specific variables and helpers.
510
+
511
+ ### JavaScript Environment
512
+ - **ES2022 support** with async/await
513
+ - **Built-in libraries**:
514
+ - **luxon** (DateTime) - Date/time manipulation
515
+ - **jmespath** - JSON queries via $jmespath()
516
+ - **crypto** - Available via require('crypto') despite editor warnings!
517
+ - **Node.js globals**: Buffer, process.env (limited)
518
+ - **require() IS available** for built-in modules only (crypto, util, etc.)
519
+ - **No npm packages** - only Node.js built-ins and n8n-provided libraries
520
+
521
+ ### Python Environment
522
+ - **Python 3.10+** with standard library (Pyodide runtime)
523
+ - **No pip install** - standard library only
524
+ - **Variables use underscore prefix**: \`_input\`, \`_json\`, \`_jmespath\` (not \`$\`)
525
+ - **item.json is JsProxy**: Use \`.to_py()\` to convert to Python dict
526
+ - **Shared state** between Code nodes in same execution
527
+
528
+ ## Essential Variables
529
+
530
+ ### $input
531
+ Access to all incoming data:
532
+ \`\`\`javascript
533
+ // Get all items from all inputs
534
+ const allItems = $input.all(); // Returns: Item[][]
535
+
536
+ // Get items from specific input (0-indexed)
537
+ const firstInput = $input.all(0); // Returns: Item[]
538
+
539
+ // Get first item from first input
540
+ const firstItem = $input.first(); // Returns: Item
541
+
542
+ // Get last item from first input
543
+ const lastItem = $input.last(); // Returns: Item
544
+
545
+ // Get specific item by index
546
+ const item = $input.item(2); // Returns: Item at index 2
547
+ \`\`\`
548
+
549
+ ### items
550
+ Direct access to incoming items (legacy, prefer $input):
551
+ \`\`\`javascript
552
+ // items is equivalent to $input.all()[0]
553
+ for (const item of items) {
554
+ console.log(item.json); // Access JSON data
555
+ console.log(item.binary); // Access binary data
556
+ }
557
+ \`\`\`
558
+
559
+ ### $json
560
+ Shortcut to current item's JSON data (only in "Run Once for Each Item" mode):
561
+ \`\`\`javascript
562
+ // These are equivalent in single-item mode:
563
+ const value1 = $json.fieldName;
564
+ const value2 = items[0].json.fieldName;
565
+ \`\`\`
566
+
567
+ ### Accessing Other Nodes
568
+ Access data from other nodes using $('Node Name') syntax:
569
+ \`\`\`javascript
570
+ // Access another node's output - use $('Node Name') NOT $node
571
+ const prevData = $('Previous Node').all();
572
+ const firstItem = $('Previous Node').first();
573
+ const specificItem = $('Previous Node').item(0);
574
+
575
+ // Get node parameter
576
+ const webhookUrl = $('Webhook').params.path;
577
+
578
+ // Python uses underscore prefix
579
+ const pythonData = _('Previous Node').all();
580
+ \`\`\`
581
+
582
+ ⚠️ **Expression vs Code Node Syntax**:
583
+ - **Expressions**: \`{{$node['Previous Node'].json.field}}\`
584
+ - **Code Node**: \`$('Previous Node').first().json.field\`
585
+ - These are NOT interchangeable!
586
+
587
+ ### $workflow
588
+ Workflow metadata:
589
+ \`\`\`javascript
590
+ const workflowId = $workflow.id;
591
+ const workflowName = $workflow.name;
592
+ const isActive = $workflow.active;
593
+ \`\`\`
594
+
595
+ ### $execution
596
+ Execution context:
597
+ \`\`\`javascript
598
+ const executionId = $execution.id;
599
+ const executionMode = $execution.mode; // 'manual', 'trigger', etc.
600
+ const resumeUrl = $execution.resumeUrl; // For wait nodes
601
+ \`\`\`
602
+
603
+ ### $prevNode
604
+ Access to the immediate previous node:
605
+ \`\`\`javascript
606
+ const prevOutput = $prevNode.outputIndex; // Which output triggered this
607
+ const prevData = $prevNode.data; // Previous node's data
608
+ const prevName = $prevNode.name; // Previous node's name
609
+ \`\`\`
610
+
611
+ ## Helper Functions
612
+
613
+ ### Date/Time (Luxon)
614
+ \`\`\`javascript
615
+ // Current time
616
+ const now = DateTime.now();
617
+ const iso = now.toISO();
618
+
619
+ // Parse dates
620
+ const date = DateTime.fromISO('2024-01-01');
621
+ const formatted = date.toFormat('yyyy-MM-dd');
622
+
623
+ // Time math
624
+ const tomorrow = now.plus({ days: 1 });
625
+ const hourAgo = now.minus({ hours: 1 });
626
+ \`\`\`
627
+
628
+ ### JSON Queries (JMESPath)
629
+ \`\`\`javascript
630
+ // n8n uses $jmespath() - NOTE: parameter order is reversed from standard JMESPath!
631
+ const data = { users: [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }] };
632
+ const names = $jmespath(data, 'users[*].name'); // ['John', 'Jane']
633
+
634
+ // ⚠️ IMPORTANT: Numeric literals in filters need BACKTICKS in n8n!
635
+ const adults = $jmespath(data, 'users[?age >= \`18\`]'); // ✅ CORRECT - backticks around 18
636
+ const seniors = $jmespath(data, 'users[?age >= \`65\`]'); // ✅ CORRECT
637
+
638
+ // ❌ WRONG - This will cause a syntax error!
639
+ // const adults = $jmespath(data, 'users[?age >= 18]'); // Missing backticks
640
+
641
+ // More filter examples with proper backticks:
642
+ const expensive = $jmespath(items, '[?price > \`100\`]');
643
+ const inStock = $jmespath(products, '[?quantity >= \`1\`]');
644
+ const highPriority = $jmespath(tasks, '[?priority == \`1\`]');
645
+
646
+ // String comparisons don't need backticks
647
+ const activeUsers = $jmespath(data, 'users[?status == "active"]');
648
+
649
+ // Python uses underscore prefix
650
+ const pythonAdults = _jmespath(data, 'users[?age >= \`18\`]');
651
+ \`\`\`
652
+
653
+ ⚠️ **CRITICAL DIFFERENCES** from standard JMESPath:
654
+ 1. **Parameter order is REVERSED**:
655
+ - **Expression**: \`{{$jmespath("query", data)}}\`
656
+ - **Code Node**: \`$jmespath(data, "query")\`
657
+ 2. **Numeric literals in filters MUST use backticks**: \`[?age >= \`18\`]\`
658
+ - This is n8n-specific and differs from standard JMESPath documentation!
659
+
660
+ ### Available Functions and Libraries
661
+
662
+ #### Built-in Node.js Modules (via require)
663
+ \`\`\`javascript
664
+ // ✅ These modules ARE available via require():
665
+ const crypto = require('crypto'); // Cryptographic functions
666
+ const util = require('util'); // Utility functions
667
+ const querystring = require('querystring'); // URL query string utilities
668
+
669
+ // Example: Generate secure random token
670
+ const crypto = require('crypto');
671
+ const token = crypto.randomBytes(32).toString('hex');
672
+ const uuid = crypto.randomUUID();
673
+ \`\`\`
674
+
675
+ **Note**: The editor may show errors for require() but it WORKS at runtime!
676
+
677
+ #### Standalone Functions (Global Scope)
678
+ \`\`\`javascript
679
+ // ✅ Workflow static data - persists between executions
680
+ // IMPORTANT: These are standalone functions, NOT methods on $helpers!
681
+ const staticData = $getWorkflowStaticData('global'); // Global static data
682
+ const nodeData = $getWorkflowStaticData('node'); // Node-specific data
683
+
684
+ // Example: Counter that persists
685
+ const staticData = $getWorkflowStaticData('global');
686
+ staticData.counter = (staticData.counter || 0) + 1;
687
+
688
+ // ❌ WRONG - This will cause "$helpers is not defined" error:
689
+ // const data = $helpers.getWorkflowStaticData('global');
690
+
691
+ // JMESPath queries - note the parameter order!
692
+ const result = $jmespath(data, 'users[*].name');
693
+ \`\`\`
694
+
695
+ #### $helpers Object (When Available)
696
+ \`\`\`javascript
697
+ // Some n8n versions provide $helpers with these methods:
698
+ // (Always test availability in your n8n instance)
699
+
700
+ // HTTP requests
701
+ const response = await $helpers.httpRequest({
702
+ method: 'GET',
703
+ url: 'https://api.example.com/data',
704
+ headers: { 'Authorization': 'Bearer token' }
705
+ });
706
+
707
+ // Binary data preparation
708
+ const binaryData = await $helpers.prepareBinaryData(
709
+ Buffer.from('content'),
710
+ 'file.txt',
711
+ 'text/plain'
712
+ );
713
+
714
+ // Check if $helpers exists before using:
715
+ if (typeof $helpers !== 'undefined' && $helpers.httpRequest) {
716
+ // Use $helpers.httpRequest
717
+ } else {
718
+ throw new Error('HTTP requests not available in this n8n version');
719
+ }
720
+ \`\`\`
721
+
722
+ #### Important Notes:
723
+ - **$getWorkflowStaticData()** is ALWAYS a standalone function
724
+ - **require()** works for built-in Node.js modules despite editor warnings
725
+ - **$helpers** availability varies by n8n version - always check first
726
+ - Python uses underscore prefix: \`_getWorkflowStaticData()\`, \`_jmespath()\`
727
+ - Editor red underlines are often false positives - test at runtime!
728
+
729
+ ## Return Format
730
+
731
+ Code nodes MUST return an array of objects with 'json' property:
732
+
733
+ \`\`\`javascript
734
+ // ✅ CORRECT - Array of objects with json property
735
+ return [
736
+ { json: { id: 1, name: 'Item 1' } },
737
+ { json: { id: 2, name: 'Item 2' } }
738
+ ];
739
+
740
+ // ✅ CORRECT - Single item (still wrapped in array)
741
+ return [{ json: { result: 'success' } }];
742
+
743
+ // ✅ CORRECT - With binary data
744
+ return [{
745
+ json: { filename: 'report.pdf' },
746
+ binary: {
747
+ data: {
748
+ data: base64String,
749
+ mimeType: 'application/pdf',
750
+ fileName: 'report.pdf'
751
+ }
752
+ }
753
+ }];
754
+
755
+ // ❌ WRONG - Not an array
756
+ return { json: { result: 'success' } };
757
+
758
+ // ❌ WRONG - No json property
759
+ return [{ result: 'success' }];
760
+
761
+ // ❌ WRONG - Not wrapped in object
762
+ return ['item1', 'item2'];
763
+ \`\`\`
764
+
765
+ ## Common Patterns
766
+
767
+ ### Data Transformation
768
+ \`\`\`javascript
769
+ // Transform all items
770
+ const transformedItems = [];
771
+ for (const item of items) {
772
+ transformedItems.push({
773
+ json: {
774
+ ...item.json,
775
+ processed: true,
776
+ timestamp: DateTime.now().toISO(),
777
+ uppercaseName: item.json.name?.toUpperCase()
778
+ }
779
+ });
780
+ }
781
+ return transformedItems;
782
+ \`\`\`
783
+
784
+ ### Filtering Items
785
+ \`\`\`javascript
786
+ // Filter items based on condition
787
+ return items
788
+ .filter(item => item.json.status === 'active')
789
+ .map(item => ({ json: item.json }));
790
+ \`\`\`
791
+
792
+ ### Aggregation
793
+ \`\`\`javascript
794
+ // Aggregate data from all items
795
+ const total = items.reduce((sum, item) => sum + (item.json.amount || 0), 0);
796
+ const average = total / items.length;
797
+
798
+ return [{
799
+ json: {
800
+ total,
801
+ average,
802
+ count: items.length,
803
+ items: items.map(i => i.json)
804
+ }
805
+ }];
806
+ \`\`\`
807
+
808
+ ### Error Handling
809
+ \`\`\`javascript
810
+ // Safe data access with defaults
811
+ const results = [];
812
+ for (const item of items) {
813
+ try {
814
+ const value = item.json?.nested?.field || 'default';
815
+ results.push({
816
+ json: {
817
+ processed: value,
818
+ status: 'success'
819
+ }
820
+ });
821
+ } catch (error) {
822
+ results.push({
823
+ json: {
824
+ error: error.message,
825
+ status: 'failed',
826
+ originalItem: item.json
827
+ }
828
+ });
829
+ }
830
+ }
831
+ return results;
832
+ \`\`\`
833
+
834
+ ### Working with APIs
835
+ \`\`\`javascript
836
+ // Make HTTP request and process response
837
+ try {
838
+ const response = await $helpers.httpRequest({
839
+ method: 'POST',
840
+ url: 'https://api.example.com/process',
841
+ body: {
842
+ data: items.map(item => item.json)
843
+ },
844
+ headers: {
845
+ 'Content-Type': 'application/json'
846
+ }
847
+ });
848
+
849
+ return [{ json: response }];
850
+ } catch (error) {
851
+ throw new Error(\`API request failed: \${error.message}\`);
852
+ }
853
+ \`\`\`
854
+
855
+ ### Async Operations
856
+ \`\`\`javascript
857
+ // Process items with async operations
858
+ const results = [];
859
+ for (const item of items) {
860
+ // Simulate async operation
861
+ await new Promise(resolve => setTimeout(resolve, 100));
862
+
863
+ results.push({
864
+ json: {
865
+ ...item.json,
866
+ processedAt: new Date().toISOString()
867
+ }
868
+ });
869
+ }
870
+ return results;
871
+ \`\`\`
872
+
873
+ ### Webhook Data Access (CRITICAL!)
874
+ \`\`\`javascript
875
+ // ⚠️ WEBHOOK DATA IS NESTED UNDER 'body' PROPERTY!
876
+ // This is a common source of errors in webhook-triggered workflows
877
+
878
+ // ❌ WRONG - This will be undefined for webhook data:
879
+ const command = items[0].json.testCommand;
880
+
881
+ // ✅ CORRECT - Webhook data is wrapped in 'body':
882
+ const command = items[0].json.body.testCommand;
883
+
884
+ // Complete webhook data processing example:
885
+ const webhookData = items[0].json.body; // Get the actual webhook payload
886
+ const headers = items[0].json.headers; // HTTP headers are separate
887
+ const query = items[0].json.query; // Query parameters are separate
888
+
889
+ // Process webhook payload
890
+ return [{
891
+ json: {
892
+ command: webhookData.testCommand,
893
+ user: webhookData.user,
894
+ timestamp: DateTime.now().toISO(),
895
+ requestId: headers['x-request-id'],
896
+ source: query.source || 'unknown'
897
+ }
898
+ }];
899
+
900
+ // For other trigger nodes (non-webhook), data is directly under json:
901
+ // - Schedule Trigger: items[0].json contains timestamp
902
+ // - Database Trigger: items[0].json contains row data
903
+ // - File Trigger: items[0].json contains file info
904
+ \`\`\`
905
+
906
+ ## Python Code Examples
907
+
908
+ ### Basic Python Structure
909
+ \`\`\`python
910
+ import json
911
+ from datetime import datetime
912
+
913
+ # Access items - Python uses underscore prefix for built-in variables
914
+ results = []
915
+ for item in _input.all():
916
+ # IMPORTANT: item.json is NOT a standard Python dict!
917
+ # Use to_py() to convert to a proper Python dict
918
+ processed_item = item.json.to_py() # Converts JsProxy to Python dict
919
+ processed_item['timestamp'] = datetime.now().isoformat()
920
+ results.append({'json': processed_item})
921
+
922
+ return results
923
+ \`\`\`
924
+
925
+ ### Python Data Processing
926
+ \`\`\`python
927
+ # Aggregate data - use _input.all() to get items
928
+ items = _input.all()
929
+ total = sum(item.json.get('amount', 0) for item in items)
930
+ average = total / len(items) if items else 0
931
+
932
+ # For safe dict operations, convert JsProxy to Python dict
933
+ safe_items = []
934
+ for item in items:
935
+ # Convert JsProxy to dict to avoid KeyError with null values
936
+ safe_dict = item.json.to_py()
937
+ safe_items.append(safe_dict)
938
+
939
+ # Return aggregated result
940
+ return [{
941
+ 'json': {
942
+ 'total': total,
943
+ 'average': average,
944
+ 'count': len(items),
945
+ 'processed_at': datetime.now().isoformat(),
946
+ 'items': safe_items # Now these are proper Python dicts
947
+ }
948
+ }]
949
+ \`\`\`
950
+
951
+ ## Code Node as AI Tool
952
+
953
+ Code nodes can be used as custom tools for AI agents:
954
+
955
+ \`\`\`javascript
956
+ // Code node configured as AI tool
957
+ // Name: "Calculate Discount"
958
+ // Description: "Calculates discount based on quantity"
959
+
960
+ const quantity = $json.quantity || 1;
961
+ const basePrice = $json.price || 0;
962
+
963
+ let discount = 0;
964
+ if (quantity >= 100) discount = 0.20;
965
+ else if (quantity >= 50) discount = 0.15;
966
+ else if (quantity >= 20) discount = 0.10;
967
+ else if (quantity >= 10) discount = 0.05;
968
+
969
+ const discountAmount = basePrice * quantity * discount;
970
+ const finalPrice = (basePrice * quantity) - discountAmount;
971
+
972
+ return [{
973
+ json: {
974
+ quantity,
975
+ basePrice,
976
+ discountPercentage: discount * 100,
977
+ discountAmount,
978
+ finalPrice,
979
+ savings: discountAmount
980
+ }
981
+ }];
982
+ \`\`\`
983
+
984
+ ## Security Considerations
985
+
986
+ ### Available Security Features
987
+ \`\`\`javascript
988
+ // ✅ Crypto IS available despite editor warnings!
989
+ const crypto = require('crypto');
990
+
991
+ // Generate secure random values
992
+ const randomBytes = crypto.randomBytes(32);
993
+ const randomUUID = crypto.randomUUID();
994
+
995
+ // Create hashes
996
+ const hash = crypto.createHash('sha256')
997
+ .update('data to hash')
998
+ .digest('hex');
999
+
1000
+ // HMAC for signatures
1001
+ const hmac = crypto.createHmac('sha256', 'secret-key')
1002
+ .update('data to sign')
1003
+ .digest('hex');
1004
+ \`\`\`
1005
+
1006
+ ### Banned Operations
1007
+ - No file system access (fs module) - except read-only for some paths
1008
+ - No network requests except via $helpers.httpRequest
1009
+ - No child process execution
1010
+ - No external npm packages (only built-in Node.js modules)
1011
+ - No eval() or Function() constructor
1012
+
1013
+ ### Safe Practices
1014
+ \`\`\`javascript
1015
+ // ✅ SAFE - Use crypto for secure operations
1016
+ const crypto = require('crypto');
1017
+ const token = crypto.randomBytes(32).toString('hex');
1018
+
1019
+ // ✅ SAFE - Use built-in JSON parsing
1020
+ const parsed = JSON.parse(jsonString);
1021
+
1022
+ // ❌ UNSAFE - Never use eval
1023
+ const parsed = eval('(' + jsonString + ')');
1024
+
1025
+ // ✅ SAFE - Validate input
1026
+ if (typeof item.json.userId !== 'string') {
1027
+ throw new Error('userId must be a string');
1028
+ }
1029
+
1030
+ // ✅ SAFE - Sanitize for logs
1031
+ const safeLog = String(userInput).substring(0, 100);
1032
+
1033
+ // ✅ SAFE - Time-safe comparison for secrets
1034
+ const expectedToken = 'abc123';
1035
+ const providedToken = item.json.token;
1036
+ const tokensMatch = crypto.timingSafeEqual(
1037
+ Buffer.from(expectedToken),
1038
+ Buffer.from(providedToken || '')
1039
+ );
1040
+ \`\`\`
1041
+
1042
+ ## Debugging Tips
1043
+
1044
+ ### Console Output
1045
+ \`\`\`javascript
1046
+ // Console.log appears in n8n execution logs
1047
+ console.log('Processing item:', item.json.id);
1048
+ console.error('Error details:', error);
1049
+
1050
+ // Return debug info in development
1051
+ return [{
1052
+ json: {
1053
+ result: processedData,
1054
+ debug: {
1055
+ itemCount: items.length,
1056
+ executionId: $execution.id,
1057
+ timestamp: new Date().toISOString()
1058
+ }
1059
+ }
1060
+ }];
1061
+ \`\`\`
1062
+
1063
+ ### Error Messages
1064
+ \`\`\`javascript
1065
+ // Provide helpful error context
1066
+ if (!item.json.requiredField) {
1067
+ throw new Error(\`Missing required field 'requiredField' in item \${items.indexOf(item)}\`);
1068
+ }
1069
+
1070
+ // Include original data in errors
1071
+ try {
1072
+ // processing...
1073
+ } catch (error) {
1074
+ throw new Error(\`Failed to process item \${item.json.id}: \${error.message}\`);
1075
+ }
1076
+ \`\`\`
1077
+
1078
+ ## Performance Best Practices
1079
+
1080
+ 1. **Avoid nested loops** when possible
1081
+ 2. **Use array methods** (map, filter, reduce) for clarity
1082
+ 3. **Limit HTTP requests** - batch when possible
1083
+ 4. **Return early** for error conditions
1084
+ 5. **Keep state minimal** - Code nodes are stateless between executions
1085
+
1086
+ ## Common Mistakes to Avoid
1087
+
1088
+ 1. **Forgetting to return an array**
1089
+ 2. **Not wrapping in json property**
1090
+ 3. **Modifying items array directly**
1091
+ 4. **Using undefined variables**
1092
+ 5. **Infinite loops with while statements**
1093
+ 6. **Not handling missing data gracefully**
1094
+ 7. **Forgetting await for async operations**`,
1095
+ parameters: {
1096
+ topic: { type: 'string', description: 'Specific Code node topic (optional)', required: false }
1097
+ },
1098
+ returns: 'Comprehensive Code node documentation and examples',
1099
+ examples: [
1100
+ 'tools_documentation({topic: "code_node_guide"}) - Full guide',
1101
+ 'tools_documentation({topic: "code_node_guide", depth: "full"}) - Complete reference'
1102
+ ],
1103
+ useCases: [
1104
+ 'Learning Code node capabilities',
1105
+ 'Understanding built-in variables',
1106
+ 'Finding the right helper function',
1107
+ 'Debugging Code node issues',
1108
+ 'Building custom AI tools'
1109
+ ],
1110
+ performance: 'Instant - returns static documentation',
1111
+ bestPractices: [
1112
+ 'Read before writing Code nodes',
1113
+ 'Reference for variable names',
1114
+ 'Copy examples as starting points',
1115
+ 'Check security considerations'
1116
+ ],
1117
+ pitfalls: [
1118
+ 'Not all Node.js features available',
1119
+ 'Python has limited libraries',
1120
+ 'State not preserved between executions'
1121
+ ],
1122
+ relatedTools: ['get_node_essentials', 'validate_node_operation', 'get_node_for_task']
1123
+ }
411
1124
  }
412
1125
  };
413
1126
  function getToolDocumentation(toolName, depth = 'essentials') {
@@ -481,6 +1194,7 @@ Welcome! Here's how to efficiently work with n8n nodes:
481
1194
 
482
1195
  ## Get Help
483
1196
  - tools_documentation({topic: "search_nodes"}) - Get help for specific tool
1197
+ - tools_documentation({topic: "code_node_guide"}) - Essential Code node reference
484
1198
  - tools_documentation({topic: "overview", depth: "full"}) - See complete guide
485
1199
  - list_tasks() - See available task templates
486
1200
 
@@ -547,6 +1261,72 @@ validate_workflow(workflow)
547
1261
  n8n_create_workflow(workflow)
548
1262
  \`\`\`
549
1263
 
1264
+ ### Working with Code Nodes
1265
+ The Code node is essential for custom logic. Always reference the guide:
1266
+ \`\`\`javascript
1267
+ // Get comprehensive Code node documentation
1268
+ tools_documentation({topic: "code_node_guide"})
1269
+
1270
+ // Common Code node pattern
1271
+ get_node_essentials("n8n-nodes-base.code")
1272
+ // Returns minimal config with JavaScript/Python examples
1273
+
1274
+ // Validate Code node configuration
1275
+ validate_node_operation("n8n-nodes-base.code", {
1276
+ language: "javaScript",
1277
+ jsCode: "return items.map(item => ({json: {...item.json, processed: true}}))"
1278
+ })
1279
+ \`\`\`
1280
+
1281
+ ### Node-Level Properties Reference
1282
+ ⚠️ **CRITICAL**: These properties go at the NODE level, not inside parameters!
1283
+
1284
+ \`\`\`javascript
1285
+ {
1286
+ // Required properties
1287
+ "id": "unique_id",
1288
+ "name": "Node Name",
1289
+ "type": "n8n-nodes-base.postgres",
1290
+ "typeVersion": 2.6,
1291
+ "position": [450, 300],
1292
+ "parameters": { /* operation-specific params */ },
1293
+
1294
+ // Optional properties (all at node level!)
1295
+ "credentials": {
1296
+ "postgres": {
1297
+ "id": "cred-id",
1298
+ "name": "My Postgres"
1299
+ }
1300
+ },
1301
+ "disabled": false, // Disable node execution
1302
+ "notes": "Internal note", // Node documentation
1303
+ "notesInFlow": true, // Show notes on canvas
1304
+ "executeOnce": true, // Execute only once per run
1305
+
1306
+ // Error handling (at node level!)
1307
+ "onError": "continueErrorOutput", // or "continueRegularOutput", "stopWorkflow"
1308
+ "retryOnFail": true,
1309
+ "maxTries": 3,
1310
+ "waitBetweenTries": 2000,
1311
+ "alwaysOutputData": true,
1312
+
1313
+ // Deprecated (use onError instead)
1314
+ "continueOnFail": false
1315
+ }
1316
+ \`\`\`
1317
+
1318
+ **Common properties explained:**
1319
+ - **credentials**: Links to credential sets (use credential ID and name)
1320
+ - **disabled**: Node won't execute when true
1321
+ - **notes**: Internal documentation for the node
1322
+ - **notesInFlow**: Display notes on workflow canvas
1323
+ - **executeOnce**: Execute node only once even with multiple input items
1324
+ - **onError**: Modern error handling - what to do on failure
1325
+ - **retryOnFail**: Automatically retry failed executions
1326
+ - **maxTries**: Number of retry attempts (with retryOnFail)
1327
+ - **waitBetweenTries**: Milliseconds between retries
1328
+ - **alwaysOutputData**: Output data even on error (for debugging)
1329
+
550
1330
  ### Using AI Tools
551
1331
  Any node can be an AI tool! Connect it to an AI Agent's ai_tool port:
552
1332
  \`\`\`javascript