n8n-mcp 2.11.2 → 2.12.2

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 (68) hide show
  1. package/.env.example +26 -0
  2. package/README.md +3 -3
  3. package/data/nodes.db +0 -0
  4. package/dist/config/n8n-api.d.ts +6 -0
  5. package/dist/config/n8n-api.d.ts.map +1 -1
  6. package/dist/config/n8n-api.js +12 -0
  7. package/dist/config/n8n-api.js.map +1 -1
  8. package/dist/http-server-single-session.d.ts +6 -1
  9. package/dist/http-server-single-session.d.ts.map +1 -1
  10. package/dist/http-server-single-session.js +116 -4
  11. package/dist/http-server-single-session.js.map +1 -1
  12. package/dist/mcp/handlers-n8n-manager.d.ts +21 -17
  13. package/dist/mcp/handlers-n8n-manager.d.ts.map +1 -1
  14. package/dist/mcp/handlers-n8n-manager.js +111 -45
  15. package/dist/mcp/handlers-n8n-manager.js.map +1 -1
  16. package/dist/mcp/handlers-workflow-diff.d.ts +2 -1
  17. package/dist/mcp/handlers-workflow-diff.d.ts.map +1 -1
  18. package/dist/mcp/handlers-workflow-diff.js +3 -3
  19. package/dist/mcp/handlers-workflow-diff.js.map +1 -1
  20. package/dist/mcp/server.d.ts +3 -1
  21. package/dist/mcp/server.d.ts.map +1 -1
  22. package/dist/mcp/server.js +34 -22
  23. package/dist/mcp/server.js.map +1 -1
  24. package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js +2 -2
  25. package/dist/mcp/tool-docs/workflow_management/n8n-update-partial-workflow.js.map +1 -1
  26. package/dist/mcp-engine.d.ts +2 -1
  27. package/dist/mcp-engine.d.ts.map +1 -1
  28. package/dist/mcp-engine.js +2 -2
  29. package/dist/mcp-engine.js.map +1 -1
  30. package/dist/scripts/sanitize-templates.js +11 -1
  31. package/dist/scripts/sanitize-templates.js.map +1 -1
  32. package/dist/services/confidence-scorer.d.ts +24 -0
  33. package/dist/services/confidence-scorer.d.ts.map +1 -0
  34. package/dist/services/confidence-scorer.js +139 -0
  35. package/dist/services/confidence-scorer.js.map +1 -0
  36. package/dist/services/expression-format-validator.d.ts +33 -0
  37. package/dist/services/expression-format-validator.d.ts.map +1 -0
  38. package/dist/services/expression-format-validator.js +209 -0
  39. package/dist/services/expression-format-validator.js.map +1 -0
  40. package/dist/services/universal-expression-validator.d.ts +20 -0
  41. package/dist/services/universal-expression-validator.d.ts.map +1 -0
  42. package/dist/services/universal-expression-validator.js +192 -0
  43. package/dist/services/universal-expression-validator.js.map +1 -0
  44. package/dist/services/workflow-diff-engine.js +7 -7
  45. package/dist/services/workflow-ownership.d.ts +35 -0
  46. package/dist/services/workflow-ownership.d.ts.map +1 -0
  47. package/dist/services/workflow-ownership.js +195 -0
  48. package/dist/services/workflow-ownership.js.map +1 -0
  49. package/dist/services/workflow-validator.d.ts +1 -0
  50. package/dist/services/workflow-validator.d.ts.map +1 -1
  51. package/dist/services/workflow-validator.js +94 -1
  52. package/dist/services/workflow-validator.js.map +1 -1
  53. package/dist/types/instance-context.d.ts +15 -0
  54. package/dist/types/instance-context.d.ts.map +1 -0
  55. package/dist/types/instance-context.js +127 -0
  56. package/dist/types/instance-context.js.map +1 -0
  57. package/dist/types/n8n-api.d.ts +6 -0
  58. package/dist/types/n8n-api.d.ts.map +1 -1
  59. package/dist/types/n8n-api.js.map +1 -1
  60. package/dist/types/workflow-diff.d.ts +2 -2
  61. package/dist/utils/cache-utils.d.ts +58 -0
  62. package/dist/utils/cache-utils.d.ts.map +1 -0
  63. package/dist/utils/cache-utils.js +243 -0
  64. package/dist/utils/cache-utils.js.map +1 -0
  65. package/dist/utils/template-sanitizer.d.ts.map +1 -1
  66. package/dist/utils/template-sanitizer.js +6 -3
  67. package/dist/utils/template-sanitizer.js.map +1 -1
  68. package/package.json +2 -1
