n8n-mcp 2.7.9 → 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.
- package/README.md +20 -42
- package/data/nodes.db +0 -0
- package/dist/database/database-adapter.d.ts +1 -0
- package/dist/database/database-adapter.d.ts.map +1 -1
- package/dist/database/database-adapter.js +20 -0
- package/dist/database/database-adapter.js.map +1 -1
- package/dist/http-server-single-session.d.ts +2 -0
- package/dist/http-server-single-session.d.ts.map +1 -1
- package/dist/http-server-single-session.js +65 -11
- package/dist/http-server-single-session.js.map +1 -1
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +41 -8
- package/dist/http-server.js.map +1 -1
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +334 -10
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools-documentation.d.ts.map +1 -1
- package/dist/mcp/tools-documentation.js +832 -52
- package/dist/mcp/tools-documentation.js.map +1 -1
- package/dist/mcp/tools-n8n-manager.d.ts.map +1 -1
- package/dist/mcp/tools-n8n-manager.js +10 -112
- package/dist/mcp/tools-n8n-manager.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +42 -36
- package/dist/mcp/tools.js.map +1 -1
- package/dist/scripts/fetch-templates.d.ts.map +1 -1
- package/dist/scripts/fetch-templates.js +37 -0
- package/dist/scripts/fetch-templates.js.map +1 -1
- package/dist/scripts/test-error-handling-validation.d.ts +3 -0
- package/dist/scripts/test-error-handling-validation.d.ts.map +1 -0
- package/dist/scripts/test-error-handling-validation.js +340 -0
- package/dist/scripts/test-error-handling-validation.js.map +1 -0
- package/dist/scripts/test-node-level-properties.d.ts +3 -0
- package/dist/scripts/test-node-level-properties.d.ts.map +1 -0
- package/dist/scripts/test-node-level-properties.js +196 -0
- package/dist/scripts/test-node-level-properties.js.map +1 -0
- package/dist/services/config-validator.d.ts +2 -2
- package/dist/services/config-validator.d.ts.map +1 -1
- package/dist/services/config-validator.js +123 -5
- package/dist/services/config-validator.js.map +1 -1
- package/dist/services/enhanced-config-validator.d.ts +2 -0
- package/dist/services/enhanced-config-validator.d.ts.map +1 -1
- package/dist/services/enhanced-config-validator.js +31 -1
- package/dist/services/enhanced-config-validator.js.map +1 -1
- package/dist/services/example-generator.d.ts.map +1 -1
- package/dist/services/example-generator.js +442 -28
- package/dist/services/example-generator.js.map +1 -1
- package/dist/services/n8n-validation.d.ts +10 -10
- package/dist/services/node-specific-validators.d.ts +8 -1
- package/dist/services/node-specific-validators.d.ts.map +1 -1
- package/dist/services/node-specific-validators.js +608 -59
- package/dist/services/node-specific-validators.js.map +1 -1
- package/dist/services/task-templates.d.ts +1 -0
- package/dist/services/task-templates.d.ts.map +1 -1
- package/dist/services/task-templates.js +858 -16
- package/dist/services/task-templates.js.map +1 -1
- package/dist/services/workflow-diff-engine.d.ts.map +1 -1
- package/dist/services/workflow-diff-engine.js +1 -0
- package/dist/services/workflow-diff-engine.js.map +1 -1
- package/dist/services/workflow-validator.d.ts +9 -0
- package/dist/services/workflow-validator.d.ts.map +1 -1
- package/dist/services/workflow-validator.js +270 -0
- package/dist/services/workflow-validator.js.map +1 -1
- package/dist/sse-server.d.ts +8 -0
- package/dist/sse-server.d.ts.map +1 -0
- package/dist/sse-server.js +652 -0
- package/dist/sse-server.js.map +1 -0
- package/dist/templates/template-repository.d.ts +4 -0
- package/dist/templates/template-repository.d.ts.map +1 -1
- package/dist/templates/template-repository.js +119 -7
- package/dist/templates/template-repository.js.map +1 -1
- package/dist/templates/template-service.d.ts.map +1 -1
- package/dist/templates/template-service.js +2 -0
- package/dist/templates/template-service.js.map +1 -1
- package/dist/types/n8n-api.d.ts +2 -1
- package/dist/types/n8n-api.d.ts.map +1 -1
- package/dist/types/n8n-api.js.map +1 -1
- package/dist/types/sse.d.ts +42 -0
- package/dist/types/sse.d.ts.map +1 -0
- package/dist/types/sse.js +3 -0
- package/dist/types/sse.js.map +1 -0
- package/dist/utils/sse-session-manager.d.ts +23 -0
- package/dist/utils/sse-session-manager.d.ts.map +1 -0
- package/dist/utils/sse-session-manager.js +178 -0
- package/dist/utils/sse-session-manager.js.map +1 -0
- 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
|
|
15
|
-
keyParameters: ['query', 'limit'],
|
|
16
|
-
example: 'search_nodes({query: "
|
|
17
|
-
performance: 'Fast -
|
|
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
|
-
'
|
|
20
|
-
'
|
|
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: '
|
|
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
|
|
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
|
|
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: "
|
|
32
|
-
'search_nodes({query: "
|
|
33
|
-
'search_nodes({query: "
|
|
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
|
|
37
|
-
'Discovering
|
|
38
|
-
'
|
|
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: '
|
|
43
|
+
performance: 'FTS5: <20ms for most queries. Falls back to optimized LIKE queries if FTS5 unavailable.',
|
|
41
44
|
bestPractices: [
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'Use
|
|
45
|
-
'
|
|
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
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
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
|
|
64
|
+
description: 'Get 10-20 key properties with examples',
|
|
60
65
|
keyParameters: ['nodeType'],
|
|
61
|
-
example: 'get_node_essentials("
|
|
62
|
-
performance: '
|
|
66
|
+
example: 'get_node_essentials("nodes-base.slack")',
|
|
67
|
+
performance: '<5KB vs 100KB+',
|
|
63
68
|
tips: [
|
|
64
|
-
'Use this
|
|
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
|
-
|
|
312
|
-
|
|
316
|
+
`// Basic workflow with proper error handling
|
|
317
|
+
n8n_create_workflow({
|
|
318
|
+
name: "Slack Notification with Error Handling",
|
|
313
319
|
nodes: [
|
|
314
|
-
{
|
|
315
|
-
|
|
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": {
|
|
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
|
-
'
|
|
334
|
-
'
|
|
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",
|
|
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
|
-
|
|
373
|
-
|
|
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
|
-
`//
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|