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.
Files changed (87) 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/http-server-single-session.d.ts +2 -0
  8. package/dist/http-server-single-session.d.ts.map +1 -1
  9. package/dist/http-server-single-session.js +65 -11
  10. package/dist/http-server-single-session.js.map +1 -1
  11. package/dist/http-server.d.ts.map +1 -1
  12. package/dist/http-server.js +41 -8
  13. package/dist/http-server.js.map +1 -1
  14. package/dist/mcp/server.d.ts +7 -0
  15. package/dist/mcp/server.d.ts.map +1 -1
  16. package/dist/mcp/server.js +334 -10
  17. package/dist/mcp/server.js.map +1 -1
  18. package/dist/mcp/tools-documentation.d.ts.map +1 -1
  19. package/dist/mcp/tools-documentation.js +832 -52
  20. package/dist/mcp/tools-documentation.js.map +1 -1
  21. package/dist/mcp/tools-n8n-manager.d.ts.map +1 -1
  22. package/dist/mcp/tools-n8n-manager.js +10 -112
  23. package/dist/mcp/tools-n8n-manager.js.map +1 -1
  24. package/dist/mcp/tools.d.ts.map +1 -1
  25. package/dist/mcp/tools.js +42 -36
  26. package/dist/mcp/tools.js.map +1 -1
  27. package/dist/scripts/fetch-templates.d.ts.map +1 -1
  28. package/dist/scripts/fetch-templates.js +37 -0
  29. package/dist/scripts/fetch-templates.js.map +1 -1
  30. package/dist/scripts/test-error-handling-validation.d.ts +3 -0
  31. package/dist/scripts/test-error-handling-validation.d.ts.map +1 -0
  32. package/dist/scripts/test-error-handling-validation.js +340 -0
  33. package/dist/scripts/test-error-handling-validation.js.map +1 -0
  34. package/dist/scripts/test-node-level-properties.d.ts +3 -0
  35. package/dist/scripts/test-node-level-properties.d.ts.map +1 -0
  36. package/dist/scripts/test-node-level-properties.js +196 -0
  37. package/dist/scripts/test-node-level-properties.js.map +1 -0
  38. package/dist/services/config-validator.d.ts +2 -2
  39. package/dist/services/config-validator.d.ts.map +1 -1
  40. package/dist/services/config-validator.js +123 -5
  41. package/dist/services/config-validator.js.map +1 -1
  42. package/dist/services/enhanced-config-validator.d.ts +2 -0
  43. package/dist/services/enhanced-config-validator.d.ts.map +1 -1
  44. package/dist/services/enhanced-config-validator.js +31 -1
  45. package/dist/services/enhanced-config-validator.js.map +1 -1
  46. package/dist/services/example-generator.d.ts.map +1 -1
  47. package/dist/services/example-generator.js +442 -28
  48. package/dist/services/example-generator.js.map +1 -1
  49. package/dist/services/n8n-validation.d.ts +10 -10
  50. package/dist/services/node-specific-validators.d.ts +8 -1
  51. package/dist/services/node-specific-validators.d.ts.map +1 -1
  52. package/dist/services/node-specific-validators.js +608 -59
  53. package/dist/services/node-specific-validators.js.map +1 -1
  54. package/dist/services/task-templates.d.ts +1 -0
  55. package/dist/services/task-templates.d.ts.map +1 -1
  56. package/dist/services/task-templates.js +858 -16
  57. package/dist/services/task-templates.js.map +1 -1
  58. package/dist/services/workflow-diff-engine.d.ts.map +1 -1
  59. package/dist/services/workflow-diff-engine.js +1 -0
  60. package/dist/services/workflow-diff-engine.js.map +1 -1
  61. package/dist/services/workflow-validator.d.ts +9 -0
  62. package/dist/services/workflow-validator.d.ts.map +1 -1
  63. package/dist/services/workflow-validator.js +270 -0
  64. package/dist/services/workflow-validator.js.map +1 -1
  65. package/dist/sse-server.d.ts +8 -0
  66. package/dist/sse-server.d.ts.map +1 -0
  67. package/dist/sse-server.js +652 -0
  68. package/dist/sse-server.js.map +1 -0
  69. package/dist/templates/template-repository.d.ts +4 -0
  70. package/dist/templates/template-repository.d.ts.map +1 -1
  71. package/dist/templates/template-repository.js +119 -7
  72. package/dist/templates/template-repository.js.map +1 -1
  73. package/dist/templates/template-service.d.ts.map +1 -1
  74. package/dist/templates/template-service.js +2 -0
  75. package/dist/templates/template-service.js.map +1 -1
  76. package/dist/types/n8n-api.d.ts +2 -1
  77. package/dist/types/n8n-api.d.ts.map +1 -1
  78. package/dist/types/n8n-api.js.map +1 -1
  79. package/dist/types/sse.d.ts +42 -0
  80. package/dist/types/sse.d.ts.map +1 -0
  81. package/dist/types/sse.js +3 -0
  82. package/dist/types/sse.js.map +1 -0
  83. package/dist/utils/sse-session-manager.d.ts +23 -0
  84. package/dist/utils/sse-session-manager.d.ts.map +1 -0
  85. package/dist/utils/sse-session-manager.js +178 -0
  86. package/dist/utils/sse-session-manager.js.map +1 -0
  87. package/package.json +1 -1
