@pagelines/n8n-mcp 0.2.1 → 0.3.0
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/CHANGELOG.md +26 -0
- package/README.md +40 -24
- package/dist/index.js +122 -18
- package/dist/n8n-client.d.ts +3 -2
- package/dist/n8n-client.js +9 -1
- package/dist/n8n-client.test.js +111 -0
- package/dist/response-format.d.ts +84 -0
- package/dist/response-format.js +183 -0
- package/dist/response-format.test.d.ts +1 -0
- package/dist/response-format.test.js +291 -0
- package/dist/tools.js +67 -3
- package/dist/types.d.ts +27 -0
- package/dist/validators.d.ts +9 -1
- package/dist/validators.js +87 -2
- package/dist/validators.test.js +83 -4
- package/docs/best-practices.md +15 -10
- package/docs/node-config.md +3 -1
- package/logo.png +0 -0
- package/package.json +1 -1
- package/plans/architecture.md +69 -26
- package/src/index.ts +159 -20
- package/src/n8n-client.test.ts +135 -0
- package/src/n8n-client.ts +13 -2
- package/src/response-format.test.ts +355 -0
- package/src/response-format.ts +278 -0
- package/src/tools.ts +68 -3
- package/src/types.ts +33 -0
- package/src/validators.test.ts +101 -4
- package/src/validators.ts +112 -3
package/dist/validators.js
CHANGED
|
@@ -106,8 +106,8 @@ function checkForHardcodedSecrets(node, warnings) {
|
|
|
106
106
|
warnings.push({
|
|
107
107
|
node: node.name,
|
|
108
108
|
rule: 'no_hardcoded_secrets',
|
|
109
|
-
message: `Node "${node.name}" may contain hardcoded secrets -
|
|
110
|
-
severity: '
|
|
109
|
+
message: `Node "${node.name}" may contain hardcoded secrets - consider using $env.VAR_NAME`,
|
|
110
|
+
severity: 'info',
|
|
111
111
|
});
|
|
112
112
|
break;
|
|
113
113
|
}
|
|
@@ -236,3 +236,88 @@ export function validatePartialUpdate(currentWorkflow, nodeName, newParameters)
|
|
|
236
236
|
}
|
|
237
237
|
return warnings;
|
|
238
238
|
}
|
|
239
|
+
// ─────────────────────────────────────────────────────────────
|
|
240
|
+
// Node Type Validation
|
|
241
|
+
// ─────────────────────────────────────────────────────────────
|
|
242
|
+
/**
|
|
243
|
+
* Validate that all node types in an array exist in the available types
|
|
244
|
+
* Returns errors for any invalid node types with suggestions
|
|
245
|
+
*/
|
|
246
|
+
export function validateNodeTypes(nodes, availableTypes) {
|
|
247
|
+
const errors = [];
|
|
248
|
+
for (const node of nodes) {
|
|
249
|
+
if (!availableTypes.has(node.type)) {
|
|
250
|
+
errors.push({
|
|
251
|
+
nodeType: node.type,
|
|
252
|
+
nodeName: node.name,
|
|
253
|
+
message: `Invalid node type "${node.type}" for node "${node.name}"`,
|
|
254
|
+
suggestions: findSimilarTypes(node.type, availableTypes),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return errors;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Find similar node types for suggestions (fuzzy matching)
|
|
262
|
+
* Returns up to 3 suggestions
|
|
263
|
+
*/
|
|
264
|
+
function findSimilarTypes(invalidType, availableTypes) {
|
|
265
|
+
const suggestions = [];
|
|
266
|
+
const searchTerm = invalidType.toLowerCase();
|
|
267
|
+
// Extract the last part after the dot (e.g., "webhook" from "n8n-nodes-base.webhook")
|
|
268
|
+
const typeParts = searchTerm.split('.');
|
|
269
|
+
const shortName = typeParts[typeParts.length - 1];
|
|
270
|
+
for (const validType of availableTypes) {
|
|
271
|
+
const validLower = validType.toLowerCase();
|
|
272
|
+
const validShortName = validLower.split('.').pop() || '';
|
|
273
|
+
// Check for partial matches (substring)
|
|
274
|
+
if (validLower.includes(shortName) ||
|
|
275
|
+
shortName.includes(validShortName) ||
|
|
276
|
+
validShortName.includes(shortName)) {
|
|
277
|
+
suggestions.push(validType);
|
|
278
|
+
if (suggestions.length >= 3)
|
|
279
|
+
break;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
// Check for typos using Levenshtein distance
|
|
283
|
+
const distance = levenshteinDistance(shortName, validShortName);
|
|
284
|
+
const maxLen = Math.max(shortName.length, validShortName.length);
|
|
285
|
+
// Allow up to 2 character differences for short names, or 20% of length for longer ones
|
|
286
|
+
const threshold = Math.max(2, Math.floor(maxLen * 0.2));
|
|
287
|
+
if (distance <= threshold) {
|
|
288
|
+
suggestions.push(validType);
|
|
289
|
+
if (suggestions.length >= 3)
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return suggestions;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Calculate Levenshtein distance between two strings
|
|
297
|
+
*/
|
|
298
|
+
function levenshteinDistance(a, b) {
|
|
299
|
+
if (a.length === 0)
|
|
300
|
+
return b.length;
|
|
301
|
+
if (b.length === 0)
|
|
302
|
+
return a.length;
|
|
303
|
+
const matrix = [];
|
|
304
|
+
// Initialize first column
|
|
305
|
+
for (let i = 0; i <= a.length; i++) {
|
|
306
|
+
matrix[i] = [i];
|
|
307
|
+
}
|
|
308
|
+
// Initialize first row
|
|
309
|
+
for (let j = 0; j <= b.length; j++) {
|
|
310
|
+
matrix[0][j] = j;
|
|
311
|
+
}
|
|
312
|
+
// Fill in the rest of the matrix
|
|
313
|
+
for (let i = 1; i <= a.length; i++) {
|
|
314
|
+
for (let j = 1; j <= b.length; j++) {
|
|
315
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
316
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // deletion
|
|
317
|
+
matrix[i][j - 1] + 1, // insertion
|
|
318
|
+
matrix[i - 1][j - 1] + cost // substitution
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return matrix[a.length][b.length];
|
|
323
|
+
}
|
package/dist/validators.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateWorkflow, validatePartialUpdate } from './validators.js';
|
|
2
|
+
import { validateWorkflow, validatePartialUpdate, validateNodeTypes } from './validators.js';
|
|
3
3
|
const createWorkflow = (overrides = {}) => ({
|
|
4
4
|
id: '1',
|
|
5
5
|
name: 'test_workflow',
|
|
@@ -55,7 +55,7 @@ describe('validateWorkflow', () => {
|
|
|
55
55
|
severity: 'warning',
|
|
56
56
|
}));
|
|
57
57
|
});
|
|
58
|
-
it('
|
|
58
|
+
it('warns on hardcoded secrets', () => {
|
|
59
59
|
const workflow = createWorkflow({
|
|
60
60
|
nodes: [
|
|
61
61
|
{
|
|
@@ -69,10 +69,9 @@ describe('validateWorkflow', () => {
|
|
|
69
69
|
],
|
|
70
70
|
});
|
|
71
71
|
const result = validateWorkflow(workflow);
|
|
72
|
-
expect(result.valid).toBe(false);
|
|
73
72
|
expect(result.warnings).toContainEqual(expect.objectContaining({
|
|
74
73
|
rule: 'no_hardcoded_secrets',
|
|
75
|
-
severity: '
|
|
74
|
+
severity: 'info',
|
|
76
75
|
}));
|
|
77
76
|
});
|
|
78
77
|
it('warns on orphan nodes', () => {
|
|
@@ -229,3 +228,83 @@ describe('validatePartialUpdate', () => {
|
|
|
229
228
|
expect(warnings).toHaveLength(0);
|
|
230
229
|
});
|
|
231
230
|
});
|
|
231
|
+
describe('validateNodeTypes', () => {
|
|
232
|
+
const availableTypes = new Set([
|
|
233
|
+
'n8n-nodes-base.webhook',
|
|
234
|
+
'n8n-nodes-base.set',
|
|
235
|
+
'n8n-nodes-base.code',
|
|
236
|
+
'n8n-nodes-base.httpRequest',
|
|
237
|
+
'@n8n/n8n-nodes-langchain.agent',
|
|
238
|
+
'@n8n/n8n-nodes-langchain.chatTrigger',
|
|
239
|
+
]);
|
|
240
|
+
it('passes when all node types are valid', () => {
|
|
241
|
+
const nodes = [
|
|
242
|
+
{ name: 'webhook_trigger', type: 'n8n-nodes-base.webhook' },
|
|
243
|
+
{ name: 'set_data', type: 'n8n-nodes-base.set' },
|
|
244
|
+
{ name: 'ai_agent', type: '@n8n/n8n-nodes-langchain.agent' },
|
|
245
|
+
];
|
|
246
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
247
|
+
expect(errors).toHaveLength(0);
|
|
248
|
+
});
|
|
249
|
+
it('returns error for invalid node type', () => {
|
|
250
|
+
const nodes = [
|
|
251
|
+
{ name: 'my_node', type: 'n8n-nodes-base.nonexistent' },
|
|
252
|
+
];
|
|
253
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
254
|
+
expect(errors).toHaveLength(1);
|
|
255
|
+
expect(errors[0]).toEqual(expect.objectContaining({
|
|
256
|
+
nodeType: 'n8n-nodes-base.nonexistent',
|
|
257
|
+
nodeName: 'my_node',
|
|
258
|
+
}));
|
|
259
|
+
});
|
|
260
|
+
it('returns errors for multiple invalid node types', () => {
|
|
261
|
+
const nodes = [
|
|
262
|
+
{ name: 'valid_node', type: 'n8n-nodes-base.webhook' },
|
|
263
|
+
{ name: 'invalid_one', type: 'n8n-nodes-base.fake' },
|
|
264
|
+
{ name: 'invalid_two', type: 'n8n-nodes-base.bogus' },
|
|
265
|
+
];
|
|
266
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
267
|
+
expect(errors).toHaveLength(2);
|
|
268
|
+
expect(errors.map((e) => e.nodeName)).toEqual(['invalid_one', 'invalid_two']);
|
|
269
|
+
});
|
|
270
|
+
it('provides suggestions for typos', () => {
|
|
271
|
+
const nodes = [
|
|
272
|
+
{ name: 'trigger', type: 'n8n-nodes-base.webhok' }, // typo: webhok
|
|
273
|
+
];
|
|
274
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
275
|
+
expect(errors).toHaveLength(1);
|
|
276
|
+
expect(errors[0].suggestions).toContain('n8n-nodes-base.webhook');
|
|
277
|
+
});
|
|
278
|
+
it('provides suggestions for partial matches', () => {
|
|
279
|
+
const nodes = [
|
|
280
|
+
{ name: 'code_node', type: 'n8n-nodes-base.cod' }, // partial: cod
|
|
281
|
+
];
|
|
282
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
283
|
+
expect(errors).toHaveLength(1);
|
|
284
|
+
expect(errors[0].suggestions).toContain('n8n-nodes-base.code');
|
|
285
|
+
});
|
|
286
|
+
it('returns empty suggestions when no matches found', () => {
|
|
287
|
+
const nodes = [
|
|
288
|
+
{ name: 'xyz_node', type: 'n8n-nodes-base.xyz123completely_random' },
|
|
289
|
+
];
|
|
290
|
+
const errors = validateNodeTypes(nodes, availableTypes);
|
|
291
|
+
expect(errors).toHaveLength(1);
|
|
292
|
+
expect(errors[0].suggestions).toHaveLength(0);
|
|
293
|
+
});
|
|
294
|
+
it('limits suggestions to 3', () => {
|
|
295
|
+
// Create a set with many similar types
|
|
296
|
+
const manyTypes = new Set([
|
|
297
|
+
'n8n-nodes-base.httpRequest',
|
|
298
|
+
'n8n-nodes-base.httpRequestTool',
|
|
299
|
+
'n8n-nodes-base.httpRequestV1',
|
|
300
|
+
'n8n-nodes-base.httpRequestV2',
|
|
301
|
+
'n8n-nodes-base.httpRequestV3',
|
|
302
|
+
]);
|
|
303
|
+
const nodes = [
|
|
304
|
+
{ name: 'http', type: 'n8n-nodes-base.http' }, // should match multiple
|
|
305
|
+
];
|
|
306
|
+
const errors = validateNodeTypes(nodes, manyTypes);
|
|
307
|
+
expect(errors).toHaveLength(1);
|
|
308
|
+
expect(errors[0].suggestions.length).toBeLessThanOrEqual(3);
|
|
309
|
+
});
|
|
310
|
+
});
|
package/docs/best-practices.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# n8n Best Practices
|
|
2
2
|
|
|
3
|
+
> **These rules are automatically enforced.** The MCP validates, auto-fixes, and formats on every `workflow_create` and `workflow_update`. You'll only see warnings for issues that can't be auto-fixed.
|
|
4
|
+
|
|
3
5
|
## Quick Reference
|
|
4
6
|
|
|
5
7
|
```javascript
|
|
@@ -21,16 +23,16 @@
|
|
|
21
23
|
|
|
22
24
|
## The Rules
|
|
23
25
|
|
|
24
|
-
### 1. snake_case
|
|
26
|
+
### 1. snake_case (auto-fixed)
|
|
25
27
|
|
|
26
28
|
```
|
|
27
29
|
Good: fetch_articles, check_approved, generate_content
|
|
28
30
|
Bad: FetchArticles, Check Approved, generate-content
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
Why: Consistency, readability
|
|
33
|
+
Why: Consistency, readability. **Auto-fixed:** renamed automatically with all references updated.
|
|
32
34
|
|
|
33
|
-
### 2. Explicit References
|
|
35
|
+
### 2. Explicit References (auto-fixed)
|
|
34
36
|
|
|
35
37
|
```javascript
|
|
36
38
|
// Bad - breaks when flow changes
|
|
@@ -40,7 +42,7 @@ Why: Consistency, readability, auto-fixable.
|
|
|
40
42
|
{{ $('node_name').item.json.field }}
|
|
41
43
|
```
|
|
42
44
|
|
|
43
|
-
Why: `$json` references "previous node" implicitly. Reorder nodes, it breaks.
|
|
45
|
+
Why: `$json` references "previous node" implicitly. Reorder nodes, it breaks. **Auto-fixed:** converted to explicit `$('prev_node')` references.
|
|
44
46
|
|
|
45
47
|
### 3. Config Node
|
|
46
48
|
|
|
@@ -63,16 +65,18 @@ Reference everywhere: `{{ $('config').item.json.channel_id }}`
|
|
|
63
65
|
|
|
64
66
|
Why: Change once, not in 5 nodes.
|
|
65
67
|
|
|
66
|
-
### 4. Secrets in Environment
|
|
68
|
+
### 4. Secrets in Environment (recommended)
|
|
67
69
|
|
|
68
70
|
```javascript
|
|
69
|
-
//
|
|
71
|
+
// Hardcoded (works, but less portable)
|
|
70
72
|
{ "apiKey": "sk_live_abc123" }
|
|
71
73
|
|
|
72
|
-
//
|
|
74
|
+
// Environment variable (recommended)
|
|
73
75
|
{{ $env.API_KEY }}
|
|
74
76
|
```
|
|
75
77
|
|
|
78
|
+
Why: Env vars make workflows portable across environments and avoid committing secrets.
|
|
79
|
+
|
|
76
80
|
## Parameter Preservation
|
|
77
81
|
|
|
78
82
|
**Critical:** Partial updates REPLACE the entire `parameters` object.
|
|
@@ -107,9 +111,9 @@ Before updating: read current state with `workflow_get`.
|
|
|
107
111
|
|
|
108
112
|
## AI Nodes
|
|
109
113
|
|
|
110
|
-
### Structured Output
|
|
114
|
+
### Structured Output (auto-fixed)
|
|
111
115
|
|
|
112
|
-
Always set for predictable JSON:
|
|
116
|
+
Always set for predictable JSON. **Auto-fixed:** `promptType: "define"` and `hasOutputParser: true` added automatically.
|
|
113
117
|
|
|
114
118
|
| Setting | Value |
|
|
115
119
|
|---------|-------|
|
|
@@ -149,7 +153,8 @@ When code IS necessary:
|
|
|
149
153
|
| 2. List versions | Know rollback point |
|
|
150
154
|
| 3. Read full workflow | Understand current state |
|
|
151
155
|
| 4. Make targeted change | Minimal surface area |
|
|
152
|
-
|
|
156
|
+
|
|
157
|
+
> **Note:** Validation and cleanup are now automatic. Every create/update validates, auto-fixes, and formats automatically.
|
|
153
158
|
|
|
154
159
|
## Node-Specific Settings
|
|
155
160
|
|
package/docs/node-config.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Node Configuration Guidelines
|
|
2
2
|
|
|
3
|
-
> Settings that make AI-created nodes human-editable
|
|
3
|
+
> Settings that make AI-created nodes human-editable.
|
|
4
|
+
>
|
|
5
|
+
> **Node types are validated.** Invalid types are blocked with suggestions before hitting n8n. Use `node_types_list` to discover available nodes.
|
|
4
6
|
|
|
5
7
|
## Resource Locator Pattern
|
|
6
8
|
|
package/logo.png
ADDED
|
Binary file
|
package/package.json
CHANGED
package/plans/architecture.md
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
+
**Opinionated** n8n workflow automation. Enforces conventions, blocks mistakes, auto-fixes issues.
|
|
4
|
+
|
|
3
5
|
## vs czlonkowski/n8n-mcp
|
|
4
6
|
|
|
5
7
|
| Concern | czlonkowski | @pagelines |
|
|
6
8
|
|---------|-------------|------------|
|
|
9
|
+
| Philosophy | Permissive | Opinionated |
|
|
7
10
|
| Node docs | 70MB SQLite, 1084 nodes | None (use Google) |
|
|
8
11
|
| Templates | 2,709 indexed | None (use n8n.io) |
|
|
9
12
|
| Update mode | Full replace | Patch (preserves params) |
|
|
13
|
+
| After mutations | Nothing | Auto-validate, auto-fix, format |
|
|
14
|
+
| Invalid node types | API error | Blocked with suggestions |
|
|
10
15
|
| Version control | Limited | Auto-snapshot, diff, rollback |
|
|
11
16
|
| Validation | Basic | Rules + expressions + circular refs |
|
|
12
|
-
| Auto-fix | No | Yes |
|
|
17
|
+
| Auto-fix | No | Yes (automatic) |
|
|
13
18
|
| Dependencies | SQLite, heavy | Zero runtime |
|
|
14
|
-
| Lines of code | ~10k+ | ~1,
|
|
19
|
+
| Lines of code | ~10k+ | ~1,500 |
|
|
15
20
|
|
|
16
21
|
## Modules
|
|
17
22
|
|
|
18
23
|
```
|
|
19
24
|
src/
|
|
20
|
-
├── index.ts
|
|
21
|
-
├── types.ts
|
|
22
|
-
├── tools.ts
|
|
23
|
-
├── n8n-client.ts
|
|
24
|
-
├── validators.ts
|
|
25
|
-
├── expressions.ts
|
|
26
|
-
├── autofix.ts
|
|
27
|
-
|
|
25
|
+
├── index.ts # MCP server, tool dispatch
|
|
26
|
+
├── types.ts # Type definitions
|
|
27
|
+
├── tools.ts # Tool schemas (JSON Schema)
|
|
28
|
+
├── n8n-client.ts # n8n REST API client
|
|
29
|
+
├── validators.ts # Validation rules + node type validation
|
|
30
|
+
├── expressions.ts # Expression parsing ({{ }})
|
|
31
|
+
├── autofix.ts # Auto-fix transforms
|
|
32
|
+
├── versions.ts # Version control (local fs)
|
|
33
|
+
└── response-format.ts # Token-efficient response formatting
|
|
28
34
|
```
|
|
29
35
|
|
|
30
36
|
## Data Flow
|
|
@@ -39,21 +45,35 @@ Tool Handler
|
|
|
39
45
|
┌─────────────────────────────────┐
|
|
40
46
|
│ n8n-client validators │
|
|
41
47
|
│ expressions autofix │
|
|
42
|
-
│ versions
|
|
48
|
+
│ versions response-format │
|
|
43
49
|
└─────────────────────────────────┘
|
|
44
50
|
↓
|
|
45
51
|
JSON Response → Claude
|
|
46
52
|
```
|
|
47
53
|
|
|
48
|
-
##
|
|
54
|
+
## Auto-Cleanup Pipeline
|
|
55
|
+
|
|
56
|
+
Every `workflow_create` and `workflow_update` runs this automatically:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
1. Validate node types → Block if invalid (with suggestions)
|
|
60
|
+
2. Execute operation
|
|
61
|
+
3. Validate workflow → Get warnings
|
|
62
|
+
4. Auto-fix fixable issues → snake_case, $json refs, AI settings
|
|
63
|
+
5. Format workflow → Sort nodes, remove nulls
|
|
64
|
+
6. Update if changes → Apply cleanup to n8n
|
|
65
|
+
7. Return result → Only unfixable warnings shown
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Tools (20 total)
|
|
49
69
|
|
|
50
70
|
### Workflow (8)
|
|
51
71
|
| Tool | Description |
|
|
52
72
|
|------|-------------|
|
|
53
73
|
| `workflow_list` | List workflows, filter by active |
|
|
54
74
|
| `workflow_get` | Get full workflow |
|
|
55
|
-
| `workflow_create` | Create with nodes/connections |
|
|
56
|
-
| `workflow_update` | Patch operations |
|
|
75
|
+
| `workflow_create` | Create with nodes/connections (auto-validates, auto-fixes) |
|
|
76
|
+
| `workflow_update` | Patch operations (auto-validates, auto-fixes) |
|
|
57
77
|
| `workflow_delete` | Delete workflow |
|
|
58
78
|
| `workflow_activate` | Enable triggers |
|
|
59
79
|
| `workflow_deactivate` | Disable triggers |
|
|
@@ -72,6 +92,11 @@ JSON Response → Claude
|
|
|
72
92
|
| `workflow_autofix` | Fix auto-fixable issues (dry-run default) |
|
|
73
93
|
| `workflow_format` | Sort nodes, clean nulls |
|
|
74
94
|
|
|
95
|
+
### Discovery (1)
|
|
96
|
+
| Tool | Description |
|
|
97
|
+
|------|-------------|
|
|
98
|
+
| `node_types_list` | Search available node types by name/category |
|
|
99
|
+
|
|
75
100
|
### Version Control (6)
|
|
76
101
|
| Tool | Description |
|
|
77
102
|
|------|-------------|
|
|
@@ -94,20 +119,38 @@ updateSettings, updateName
|
|
|
94
119
|
|
|
95
120
|
Key: Preserves unmodified parameters.
|
|
96
121
|
|
|
122
|
+
## Node Type Validation
|
|
123
|
+
|
|
124
|
+
Before `workflow_create` or `workflow_update` with `addNode`:
|
|
125
|
+
|
|
126
|
+
1. Fetch available types from n8n API
|
|
127
|
+
2. Validate all node types exist
|
|
128
|
+
3. **Block** if invalid with suggestions (fuzzy matching)
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Error: Invalid node types detected:
|
|
132
|
+
Invalid node type "n8n-nodes-base.webhok" for node "trigger".
|
|
133
|
+
Did you mean: n8n-nodes-base.webhook?
|
|
134
|
+
|
|
135
|
+
Use node_types_list to discover available node types.
|
|
136
|
+
```
|
|
137
|
+
|
|
97
138
|
## Validation Rules
|
|
98
139
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
|
102
|
-
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
140
|
+
All rules are checked automatically on every `workflow_create` and `workflow_update`:
|
|
141
|
+
|
|
142
|
+
| Rule | Severity | Auto-fix | Description |
|
|
143
|
+
|------|----------|----------|-------------|
|
|
144
|
+
| `snake_case` | warning | Yes | Names should be snake_case |
|
|
145
|
+
| `explicit_reference` | warning | Yes | Use `$('node')` not `$json` |
|
|
146
|
+
| `ai_structured_output` | warning | Yes | AI node missing structured output |
|
|
147
|
+
| `no_hardcoded_ids` | info | No | Avoid hardcoded IDs |
|
|
148
|
+
| `no_hardcoded_secrets` | info | No | Consider using $env vars |
|
|
149
|
+
| `code_node_usage` | info | No | Code node detected |
|
|
150
|
+
| `in_memory_storage` | warning | No | Non-persistent storage |
|
|
151
|
+
| `orphan_node` | warning | No | Node has no connections |
|
|
152
|
+
| `node_exists` | error | No | Node doesn't exist (for updates) |
|
|
153
|
+
| `parameter_preservation` | error | No | Update would lose parameters |
|
|
111
154
|
|
|
112
155
|
## Expression Validation
|
|
113
156
|
|