@@ -336,7 +336,7 @@ class WorkflowDiffEngine {
336
336
  const node = this.findNode(workflow, operation.nodeId, operation.nodeName);
337
337
  if (!node)
338
338
  return;
339
- Object.entries(operation.changes).forEach(([path, value]) => {
339
+ Object.entries(operation.updates).forEach(([path, value]) => {
340
340
  this.setNestedProperty(node, path, value);
341
341
  });
342
342
  }
@@ -406,17 +406,17 @@ class WorkflowDiffEngine {
406
406
  type: 'removeConnection',
407
407
  source: operation.source,
408
408
  target: operation.target,
409
- sourceOutput: operation.changes.sourceOutput,
410
- targetInput: operation.changes.targetInput
409
+ sourceOutput: operation.updates.sourceOutput,
410
+ targetInput: operation.updates.targetInput
411
411
  });
412
412
  this.applyAddConnection(workflow, {
413
413
  type: 'addConnection',
414
414
  source: operation.source,
415
415
  target: operation.target,
416
- sourceOutput: operation.changes.sourceOutput,
417
- targetInput: operation.changes.targetInput,
418
- sourceIndex: operation.changes.sourceIndex,
419
- targetIndex: operation.changes.targetIndex
416
+ sourceOutput: operation.updates.sourceOutput,
417
+ targetInput: operation.updates.targetInput,
418
+ sourceIndex: operation.updates.sourceIndex,
419
+ targetIndex: operation.updates.targetIndex
420
420
  });
421
421
  }