@@ -13,6 +13,9 @@ class TaskTemplates {
13
13
  static getTaskTemplate(task) {
14
14
  return this.templates[task];
15
15
  }
16
+ static getTemplate(task) {
17
+ return this.getTaskTemplate(task);
18
+ }
16
19
  static searchTasks(keyword) {
17
20
  const lower = keyword.toLowerCase();
18
21
  return Object.entries(this.templates)
@@ -23,13 +26,14 @@ class TaskTemplates {
23
26
  }
24
27
  static getTaskCategories() {
25
28
  return {
26
- 'HTTP/API': ['get_api_data', 'post_json_request', 'call_api_with_auth'],
27
- 'Webhooks': ['receive_webhook', 'webhook_with_response'],
28
- 'Database': ['query_postgres', 'insert_postgres_data'],
29
- 'AI/LangChain': ['chat_with_ai', 'ai_agent_workflow', 'multi_tool_ai_agent'],
30
- 'Data Processing': ['transform_data', 'filter_data'],
29
+ 'HTTP/API': ['get_api_data', 'post_json_request', 'call_api_with_auth', 'api_call_with_retry'],
30
+ 'Webhooks': ['receive_webhook', 'webhook_with_response', 'webhook_with_error_handling', 'process_webhook_data'],
31
+ 'Database': ['query_postgres', 'insert_postgres_data', 'database_transaction_safety'],
32
+ 'AI/LangChain': ['chat_with_ai', 'ai_agent_workflow', 'multi_tool_ai_agent', 'ai_rate_limit_handling'],
33
+ 'Data Processing': ['transform_data', 'filter_data', 'fault_tolerant_processing', 'process_webhook_data'],
31
34
  'Communication': ['send_slack_message', 'send_email'],
32
- 'AI Tool Usage': ['use_google_sheets_as_tool', 'use_slack_as_tool', 'multi_tool_ai_agent']
35
+ 'AI Tool Usage': ['use_google_sheets_as_tool', 'use_slack_as_tool', 'multi_tool_ai_agent'],
36
+ 'Error Handling': ['modern_error_handling_patterns', 'api_call_with_retry', 'fault_tolerant_processing', 'webhook_with_error_handling', 'database_transaction_safety', 'ai_rate_limit_handling']
33
37
  };
34
38
  }
35
39
  }
@@ -42,7 +46,12 @@ TaskTemplates.templates = {
42
46
  configuration: {
43
47
  method: 'GET',
44
48
  url: '',
45
- authentication: 'none'
49
+ authentication: 'none',
50
+ onError: 'continueRegularOutput',
51
+ retryOnFail: true,
52
+ maxTries: 3,
53
+ waitBetweenTries: 1000,
54
+ alwaysOutputData: true
46
55
  },
47
56
  userMustProvide: [
48
57
  {
@@ -61,6 +70,11 @@ TaskTemplates.templates = {
61
70
  property: 'sendHeaders',
62
71
  description: 'Add custom headers if needed',
63
72
  when: 'API requires specific headers'
73
+ },
74
+ {
75
+ property: 'alwaysOutputData',
76
+ description: 'Set to true to capture error responses',
77
+ when: 'Need to debug API errors'
64
78
  }
65
79
  ]
66
80
  },
@@ -74,7 +88,12 @@ TaskTemplates.templates = {
74
88
  sendBody: true,
75
89
  contentType: 'json',
76
90
  specifyBody: 'json',
77
- jsonBody: ''
91
+ jsonBody: '',
92
+ onError: 'continueRegularOutput',
93
+ retryOnFail: true,
94
+ maxTries: 2,
95
+ waitBetweenTries: 1000,
96
+ alwaysOutputData: true
78
97
  },
79
98
  userMustProvide: [
80
99
  {
@@ -92,11 +111,17 @@ TaskTemplates.templates = {
92
111
  {
93
112
  property: 'authentication',
94
113
  description: 'Add authentication if required'
114
+ },
115
+ {
116
+ property: 'onError',
117
+ description: 'Set to "continueRegularOutput" for non-critical operations',
118
+ when: 'Failure should not stop the workflow'
95
119
  }
96
120
  ],
97
121
  notes: [
98
122
  'Make sure jsonBody contains valid JSON',
99
- 'Content-Type header is automatically set to application/json'
123
+ 'Content-Type header is automatically set to application/json',
124
+ 'Be careful with retries on non-idempotent operations'
100
125
  ]
101
126
  },
102
127
  'call_api_with_auth': {
@@ -109,6 +134,11 @@ TaskTemplates.templates = {
109
134
  authentication: 'genericCredentialType',
110
135
  genericAuthType: 'headerAuth',
111
136
  sendHeaders: true,
137
+ onError: 'continueErrorOutput',
138
+ retryOnFail: true,
139
+ maxTries: 3,
140
+ waitBetweenTries: 2000,
141
+ alwaysOutputData: true,
112
142
  headerParameters: {
113
143
  parameters: [
114
144
  {
@@ -149,7 +179,9 @@ TaskTemplates.templates = {
149
179
  httpMethod: 'POST',
150
180
  path: 'webhook',
151
181
  responseMode: 'lastNode',
152
- responseData: 'allEntries'
182
+ responseData: 'allEntries',
183
+ onError: 'continueRegularOutput',
184
+ alwaysOutputData: true
153
185
  },
154
186
  userMustProvide: [
155
187
  {
@@ -182,7 +214,9 @@ TaskTemplates.templates = {
182
214
  path: 'webhook',
183
215
  responseMode: 'responseNode',
184
216
  responseData: 'firstEntryJson',
185
- responseCode: 200
217
+ responseCode: 200,
218
+ onError: 'continueRegularOutput',
219
+ alwaysOutputData: true
186
220
  },
187
221
  userMustProvide: [
188
222
  {
@@ -195,13 +229,104 @@ TaskTemplates.templates = {
195
229
  'responseMode: responseNode requires a Respond to Webhook node'
196
230
  ]
197
231
  },
232
+ 'process_webhook_data': {
233
+ task: 'process_webhook_data',
234
+ description: 'Process incoming webhook data with Code node (shows correct data access)',
235
+ nodeType: 'nodes-base.code',
236
+ configuration: {
237
+ language: 'javaScript',
238
+ jsCode: `// ⚠️ CRITICAL: Webhook data is nested under 'body' property!
239
+ // Connect this Code node after a Webhook node
240
+
241
+ // Access webhook payload data - it's under .body, not directly under .json
242
+ const webhookData = items[0].json.body; // ✅ CORRECT
243
+ const headers = items[0].json.headers; // HTTP headers
244
+ const query = items[0].json.query; // Query parameters
245
+
246
+ // Common mistake to avoid:
247
+ // const command = items[0].json.testCommand; // ❌ WRONG - will be undefined!
248
+ // const command = items[0].json.body.testCommand; // ✅ CORRECT
249
+
250
+ // Process the webhook data
251
+ try {
252
+ // Validate required fields
253
+ if (!webhookData.command) {
254
+ throw new Error('Missing required field: command');
255
+ }
256
+
257
+ // Process based on command
258
+ let result = {};
259
+ switch (webhookData.command) {
260
+ case 'process':
261
+ result = {
262
+ status: 'processed',
263
+ data: webhookData.data,
264
+ processedAt: DateTime.now().toISO()
265
+ };
266
+ break;
267
+
268
+ case 'validate':
269
+ result = {
270
+ status: 'validated',
271
+ isValid: true,
272
+ validatedFields: Object.keys(webhookData.data || {})
273
+ };
274
+ break;
275
+
276
+ default:
277
+ result = {
278
+ status: 'unknown_command',
279
+ command: webhookData.command
280
+ };
281
+ }
282
+
283
+ // Return processed data
284
+ return [{
285
+ json: {
286
+ ...result,
287
+ requestId: headers['x-request-id'] || crypto.randomUUID(),
288
+ source: query.source || 'webhook',
289
+ originalCommand: webhookData.command,
290
+ metadata: {
291
+ httpMethod: items[0].json.httpMethod,
292
+ webhookPath: items[0].json.webhookPath,
293
+ timestamp: DateTime.now().toISO()
294
+ }
295
+ }
296
+ }];
297
+
298
+ } catch (error) {
299
+ // Return error response
300
+ return [{
301
+ json: {
302
+ status: 'error',
303
+ error: error.message,
304
+ timestamp: DateTime.now().toISO()
305
+ }
306
+ }];
307
+ }`,
308
+ onError: 'continueRegularOutput'
309
+ },
310
+ userMustProvide: [],
311
+ notes: [
312
+ '⚠️ WEBHOOK DATA IS AT items[0].json.body, NOT items[0].json',
313
+ 'This is the most common webhook processing mistake',
314
+ 'Headers are at items[0].json.headers',
315
+ 'Query parameters are at items[0].json.query',
316
+ 'Connect this Code node directly after a Webhook node'
317
+ ]
318
+ },
198
319
  'query_postgres': {
199
320
  task: 'query_postgres',
200
321
  description: 'Query data from PostgreSQL database',
201
322
  nodeType: 'nodes-base.postgres',
202
323
  configuration: {
203
324
  operation: 'executeQuery',
204
- query: ''
325
+ query: '',
326
+ onError: 'continueRegularOutput',
327
+ retryOnFail: true,
328
+ maxTries: 3,
329
+ waitBetweenTries: 1000
205
330
  },
206
331
  userMustProvide: [
207
332
  {
@@ -230,7 +355,11 @@ TaskTemplates.templates = {
230
355
  operation: 'insert',
231
356
  table: '',
232
357
  columns: '',
233
- returnFields: '*'
358
+ returnFields: '*',
359
+ onError: 'stopWorkflow',
360
+ retryOnFail: true,
361
+ maxTries: 2,
362
+ waitBetweenTries: 1000
234
363
  },
235
364
  userMustProvide: [
236
365
  {
@@ -264,7 +393,12 @@ TaskTemplates.templates = {
264
393
  content: ''
265
394
  }
266
395
  ]
267
- }
396
+ },
397
+ onError: 'continueRegularOutput',
398
+ retryOnFail: true,
399
+ maxTries: 3,
400
+ waitBetweenTries: 5000,
401
+ alwaysOutputData: true
268
402
  },
269
403
  userMustProvide: [
270
404
  {
@@ -386,7 +520,11 @@ return results;`
386
520
  resource: 'message',
387
521
  operation: 'post',
388
522
  channel: '',
389
- text: ''
523
+ text: '',
524
+ onError: 'continueRegularOutput',
525
+ retryOnFail: true,
526
+ maxTries: 2,
527
+ waitBetweenTries: 2000
390
528
  },
391
529
  userMustProvide: [
392
530
  {
@@ -419,7 +557,12 @@ return results;`
419
557
  fromEmail: '',
420
558
  toEmail: '',
421
559
  subject: '',
422
- text: ''
560
+ text: '',
561
+ onError: 'continueRegularOutput',
562
+ retryOnFail: true,
563
+ maxTries: 3,
564
+ waitBetweenTries: 3000,
565
+ alwaysOutputData: true
423
566
  },
424
567
  userMustProvide: [
425
568
  {
@@ -550,6 +693,705 @@ return results;`
550
693
  'Test each tool individually before combining',
551
694
  'Set N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true for community nodes'
552
695
  ]
696
+ },
697
+ 'api_call_with_retry': {
698
+ task: 'api_call_with_retry',
699
+ description: 'Resilient API call with automatic retry on failure',
700
+ nodeType: 'nodes-base.httpRequest',
701
+ configuration: {
702
+ method: 'GET',
703
+ url: '',
704
+ retryOnFail: true,
705
+ maxTries: 5,
706
+ waitBetweenTries: 2000,
707
+ alwaysOutputData: true,
708
+ sendHeaders: true,
709
+ headerParameters: {
710
+ parameters: [
711
+ {
712
+ name: 'X-Request-ID',
713
+ value: '={{ $workflow.id }}-{{ $itemIndex }}'
714
+ }
715
+ ]
716
+ }
717
+ },
718
+ userMustProvide: [
719
+ {
720
+ property: 'url',
721
+ description: 'The API endpoint to call',
722
+ example: 'https://api.example.com/resource/{{ $json.id }}'
723
+ }
724
+ ],
725
+ optionalEnhancements: [
726
+ {
727
+ property: 'authentication',
728
+ description: 'Add API authentication'
729
+ },
730
+ {
731
+ property: 'onError',
732
+ description: 'Change to "stopWorkflow" for critical API calls',
733
+ when: 'This is a critical API call that must succeed'
734
+ }
735
+ ],
736
+ notes: [
737
+ 'Retries help with rate limits and transient network issues',
738
+ 'waitBetweenTries prevents hammering the API',
739
+ 'alwaysOutputData captures error responses for debugging',
740
+ 'Consider exponential backoff for production use'
741
+ ]
742
+ },
743
+ 'fault_tolerant_processing': {
744
+ task: 'fault_tolerant_processing',
745
+ description: 'Data processing that continues despite individual item failures',
746
+ nodeType: 'nodes-base.code',
747
+ configuration: {
748
+ language: 'javaScript',
749
+ jsCode: `// Process items with error handling
750
+ const results = [];
751
+
752
+ for (const item of items) {
753
+ try {
754
+ // Your processing logic here
755
+ const processed = {
756
+ ...item.json,
757
+ processed: true,
758
+ timestamp: new Date().toISOString()
759
+ };
760
+
761
+ results.push({ json: processed });
762
+ } catch (error) {
763
+ // Log error but continue processing
764
+ console.error('Processing failed for item:', item.json.id, error);
765
+
766
+ // Add error item to results
767
+ results.push({
768
+ json: {
769
+ ...item.json,
770
+ error: error.message,
771
+ processed: false
772
+ }
773
+ });
774
+ }
775
+ }
776
+
777
+ return results;`,
778
+ onError: 'continueRegularOutput',
779
+ alwaysOutputData: true
780
+ },
781
+ userMustProvide: [
782
+ {
783
+ property: 'Processing logic',
784
+ description: 'Replace the comment with your data transformation logic'
785
+ }
786
+ ],
787
+ optionalEnhancements: [
788
+ {
789
+ property: 'Error notification',
790
+ description: 'Add IF node after to handle error items separately'
791
+ }
792
+ ],
793
+ notes: [
794
+ 'Individual item failures won\'t stop processing of other items',
795
+ 'Error items are marked and can be handled separately',
796
+ 'continueOnFail ensures workflow continues even on total failure'
797
+ ]
798
+ },
799
+ 'webhook_with_error_handling': {
800
+ task: 'webhook_with_error_handling',
801
+ description: 'Webhook that gracefully handles processing errors',
802
+ nodeType: 'nodes-base.webhook',
803
+ configuration: {
804
+ httpMethod: 'POST',
805
+ path: 'resilient-webhook',
806
+ responseMode: 'responseNode',
807
+ responseData: 'firstEntryJson',
808
+ onError: 'continueRegularOutput',
809
+ alwaysOutputData: true
810
+ },
811
+ userMustProvide: [
812
+ {
813
+ property: 'path',
814
+ description: 'Unique webhook path',
815
+ example: 'order-processor'
816
+ },
817
+ {
818
+ property: 'Respond to Webhook node',
819
+ description: 'Add node to send appropriate success/error responses'
820
+ }
821
+ ],
822
+ optionalEnhancements: [
823
+ {
824
+ property: 'Validation',
825
+ description: 'Add IF node to validate webhook payload'
826
+ },
827
+ {
828
+ property: 'Error logging',
829
+ description: 'Add error handler node for failed requests'
830
+ }
831
+ ],
832
+ notes: [
833
+ 'onError: continueRegularOutput ensures webhook always sends a response',
834
+ 'Use Respond to Webhook node to send appropriate status codes',
835
+ 'Log errors but don\'t expose internal errors to webhook callers',
836
+ 'Consider rate limiting for public webhooks'
837
+ ]
838
+ },
839
+ 'modern_error_handling_patterns': {
840
+ task: 'modern_error_handling_patterns',
841
+ description: 'Examples of modern error handling using onError property',
842
+ nodeType: 'nodes-base.httpRequest',
843
+ configuration: {
844
+ method: 'GET',
845
+ url: '',
846
+ onError: 'continueRegularOutput',
847
+ retryOnFail: true,
848
+ maxTries: 3,
849
+ waitBetweenTries: 2000,
850
+ alwaysOutputData: true
851
+ },
852
+ userMustProvide: [
853
+ {
854
+ property: 'url',
855
+ description: 'The API endpoint'
856
+ },
857
+ {
858
+ property: 'onError',
859
+ description: 'Choose error handling strategy',
860
+ example: 'continueRegularOutput'
861
+ }
862
+ ],
863
+ notes: [
864
+ 'onError replaces the deprecated continueOnFail property',
865
+ 'continueRegularOutput: Continue with normal output on error',
866
+ 'continueErrorOutput: Route errors to error output for special handling',
867
+ 'stopWorkflow: Stop the entire workflow on error',
868
+ 'Combine with retryOnFail for resilient workflows'
869
+ ]
870
+ },
871
+ 'database_transaction_safety': {
872
+ task: 'database_transaction_safety',
873
+ description: 'Database operations with proper error handling',
874
+ nodeType: 'nodes-base.postgres',
875
+ configuration: {
876
+ operation: 'executeQuery',
877
+ query: 'BEGIN; INSERT INTO orders ...; COMMIT;',
878
+ onError: 'continueErrorOutput',
879
+ retryOnFail: false,
880
+ alwaysOutputData: true
881
+ },
882
+ userMustProvide: [
883
+ {
884
+ property: 'query',
885
+ description: 'Your SQL query or transaction'
886
+ }
887
+ ],
888
+ notes: [
889
+ 'Transactions should not be retried automatically',
890
+ 'Use continueErrorOutput to handle errors separately',
891
+ 'Consider implementing compensating transactions',
892
+ 'Always log transaction failures for audit'
893
+ ]
894
+ },
895
+ 'ai_rate_limit_handling': {
896
+ task: 'ai_rate_limit_handling',
897
+ description: 'AI API calls with rate limit handling',
898
+ nodeType: 'nodes-base.openAi',
899
+ configuration: {
900
+ resource: 'chat',
901
+ operation: 'message',
902
+ modelId: 'gpt-4',
903
+ messages: {
904
+ values: [
905
+ {
906
+ role: 'user',
907
+ content: ''
908
+ }
909
+ ]
910
+ },
911
+ onError: 'continueRegularOutput',
912
+ retryOnFail: true,
913
+ maxTries: 5,
914
+ waitBetweenTries: 5000,
915
+ alwaysOutputData: true
916
+ },
917
+ userMustProvide: [
918
+ {
919
+ property: 'messages.values[0].content',
920
+ description: 'The prompt for the AI'
921
+ }
922
+ ],
923
+ notes: [
924
+ 'AI APIs often have rate limits',
925
+ 'Longer wait times help avoid hitting limits',
926
+ 'Consider implementing exponential backoff in Code node',
927
+ 'Monitor usage to stay within quotas'
928
+ ]
929
+ },
930
+ 'custom_ai_tool': {
931
+ task: 'custom_ai_tool',
932
+ description: 'Create a custom tool for AI agents using Code node',
933
+ nodeType: 'nodes-base.code',
934
+ configuration: {
935
+ language: 'javaScript',
936
+ mode: 'runOnceForEachItem',
937
+ jsCode: `// Custom AI Tool - Example: Text Analysis
938
+ // This code will be called by AI agents with $json containing the input
939
+
940
+ // Access the input from the AI agent
941
+ const text = $json.text || '';
942
+ const operation = $json.operation || 'analyze';
943
+
944
+ // Perform the requested operation
945
+ let result = {};
946
+
947
+ switch (operation) {
948
+ case 'wordCount':
949
+ result = {
950
+ wordCount: text.split(/\\s+/).filter(word => word.length > 0).length,
951
+ characterCount: text.length,
952
+ lineCount: text.split('\\n').length
953
+ };
954
+ break;
955
+
956
+ case 'extract':
957
+ // Extract specific patterns (emails, URLs, etc.)
958
+ result = {
959
+ emails: text.match(/[\\w.-]+@[\\w.-]+\\.\\w+/g) || [],
960
+ urls: text.match(/https?:\\/\\/[^\\s]+/g) || [],
961
+ numbers: text.match(/\\b\\d+\\b/g) || []
962
+ };
963
+ break;
964
+
965
+ default:
966
+ result = {
967
+ error: 'Unknown operation',
968
+ availableOperations: ['wordCount', 'extract']
969
+ };
970
+ }
971
+
972
+ return [{
973
+ json: {
974
+ ...result,
975
+ originalText: text,
976
+ operation: operation,
977
+ processedAt: DateTime.now().toISO()
978
+ }
979
+ }];`,
980
+ onError: 'continueRegularOutput'
981
+ },
982
+ userMustProvide: [],
983
+ notes: [
984
+ 'Connect this to AI Agent node\'s tool input',
985
+ 'AI will pass data in $json',
986
+ 'Use "Run Once for Each Item" mode for AI tools',
987
+ 'Return structured data the AI can understand'
988
+ ]
989
+ },
990
+ 'aggregate_data': {
991
+ task: 'aggregate_data',
992
+ description: 'Aggregate data from multiple items into summary statistics',
993
+ nodeType: 'nodes-base.code',
994
+ configuration: {
995
+ language: 'javaScript',
996
+ jsCode: `// Aggregate data from all items
997
+ const stats = {
998
+ count: 0,
999
+ sum: 0,
1000
+ min: Infinity,
1001
+ max: -Infinity,
1002
+ values: [],
1003
+ categories: {},
1004
+ errors: []
1005
+ };
1006
+
1007
+ // Process each item
1008
+ for (const item of items) {
1009
+ try {
1010
+ const value = item.json.value || item.json.amount || 0;
1011
+ const category = item.json.category || 'uncategorized';
1012
+
1013
+ stats.count++;
1014
+ stats.sum += value;
1015
+ stats.min = Math.min(stats.min, value);
1016
+ stats.max = Math.max(stats.max, value);
1017
+ stats.values.push(value);
1018
+
1019
+ // Count by category
1020
+ stats.categories[category] = (stats.categories[category] || 0) + 1;
1021
+
1022
+ } catch (error) {
1023
+ stats.errors.push({
1024
+ item: item.json,
1025
+ error: error.message
1026
+ });
1027
+ }
1028
+ }
1029
+
1030
+ // Calculate additional statistics
1031
+ const average = stats.count > 0 ? stats.sum / stats.count : 0;
1032
+ const sorted = [...stats.values].sort((a, b) => a - b);
1033
+ const median = sorted.length > 0
1034
+ ? sorted[Math.floor(sorted.length / 2)]
1035
+ : 0;
1036
+
1037
+ return [{
1038
+ json: {
1039
+ totalItems: stats.count,
1040
+ sum: stats.sum,
1041
+ average: average,
1042
+ median: median,
1043
+ min: stats.min === Infinity ? 0 : stats.min,
1044
+ max: stats.max === -Infinity ? 0 : stats.max,
1045
+ categoryCounts: stats.categories,
1046
+ errorCount: stats.errors.length,
1047
+ errors: stats.errors,
1048
+ processedAt: DateTime.now().toISO()
1049
+ }
1050
+ }];`,
1051
+ onError: 'continueRegularOutput'
1052
+ },
1053
+ userMustProvide: [],
1054
+ notes: [
1055
+ 'Assumes items have "value" or "amount" field',
1056
+ 'Groups by "category" field if present',
1057
+ 'Returns single item with all statistics',
1058
+ 'Handles errors gracefully'
1059
+ ]
1060
+ },
1061
+ 'batch_process_with_api': {
1062
+ task: 'batch_process_with_api',
1063
+ description: 'Process items in batches with API calls',
1064
+ nodeType: 'nodes-base.code',
1065
+ configuration: {
1066
+ language: 'javaScript',
1067
+ jsCode: `// Batch process items with API calls
1068
+ const BATCH_SIZE = 10;
1069
+ const API_URL = 'https://api.example.com/batch-process'; // USER MUST UPDATE
1070
+ const results = [];
1071
+
1072
+ // Process items in batches
1073
+ for (let i = 0; i < items.length; i += BATCH_SIZE) {
1074
+ const batch = items.slice(i, i + BATCH_SIZE);
1075
+
1076
+ try {
1077
+ // Prepare batch data
1078
+ const batchData = batch.map(item => ({
1079
+ id: item.json.id,
1080
+ data: item.json
1081
+ }));
1082
+
1083
+ // Make API request for batch
1084
+ const response = await $helpers.httpRequest({
1085
+ method: 'POST',
1086
+ url: API_URL,
1087
+ body: {
1088
+ items: batchData
1089
+ },
1090
+ headers: {
1091
+ 'Content-Type': 'application/json'
1092
+ }
1093
+ });
1094
+
1095
+ // Add results
1096
+ if (response.results && Array.isArray(response.results)) {
1097
+ response.results.forEach((result, index) => {
1098
+ results.push({
1099
+ json: {
1100
+ ...batch[index].json,
1101
+ ...result,
1102
+ batchNumber: Math.floor(i / BATCH_SIZE) + 1,
1103
+ processedAt: DateTime.now().toISO()
1104
+ }
1105
+ });
1106
+ });
1107
+ }
1108
+
1109
+ // Add delay between batches to avoid rate limits
1110
+ if (i + BATCH_SIZE < items.length) {
1111
+ await new Promise(resolve => setTimeout(resolve, 1000));
1112
+ }
1113
+
1114
+ } catch (error) {
1115
+ // Add failed batch items with error
1116
+ batch.forEach(item => {
1117
+ results.push({
1118
+ json: {
1119
+ ...item.json,
1120
+ error: error.message,
1121
+ status: 'failed',
1122
+ batchNumber: Math.floor(i / BATCH_SIZE) + 1
1123
+ }
1124
+ });
1125
+ });
1126
+ }
1127
+ }
1128
+
1129
+ return results;`,
1130
+ onError: 'continueRegularOutput',
1131
+ retryOnFail: true,
1132
+ maxTries: 2
1133
+ },
1134
+ userMustProvide: [
1135
+ {
1136
+ property: 'jsCode',
1137
+ description: 'Update API_URL in the code',
1138
+ example: 'https://your-api.com/batch'
1139
+ }
1140
+ ],
1141
+ notes: [
1142
+ 'Processes items in batches of 10',
1143
+ 'Includes delay between batches',
1144
+ 'Handles batch failures gracefully',
1145
+ 'Update API_URL and adjust BATCH_SIZE as needed'
1146
+ ]
1147
+ },
1148
+ 'error_safe_transform': {
1149
+ task: 'error_safe_transform',
1150
+ description: 'Transform data with comprehensive error handling',
1151
+ nodeType: 'nodes-base.code',
1152
+ configuration: {
1153
+ language: 'javaScript',
1154
+ jsCode: `// Safe data transformation with validation
1155
+ const results = [];
1156
+ const errors = [];
1157
+
1158
+ for (const item of items) {
1159
+ try {
1160
+ // Validate required fields
1161
+ const required = ['id', 'name']; // USER SHOULD UPDATE
1162
+ const missing = required.filter(field => !item.json[field]);
1163
+
1164
+ if (missing.length > 0) {
1165
+ throw new Error(\`Missing required fields: \${missing.join(', ')}\`);
1166
+ }
1167
+
1168
+ // Transform data with type checking
1169
+ const transformed = {
1170
+ // Ensure ID is string
1171
+ id: String(item.json.id),
1172
+
1173
+ // Clean and validate name
1174
+ name: String(item.json.name).trim(),
1175
+
1176
+ // Parse numbers safely
1177
+ amount: parseFloat(item.json.amount) || 0,
1178
+
1179
+ // Parse dates safely
1180
+ date: item.json.date
1181
+ ? DateTime.fromISO(item.json.date).isValid
1182
+ ? DateTime.fromISO(item.json.date).toISO()
1183
+ : null
1184
+ : null,
1185
+
1186
+ // Boolean conversion
1187
+ isActive: Boolean(item.json.active || item.json.isActive),
1188
+
1189
+ // Array handling
1190
+ tags: Array.isArray(item.json.tags)
1191
+ ? item.json.tags.filter(tag => typeof tag === 'string')
1192
+ : [],
1193
+
1194
+ // Nested object handling
1195
+ metadata: typeof item.json.metadata === 'object'
1196
+ ? item.json.metadata
1197
+ : {},
1198
+
1199
+ // Add processing info
1200
+ processedAt: DateTime.now().toISO(),
1201
+ originalIndex: items.indexOf(item)
1202
+ };
1203
+
1204
+ results.push({
1205
+ json: transformed
1206
+ });
1207
+
1208
+ } catch (error) {
1209
+ errors.push({
1210
+ json: {
1211
+ error: error.message,
1212
+ originalData: item.json,
1213
+ index: items.indexOf(item),
1214
+ status: 'failed'
1215
+ }
1216
+ });
1217
+ }
1218
+ }
1219
+
1220
+ // Add summary at the end
1221
+ results.push({
1222
+ json: {
1223
+ _summary: {
1224
+ totalProcessed: results.length - errors.length,
1225
+ totalErrors: errors.length,
1226
+ successRate: ((results.length - errors.length) / items.length * 100).toFixed(2) + '%',
1227
+ timestamp: DateTime.now().toISO()
1228
+ }
1229
+ }
1230
+ });
1231
+
1232
+ // Include errors at the end
1233
+ return [...results, ...errors];`,
1234
+ onError: 'continueRegularOutput'
1235
+ },
1236
+ userMustProvide: [
1237
+ {
1238
+ property: 'jsCode',
1239
+ description: 'Update required fields array',
1240
+ example: "const required = ['id', 'email', 'name'];"
1241
+ }
1242
+ ],
1243
+ notes: [
1244
+ 'Validates all data types',
1245
+ 'Handles missing/invalid data gracefully',
1246
+ 'Returns both successful and failed items',
1247
+ 'Includes processing summary'
1248
+ ]
1249
+ },
1250
+ 'async_data_processing': {
1251
+ task: 'async_data_processing',
1252
+ description: 'Process data with async operations and proper error handling',
1253
+ nodeType: 'nodes-base.code',
1254
+ configuration: {
1255
+ language: 'javaScript',
1256
+ jsCode: `// Async processing with concurrent limits
1257
+ const CONCURRENT_LIMIT = 5;
1258
+ const results = [];
1259
+
1260
+ // Process items with concurrency control
1261
+ async function processItem(item, index) {
1262
+ try {
1263
+ // Simulate async operation (replace with actual logic)
1264
+ // Example: API call, database query, file operation
1265
+ await new Promise(resolve => setTimeout(resolve, 100));
1266
+
1267
+ // Actual processing logic here
1268
+ const processed = {
1269
+ ...item.json,
1270
+ processed: true,
1271
+ index: index,
1272
+ timestamp: DateTime.now().toISO()
1273
+ };
1274
+
1275
+ // Example async operation - external API call
1276
+ if (item.json.needsEnrichment) {
1277
+ const enrichment = await $helpers.httpRequest({
1278
+ method: 'GET',
1279
+ url: \`https://api.example.com/enrich/\${item.json.id}\`
1280
+ });
1281
+ processed.enrichment = enrichment;
1282
+ }
1283
+
1284
+ return { json: processed };
1285
+
1286
+ } catch (error) {
1287
+ return {
1288
+ json: {
1289
+ ...item.json,
1290
+ error: error.message,
1291
+ status: 'failed',
1292
+ index: index
1293
+ }
1294
+ };
1295
+ }
1296
+ }
1297
+
1298
+ // Process in batches with concurrency limit
1299
+ for (let i = 0; i < items.length; i += CONCURRENT_LIMIT) {
1300
+ const batch = items.slice(i, i + CONCURRENT_LIMIT);
1301
+ const batchPromises = batch.map((item, batchIndex) =>
1302
+ processItem(item, i + batchIndex)
1303
+ );
1304
+
1305
+ const batchResults = await Promise.all(batchPromises);
1306
+ results.push(...batchResults);
1307
+ }
1308
+
1309
+ return results;`,
1310
+ onError: 'continueRegularOutput',
1311
+ retryOnFail: true,
1312
+ maxTries: 2
1313
+ },
1314
+ userMustProvide: [],
1315
+ notes: [
1316
+ 'Processes 5 items concurrently',
1317
+ 'Prevents overwhelming external services',
1318
+ 'Each item processed independently',
1319
+ 'Errors don\'t affect other items'
1320
+ ]
1321
+ },
1322
+ 'python_data_analysis': {
1323
+ task: 'python_data_analysis',
1324
+ description: 'Analyze data using Python with statistics',
1325
+ nodeType: 'nodes-base.code',
1326
+ configuration: {
1327
+ language: 'python',
1328
+ pythonCode: `# Python data analysis - use underscore prefix for built-in variables
1329
+ import json
1330
+ from datetime import datetime
1331
+ import statistics
1332
+
1333
+ # Collect data for analysis
1334
+ values = []
1335
+ categories = {}
1336
+ dates = []
1337
+
1338
+ # Use _input.all() to get items in Python
1339
+ for item in _input.all():
1340
+ # Convert JsProxy to Python dict for safe access
1341
+ item_data = item.json.to_py()
1342
+
1343
+ # Extract numeric values
1344
+ if 'value' in item_data or 'amount' in item_data:
1345
+ value = item_data.get('value', item_data.get('amount', 0))
1346
+ if isinstance(value, (int, float)):
1347
+ values.append(value)
1348
+
1349
+ # Count categories
1350
+ category = item_data.get('category', 'uncategorized')
1351
+ categories[category] = categories.get(category, 0) + 1
1352
+
1353
+ # Collect dates
1354
+ if 'date' in item_data:
1355
+ dates.append(item_data['date'])
1356
+
1357
+ # Calculate statistics
1358
+ result = {
1359
+ 'itemCount': len(_input.all()),
1360
+ 'values': {
1361
+ 'count': len(values),
1362
+ 'sum': sum(values) if values else 0,
1363
+ 'mean': statistics.mean(values) if values else 0,
1364
+ 'median': statistics.median(values) if values else 0,
1365
+ 'min': min(values) if values else 0,
1366
+ 'max': max(values) if values else 0,
1367
+ 'stdev': statistics.stdev(values) if len(values) > 1 else 0
1368
+ },
1369
+ 'categories': categories,
1370
+ 'dateRange': {
1371
+ 'earliest': min(dates) if dates else None,
1372
+ 'latest': max(dates) if dates else None,
1373
+ 'count': len(dates)
1374
+ },
1375
+ 'analysis': {
1376
+ 'hasNumericData': len(values) > 0,
1377
+ 'hasCategoricalData': len(categories) > 0,
1378
+ 'hasTemporalData': len(dates) > 0,
1379
+ 'dataQuality': 'good' if len(values) > len(items) * 0.8 else 'partial'
1380
+ },
1381
+ 'processedAt': datetime.now().isoformat()
1382
+ }
1383
+
1384
+ # Return single summary item
1385
+ return [{'json': result}]`,
1386
+ onError: 'continueRegularOutput'
1387
+ },
1388
+ userMustProvide: [],
1389
+ notes: [
1390
+ 'Uses Python statistics module',
1391
+ 'Analyzes numeric, categorical, and date data',
1392
+ 'Returns comprehensive summary',
1393
+ 'Handles missing data gracefully'
1394
+ ]
553
1395
  }
554
1396
  };
555
1397
  //# sourceMappingURL=task-templates.js.map