422
422
  applyUpdateSettings(workflow, operation) {
@@ -0,0 +1,35 @@
1
+ import { InstanceContext } from '../types/instance-context';
2
+ import { N8nApiClient } from './n8n-api-client';
3
+ import { Workflow } from '../types/n8n-api';
4
+ export interface WorkflowOwnershipInfo {
5
+ workflowId: string;
6
+ instanceId: string;
7
+ instanceUrl: string;
8
+ createdAt: Date;
9
+ lastVerified: Date;
10
+ }
11
+ export declare class WorkflowOwnershipService {
12
+ private apiClient?;
13
+ private ownershipCache;
14
+ private readonly CACHE_TTL;
15
+ private readonly INSTANCE_TAG_PREFIX;
16
+ private readonly MAX_CACHE_SIZE;
17
+ constructor(apiClient?: N8nApiClient | undefined);
18
+ setApiClient(client: N8nApiClient): void;
19
+ private getInstanceIdentifier;
20
+ private getInstanceTag;
21
+ private extractInstanceFromTags;
22
+ validateAccess(workflowId: string, context: InstanceContext): Promise<boolean>;
23
+ trackOwnership(workflowId: string, context: InstanceContext): Promise<void>;
24
+ private claimWorkflow;
25
+ filterWorkflowsByInstance(workflows: Workflow[], context: InstanceContext): Promise<Workflow[]>;
26
+ private updateOwnershipCache;
27
+ private isCacheValid;
28
+ clearCache(): void;
29
+ getCacheStats(): {
30
+ size: number;
31
+ maxSize: number;
32
+ ttl: number;
33
+ };
34
+ }
35
+ //# sourceMappingURL=workflow-ownership.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-ownership.d.ts","sourceRoot":"","sources":["../../src/services/workflow-ownership.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,IAAI,CAAC;CACpB;AAKD,qBAAa,wBAAwB;IAavB,OAAO,CAAC,SAAS,CAAC;IAX9B,OAAO,CAAC,cAAc,CAA4C;IAGlE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAG3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmB;IAGvD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAEpB,SAAS,CAAC,EAAE,YAAY,YAAA;IAK5C,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAOxC,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,uBAAuB;IAYzB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAiF9E,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;YA6CnE,aAAa;IAkCrB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2BrG,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,YAAY;IAQpB,UAAU,IAAI,IAAI;IAQlB,aAAa,IAAI;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb;CAOF"}
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkflowOwnershipService = void 0;
4
+ const logger_1 = require("../utils/logger");
5
+ const cache_utils_1 = require("../utils/cache-utils");
6
+ class WorkflowOwnershipService {
7
+ constructor(apiClient) {
8
+ this.apiClient = apiClient;
9
+ this.ownershipCache = new Map();
10
+ this.CACHE_TTL = 5 * 60 * 1000;
11
+ this.INSTANCE_TAG_PREFIX = 'mcp-instance:';
12
+ this.MAX_CACHE_SIZE = 10000;
13
+ }
14
+ setApiClient(client) {
15
+ this.apiClient = client;
16
+ }
17
+ getInstanceIdentifier(context) {
18
+ if (context.instanceId) {
19
+ return context.instanceId;
20
+ }
21
+ return (0, cache_utils_1.createCacheKey)(`${context.n8nApiUrl}:${context.n8nApiKey}`).substring(0, 16);
22
+ }
23
+ getInstanceTag(context) {
24
+ return `${this.INSTANCE_TAG_PREFIX}${this.getInstanceIdentifier(context)}`;
25
+ }
26
+ extractInstanceFromTags(tags) {
27
+ if (!tags || tags.length === 0)
28
+ return null;
29
+ const instanceTag = tags.find(tag => tag.startsWith(this.INSTANCE_TAG_PREFIX));
30
+ if (!instanceTag)
31
+ return null;
32
+ return instanceTag.replace(this.INSTANCE_TAG_PREFIX, '');
33
+ }
34
+ async validateAccess(workflowId, context) {
35
+ if (!context?.n8nApiUrl || !context?.n8nApiKey) {
36
+ logger_1.logger.debug('No instance context provided, allowing access for backward compatibility');
37
+ return true;
38
+ }
39
+ const instanceId = this.getInstanceIdentifier(context);
40
+ const cached = this.ownershipCache.get(workflowId);
41
+ if (cached && this.isCacheValid(cached)) {
42
+ const hasAccess = cached.instanceId === instanceId;
43
+ logger_1.logger.debug('Workflow ownership cache hit', {
44
+ workflowId,
45
+ instanceId,
46
+ cachedOwner: cached.instanceId,
47
+ hasAccess
48
+ });
49
+ return hasAccess;
50
+ }
51
+ logger_1.logger.debug('Workflow ownership cache miss, verifying ownership', {
52
+ workflowId,
53
+ instanceId
54
+ });
55
+ try {
56
+ if (!this.apiClient) {
57
+ logger_1.logger.warn('No API client available for ownership verification');
58
+ return true;
59
+ }
60
+ const workflow = await this.apiClient.getWorkflow(workflowId);
61
+ const workflowInstanceId = this.extractInstanceFromTags(workflow.tags);
62
+ if (workflowInstanceId === null) {
63
+ logger_1.logger.info('Workflow has no instance tag, treating as unclaimed', {
64
+ workflowId,
65
+ workflowName: workflow.name
66
+ });
67
+ await this.claimWorkflow(workflowId, workflow, context);
68
+ return true;
69
+ }
70
+ this.updateOwnershipCache(workflowId, workflowInstanceId, context.n8nApiUrl || '');
71
+ const hasAccess = workflowInstanceId === instanceId;
72
+ if (!hasAccess) {
73
+ logger_1.logger.warn('Access denied: workflow belongs to different instance', {
74
+ workflowId,
75
+ requestingInstance: instanceId,
76
+ owningInstance: workflowInstanceId
77
+ });
78
+ }
79
+ return hasAccess;
80
+ }
81
+ catch (error) {
82
+ logger_1.logger.error('Error validating workflow ownership', {
83
+ workflowId,
84
+ instanceId,
85
+ error: error instanceof Error ? error.message : String(error)
86
+ });
87
+ return false;
88
+ }
89
+ }
90
+ async trackOwnership(workflowId, context) {
91
+ if (!context?.n8nApiUrl || !context?.n8nApiKey) {
92
+ logger_1.logger.debug('No instance context for ownership tracking');
93
+ return;
94
+ }
95
+ const instanceId = this.getInstanceIdentifier(context);
96
+ const instanceTag = this.getInstanceTag(context);
97
+ try {
98
+ if (this.apiClient) {
99
+ const workflow = await this.apiClient.getWorkflow(workflowId);
100
+ const tags = workflow.tags || [];
101
+ if (!tags.some(tag => tag.startsWith(this.INSTANCE_TAG_PREFIX))) {
102
+ tags.push(instanceTag);
103
+ await this.apiClient.updateWorkflow(workflowId, { tags });
104
+ logger_1.logger.info('Added instance ownership tag to workflow', {
105
+ workflowId,
106
+ instanceId,
107
+ instanceTag
108
+ });
109
+ }
110
+ }
111
+ this.updateOwnershipCache(workflowId, instanceId, context.n8nApiUrl);
112
+ }
113
+ catch (error) {
114
+ logger_1.logger.error('Error tracking workflow ownership', {
115
+ workflowId,
116
+ instanceId,
117
+ error: error instanceof Error ? error.message : String(error)
118
+ });
119
+ }
120
+ }
121
+ async claimWorkflow(workflowId, workflow, context) {
122
+ if (!this.apiClient)
123
+ return;
124
+ const instanceTag = this.getInstanceTag(context);
125
+ const tags = workflow.tags || [];
126
+ tags.push(instanceTag);
127
+ try {
128
+ await this.apiClient.updateWorkflow(workflowId, { tags });
129
+ logger_1.logger.info('Claimed unclaimed workflow for instance', {
130
+ workflowId,
131
+ workflowName: workflow.name,
132
+ instanceId: this.getInstanceIdentifier(context)
133
+ });
134
+ this.updateOwnershipCache(workflowId, this.getInstanceIdentifier(context), context.n8nApiUrl || '');
135
+ }
136
+ catch (error) {
137
+ logger_1.logger.error('Error claiming workflow', {
138
+ workflowId,
139
+ error: error instanceof Error ? error.message : String(error)
140
+ });
141
+ }
142
+ }
143
+ async filterWorkflowsByInstance(workflows, context) {
144
+ if (!context?.n8nApiUrl || !context?.n8nApiKey) {
145
+ return workflows;
146
+ }
147
+ const instanceId = this.getInstanceIdentifier(context);
148
+ return workflows.filter(workflow => {
149
+ const workflowInstanceId = this.extractInstanceFromTags(workflow.tags);
150
+ if (workflowInstanceId === null) {
151
+ logger_1.logger.debug('Including unclaimed workflow in list', {
152
+ workflowId: workflow.id,
153
+ workflowName: workflow.name
154
+ });
155
+ return true;
156
+ }
157
+ return workflowInstanceId === instanceId;
158
+ });
159
+ }
160
+ updateOwnershipCache(workflowId, instanceId, instanceUrl) {
161
+ if (this.ownershipCache.size >= this.MAX_CACHE_SIZE) {
162
+ const entriesToRemove = Math.floor(this.MAX_CACHE_SIZE * 0.1);
163
+ const keys = Array.from(this.ownershipCache.keys()).slice(0, entriesToRemove);
164
+ keys.forEach(key => this.ownershipCache.delete(key));
165
+ logger_1.logger.debug('Evicted ownership cache entries', {
166
+ evicted: entriesToRemove,
167
+ remaining: this.ownershipCache.size
168
+ });
169
+ }
170
+ this.ownershipCache.set(workflowId, {
171
+ workflowId,
172
+ instanceId,
173
+ instanceUrl,
174
+ createdAt: new Date(),
175
+ lastVerified: new Date()
176
+ });
177
+ }
178
+ isCacheValid(entry) {
179
+ const age = Date.now() - entry.lastVerified.getTime();
180
+ return age < this.CACHE_TTL;
181
+ }
182
+ clearCache() {
183
+ this.ownershipCache.clear();
184
+ logger_1.logger.debug('Cleared workflow ownership cache');
185
+ }
186
+ getCacheStats() {
187
+ return {
188
+ size: this.ownershipCache.size,
189
+ maxSize: this.MAX_CACHE_SIZE,
190
+ ttl: this.CACHE_TTL
191
+ };
192
+ }
193
+ }
194
+ exports.WorkflowOwnershipService = WorkflowOwnershipService;
195
+ //# sourceMappingURL=workflow-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-ownership.js","sourceRoot":"","sources":["../../src/services/workflow-ownership.ts"],"names":[],"mappings":"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"}
@@ -84,6 +84,7 @@ export declare class WorkflowValidator {
84
84
  private validateAllNodes;
85
85
  private validateConnections;
86
86
  private validateConnectionOutputs;
87
+ private validateErrorOutputConfiguration;
87
88
  private validateAIToolConnection;
88
89
  private hasCycle;
89
90
  private validateExpressions;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-validator.d.ts","sourceRoot":"","sources":["../../src/services/workflow-validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-validator.d.ts","sourceRoot":"","sources":["../../src/services/workflow-validator.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WorkflowValidator = void 0;
4
4
  const expression_validator_1 = require("./expression-validator");
5
+ const expression_format_validator_1 = require("./expression-format-validator");
5
6
  const logger_1 = require("../utils/logger");
6
7
  const logger = new logger_1.Logger({ prefix: '[WorkflowValidator]' });
7
8
  class WorkflowValidator {
@@ -411,6 +412,9 @@ class WorkflowValidator {
411
412
  }
412
413
  validateConnectionOutputs(sourceName, outputs, nodeMap, nodeIdMap, result, outputType) {
413
414
  const sourceNode = nodeMap.get(sourceName);
415
+ if (outputType === 'main' && sourceNode) {
416
+ this.validateErrorOutputConfiguration(sourceName, sourceNode, outputs, nodeMap, result);
417
+ }
414
418
  outputs.forEach((outputConnections, outputIndex) => {
415
419
  if (!outputConnections)
416
420
  return;
@@ -468,6 +472,70 @@ class WorkflowValidator {
468
472
  });
469
473
  });
470
474
  }
475
+ validateErrorOutputConfiguration(sourceName, sourceNode, outputs, nodeMap, result) {
476
+ const hasErrorOutputSetting = sourceNode.onError === 'continueErrorOutput';
477
+ const hasErrorConnections = outputs.length > 1 && outputs[1] && outputs[1].length > 0;
478
+ if (hasErrorOutputSetting && !hasErrorConnections) {
479
+ result.errors.push({
480
+ type: 'error',
481
+ nodeId: sourceNode.id,
482
+ nodeName: sourceNode.name,
483
+ message: `Node has onError: 'continueErrorOutput' but no error output connections in main[1]. Add error handler connections to main[1] or change onError to 'continueRegularOutput' or 'stopWorkflow'.`
484
+ });
485
+ }
486
+ if (!hasErrorOutputSetting && hasErrorConnections) {
487
+ result.warnings.push({
488
+ type: 'warning',
489
+ nodeId: sourceNode.id,
490
+ nodeName: sourceNode.name,
491
+ message: `Node has error output connections in main[1] but missing onError: 'continueErrorOutput'. Add this property to properly handle errors.`
492
+ });
493
+ }
494
+ if (outputs.length >= 1 && outputs[0] && outputs[0].length > 1) {
495
+ const potentialErrorHandlers = outputs[0].filter(conn => {
496
+ const targetNode = nodeMap.get(conn.node);
497
+ if (!targetNode)
498
+ return false;
499
+ const nodeName = targetNode.name.toLowerCase();
500
+ const nodeType = targetNode.type.toLowerCase();
501
+ return nodeName.includes('error') ||
502
+ nodeName.includes('fail') ||
503
+ nodeName.includes('catch') ||
504
+ nodeName.includes('exception') ||
505
+ nodeType.includes('respondtowebhook') ||
506
+ nodeType.includes('emailsend');
507
+ });
508
+ if (potentialErrorHandlers.length > 0) {
509
+ const errorHandlerNames = potentialErrorHandlers.map(conn => `"${conn.node}"`).join(', ');
510
+ result.errors.push({
511
+ type: 'error',
512
+ nodeId: sourceNode.id,
513
+ nodeName: sourceNode.name,
514
+ message: `Incorrect error output configuration. Nodes ${errorHandlerNames} appear to be error handlers but are in main[0] (success output) along with other nodes.\n\n` +
515
+ `INCORRECT (current):\n` +
516
+ `"${sourceName}": {\n` +
517
+ ` "main": [\n` +
518
+ ` [ // main[0] has multiple nodes mixed together\n` +
519
+ outputs[0].map(conn => ` {"node": "${conn.node}", "type": "${conn.type}", "index": ${conn.index}}`).join(',\n') + '\n' +
520
+ ` ]\n` +
521
+ ` ]\n` +
522
+ `}\n\n` +
523
+ `CORRECT (should be):\n` +
524
+ `"${sourceName}": {\n` +
525
+ ` "main": [\n` +
526
+ ` [ // main[0] = success output\n` +
527
+ outputs[0].filter(conn => !potentialErrorHandlers.includes(conn)).map(conn => ` {"node": "${conn.node}", "type": "${conn.type}", "index": ${conn.index}}`).join(',\n') + '\n' +
528
+ ` ],\n` +
529
+ ` [ // main[1] = error output\n` +
530
+ potentialErrorHandlers.map(conn => ` {"node": "${conn.node}", "type": "${conn.type}", "index": ${conn.index}}`).join(',\n') + '\n' +
531
+ ` ]\n` +
532
+ ` ]\n` +
533
+ `}\n\n` +
534
+ `Also add: "onError": "continueErrorOutput" to the "${sourceName}" node.`
535
+ });
536
+ }
537
+ }
538
+ }
471
539
  validateAIToolConnection(sourceName, targetNode, result) {
472
540
  let targetNodeInfo = this.nodeRepository.getNode(targetNode.type);
473
541
  if (!targetNodeInfo) {
@@ -589,6 +657,31 @@ class WorkflowValidator {
589
657
  message: `Expression warning: ${warning}`
590
658
  });
591
659
  });
660
+ const formatContext = {
661
+ nodeType: node.type,
662
+ nodeName: node.name,
663
+ nodeId: node.id
664
+ };
665
+ const formatIssues = expression_format_validator_1.ExpressionFormatValidator.validateNodeParameters(node.parameters, formatContext);
666
+ formatIssues.forEach(issue => {
667
+ const formattedMessage = expression_format_validator_1.ExpressionFormatValidator.formatErrorMessage(issue, formatContext);
668
+ if (issue.severity === 'error') {
669
+ result.errors.push({
670
+ type: 'error',
671
+ nodeId: node.id,
672
+ nodeName: node.name,
673
+ message: formattedMessage
674
+ });
675
+ }
676
+ else {
677
+ result.warnings.push({
678
+ type: 'warning',
679
+ nodeId: node.id,
680
+ nodeName: node.name,
681
+ message: formattedMessage
682
+ });
683
+ }
684
+ });
592
685
  }
593
686
  }
594
687
  countExpressionsInObject(obj) {
@@ -624,7 +717,7 @@ class WorkflowValidator {
624
717
  return false;
625
718
  }
626
719
  checkWorkflowPatterns(workflow, result, profile = 'runtime') {
627
- const hasErrorHandling = Object.values(workflow.connections).some(outputs => outputs.error && outputs.error.length > 0);
720
+ const hasErrorHandling = Object.values(workflow.connections).some(outputs => outputs.main && outputs.main.length > 1 && outputs.main[1] && outputs.main[1].length > 0);
628
721
  if (!hasErrorHandling && workflow.nodes.length > 3 && profile !== 'minimal') {
629
722
  result.warnings.push({
630
723
  type: 'warning',