@gmag11/nodered-mcp-server 1.0.1

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 (89) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +162 -0
  3. package/index.js +133 -0
  4. package/package.json +58 -0
  5. package/resources/skills/nodered-flow-builder/SKILL.md +659 -0
  6. package/resources/skills/nodered-flow-layout/SKILL.md +395 -0
  7. package/resources/skills/nodered-flowfuse-dashboard/SKILL.md +941 -0
  8. package/resources/skills/nodered-fundamentals/SKILL.md +323 -0
  9. package/resources/skills/nodered-jsonata/SKILL.md +1039 -0
  10. package/resources/skills/nodered-mustache/SKILL.md +588 -0
  11. package/resources/skills/nodered-node-reference/SKILL.md +1020 -0
  12. package/resources/skills/nodered-node-reference/examples/common.json +113 -0
  13. package/resources/skills/nodered-node-reference/examples/network.json +107 -0
  14. package/resources/skills/nodered-node-reference/examples/parser.json +147 -0
  15. package/resources/skills/nodered-node-reference/examples/sequence.json +141 -0
  16. package/resources/skills/nodered-node-reference/examples/storage.json +104 -0
  17. package/resources/skills/nodered-patterns/SKILL.md +414 -0
  18. package/resources/skills/nodered-patterns/examples/error-handler.json +72 -0
  19. package/resources/skills/nodered-patterns/examples/http-endpoint.json +42 -0
  20. package/resources/skills/nodered-patterns/examples/mqtt-subscriber.json +47 -0
  21. package/resources/skills/nodered-patterns/examples/timer-flow.json +50 -0
  22. package/resources/skills/nodered-subflows/SKILL.md +261 -0
  23. package/resources/skills/nodered-uibuilder/SKILL.md +500 -0
  24. package/src/auth/api-key-verifier.js +36 -0
  25. package/src/auth/composite-verifier.js +59 -0
  26. package/src/auth/config.js +106 -0
  27. package/src/auth/oauth-clients-store.js +107 -0
  28. package/src/auth/oauth-provider.js +149 -0
  29. package/src/auth/oauth-token-store.js +312 -0
  30. package/src/nodered/auth.js +158 -0
  31. package/src/nodered/client.js +199 -0
  32. package/src/nodered/comms-client.js +500 -0
  33. package/src/renderer/colors.js +161 -0
  34. package/src/renderer/geometry.js +115 -0
  35. package/src/renderer/html-builder.js +571 -0
  36. package/src/renderer/index.js +51 -0
  37. package/src/renderer/ir-builder.js +161 -0
  38. package/src/renderer/layout.js +126 -0
  39. package/src/renderer/mermaid-builder.js +109 -0
  40. package/src/renderer/svg-builder.js +228 -0
  41. package/src/schemas/responses.js +283 -0
  42. package/src/server.js +844 -0
  43. package/src/skills/loader.js +84 -0
  44. package/src/staging-store.js +258 -0
  45. package/src/tools/add-nodes-to-group.js +216 -0
  46. package/src/tools/connect-nodes.js +115 -0
  47. package/src/tools/constants.js +45 -0
  48. package/src/tools/create-flow.js +87 -0
  49. package/src/tools/create-node.js +126 -0
  50. package/src/tools/create-subflow-instance.js +123 -0
  51. package/src/tools/create-subflow.js +101 -0
  52. package/src/tools/delete-context.js +60 -0
  53. package/src/tools/delete-flow.js +81 -0
  54. package/src/tools/delete-group.js +116 -0
  55. package/src/tools/delete-node.js +73 -0
  56. package/src/tools/delete-subflow.js +103 -0
  57. package/src/tools/deploy.js +94 -0
  58. package/src/tools/disconnect-nodes.js +158 -0
  59. package/src/tools/export-flow.js +161 -0
  60. package/src/tools/export-subflow.js +78 -0
  61. package/src/tools/flow-utils.js +376 -0
  62. package/src/tools/get-config-nodes.js +86 -0
  63. package/src/tools/get-context.js +76 -0
  64. package/src/tools/get-flow-diagram.js +99 -0
  65. package/src/tools/get-flow-nodes.js +116 -0
  66. package/src/tools/get-flows.js +74 -0
  67. package/src/tools/get-node-detail.js +77 -0
  68. package/src/tools/get-node-type-detail.js +92 -0
  69. package/src/tools/get-palette-nodes.js +63 -0
  70. package/src/tools/get-staging-status.js +34 -0
  71. package/src/tools/get-subflow-detail.js +110 -0
  72. package/src/tools/get-subflows.js +105 -0
  73. package/src/tools/import-flow.js +310 -0
  74. package/src/tools/inject-message.js +117 -0
  75. package/src/tools/install-node.js +31 -0
  76. package/src/tools/read-debug-messages.js +155 -0
  77. package/src/tools/refresh-staging.js +62 -0
  78. package/src/tools/remove-nodes-from-group.js +162 -0
  79. package/src/tools/render-staging.js +69 -0
  80. package/src/tools/response-utils.js +42 -0
  81. package/src/tools/search-nodes.js +134 -0
  82. package/src/tools/uninstall-node.js +31 -0
  83. package/src/tools/update-flow.js +95 -0
  84. package/src/tools/update-group.js +77 -0
  85. package/src/tools/update-node.js +132 -0
  86. package/src/tools/update-subflow.js +84 -0
  87. package/src/transport/http.js +252 -0
  88. package/src/transport/stdio.js +16 -0
  89. package/src/transport/ws-server.js +223 -0
@@ -0,0 +1,395 @@
1
+ ---
2
+ name: nodered-flow-layout
3
+ description: >-
4
+ Comprehensive rules for positioning and arranging nodes in Node-RED flows.
5
+ Covers debug node placement, vertical centering around branch points,
6
+ horizontal spacing standards, comment placement, group spacing, and
7
+ bounding-box calculations. Use whenever creating or rearranging multi-node
8
+ flows to ensure visual clarity and avoid overlapping elements.
9
+ tools:
10
+ - refresh-staging
11
+ - get-flows
12
+ - get-flow-nodes
13
+ - create-node
14
+ - add-nodes-to-group
15
+ - remove-nodes-from-group
16
+ - update-group
17
+ - update-node
18
+ - deploy
19
+ ---
20
+
21
+ # Node-RED Flow Layout
22
+
23
+ Positioning rules extracted from real-world flow analysis. These ensure flows are readable, well-organized, and visually balanced. Use these rules when creating multi-node flows, adding groups, or rearranging existing flows.
24
+
25
+ > **Prerequisites:** Read `nodered-fundamentals` for core vocabulary. See `nodered-flow-builder` for node dimension tables and the `estimateNodeHeight()` formula.
26
+
27
+ ---
28
+
29
+ ## 1️⃣ Debug Node Placement
30
+
31
+ Debug nodes can be placed **above** or **below** the node they monitor, never on the same horizontal row — unless they are the final terminal node of a branch.
32
+
33
+ | Scenario | Placement | ΔY | Example |
34
+ |----------|-----------|-----|---------|
35
+ | Intermediate debug | Above source node | −40px | `Alerts (y=540)` above `Alert (y=580)` |
36
+ | Intermediate debug | Below source node | +40px | `Out A (y=120)` below `Process A (y=80)` |
37
+ | Terminal debug (end of branch) | Same row as predecessor | +0px | Allowed only if no further nodes follow |
38
+
39
+ ```
40
+ ✅ CORRECT:
41
+ [function: Alert] [function: Alert]
42
+ ↓ ↓
43
+ [debug: Alerts] (y−40) [debug: Alerts] (y+40)
44
+
45
+ ✅ CORRECT (terminal only):
46
+ [function: Process A]──[debug: Out A] (same y, end of branch)
47
+
48
+ ❌ WRONG:
49
+ [function: Alert]──[debug: Alerts]──[function: Next] (debug inline in middle of chain)
50
+ ```
51
+
52
+ **Rule of thumb:** Debug nodes are diagnostic tools. Place them where they don't interrupt the visual flow of the main processing chain. Above/below their source node keeps the main row clean.
53
+
54
+ ---
55
+
56
+ ## 2️⃣ Vertical Centering Around Branch Points
57
+
58
+ ### Before a split (switch / multi-output)
59
+
60
+ Position the splitting node at the **vertical midpoint** of its output branches.
61
+
62
+ ```javascript
63
+ /**
64
+ * Calculate the Y position for a node that fans out to multiple branches.
65
+ * @param {number[]} branchYValues - Y coordinates of each branch target
66
+ * @returns {number} Centered Y coordinate
67
+ */
68
+ function centerYForSplit(branchYValues) {
69
+ const sum = branchYValues.reduce((a, b) => a + b, 0);
70
+ return Math.round(sum / branchYValues.length);
71
+ }
72
+ ```
73
+
74
+ ```
75
+ Example: Route switch → 3 branches at y=80, y=220, y=400
76
+ centerY = (80 + 220 + 400) / 3 = 233 → rounded to 220
77
+ Route placed at y=220 ✓
78
+
79
+ Example: Threshold? switch → 2 branches at y=580, y=680
80
+ centerY = (580 + 680) / 2 = 630 → rounded to 640
81
+ Threshold? placed at y=640 ✓
82
+ ```
83
+
84
+ ### After a merge (join / link in)
85
+
86
+ Same principle — position the merged node at the vertical center of its input branches.
87
+
88
+ ```
89
+ Branch A (y=200) ──┐
90
+ ├── [join: Merge] (centered y)
91
+ Branch B (y=300) ──┘
92
+
93
+ centerY = (200 + 300) / 2 = 250
94
+ ```
95
+
96
+ ### General centering rule
97
+
98
+ Whenever N wires converge on or diverge from a single node, that node's Y should equal the **arithmetic mean** of the Y coordinates at the other ends of those wires.
99
+
100
+ ---
101
+
102
+ ## 3️⃣ Horizontal Spacing Standards
103
+
104
+ | Transition | ΔX | Use Case |
105
+ |------------|-----|----------|
106
+ | inject → function | **+170 to +190** | Starting a row |
107
+ | function → function | **+170 to +200** | Sequential processing |
108
+ | function/change → debug (adjacent) | **+10** | Debug alongside predecessor |
109
+ | switch → branch node | **+240** | Crossing group boundaries |
110
+
111
+ ```
112
+ Measured from real reference flows:
113
+ Start(110) → Validate(290) → Route(480) Δx = 180, 190
114
+ Sanitize(300) → Enrich(470) → Validate(640) Δx = 170, 170
115
+ Out A(930) right next to Process A(920) Δx = 10 (debug adjacent)
116
+ ```
117
+
118
+ ### Long-name node adjustment
119
+
120
+ When a node has a name >25 characters, increase the horizontal gap:
121
+
122
+ ```javascript
123
+ function spacingForNode(nodeName) {
124
+ const baseSpacing = 180;
125
+ if (nodeName.length > 25) {
126
+ return baseSpacing + (nodeName.length - 25) * 8; // +8px per extra char
127
+ }
128
+ return baseSpacing;
129
+ }
130
+
131
+ // Examples:
132
+ spacingForNode("Process A") // → 180
133
+ spacingForNode("Handle Type C (extended)") // → 180 + 2*8 = 196 → ~200
134
+ ```
135
+
136
+ ---
137
+
138
+ ## 4️⃣ Comment Node Placement
139
+
140
+ | Comment refers to... | Placement | Position |
141
+ |----------------------|-----------|----------|
142
+ | **A group** | Inside that group | Top of group rectangle (y = group.y + margin) |
143
+ | **A single node** | Above the node | Directly above, Δy ≈ −30px |
144
+ | **A section (no group)** | Above the first node | Left-aligned with first node, above it |
145
+
146
+ ```
147
+ ✅ Comments inside groups:
148
+ ┌─ Main Router ─────────────────────────────┐
149
+ │ 🟢 Main Routing Flow — routes by category │ ← comment inside group
150
+ │ [Start] → [Validate] → [Route] │
151
+ └───────────────────────────────────────────┘
152
+
153
+ ✅ Comment above a single node:
154
+ ⚠️ Alert Path — threshold ≥ 10 ← comment above node (Δy = −30)
155
+ [function: Alert] → [debug: Alerts]
156
+ ```
157
+
158
+ **When creating a group with a comment:**
159
+ 1. Create the comment node first (at the group's intended top)
160
+ 2. Create the group, including the comment in its `nodeIds`
161
+ 3. Position functional nodes below the comment within the group
162
+
163
+ ---
164
+
165
+ ## 5️⃣ Group-to-Group Vertical Spacing
166
+
167
+ When stacking independent groups vertically, use the following spacing conventions:
168
+
169
+ | Relationship | Gap | Use |
170
+ |-------------|-----|-----|
171
+ | **Closely related** (branches of same switch) | ~40px | Branch A → Branch B → Branch C |
172
+ | **Loosely related** (same flow section) | ~80px | Branch C → Scheduled Pipeline |
173
+ | **Unrelated** (different flow section) | ~100px | Scheduled Pipeline → Manual ETL |
174
+
175
+ ### Vertical Packing Formula
176
+
177
+ Calculate the gap based on what sits between rows:
178
+
179
+ ```javascript
180
+ /**
181
+ * Calculate vertical gap between consecutive rows.
182
+ * @param {number} debugCount - Number of debug nodes between rows (0-2)
183
+ * @param {boolean} hasComment - Whether next row starts with a comment
184
+ * @param {string} relationship - 'same-group' | 'related' | 'unrelated'
185
+ * @returns {number} Gap in pixels
186
+ */
187
+ function verticalGap(debugCount = 0, hasComment = true, relationship = 'related') {
188
+ const baseGaps = { 'same-group': 40, 'related': 60, 'unrelated': 80 };
189
+ const debugOverhead = debugCount * 20; // each debug adds half its offset
190
+ const commentOverhead = hasComment ? 10 : 0;
191
+ return baseGaps[relationship] + debugOverhead + commentOverhead;
192
+ }
193
+
194
+ // Examples:
195
+ verticalGap(0, true, 'same-group') // → 50 (comment overhead inside group)
196
+ verticalGap(1, false, 'related') // → 80 (one debug between related groups)
197
+ verticalGap(0, true, 'unrelated') // → 90 (comment to unrelated section)
198
+ ```
199
+
200
+ ```
201
+ Measured from compact layout:
202
+
203
+ Group A bottom (y + h): ~310
204
+ ↓ +80px (related sections with debug overhead)
205
+ Group B top (y): ~390
206
+
207
+ Group B bottom: ~440
208
+ ↓ +100px (unrelated sections)
209
+ Group C top: ~540
210
+ ```
211
+
212
+ ---
213
+
214
+ ## 6️⃣ Group Bounding Box Calculation
215
+
216
+ When creating groups via `add-nodes-to-group`, calculate the bounding box to enclose all member nodes plus any comments:
217
+
218
+ ```javascript
219
+ /**
220
+ * Calculate group bounding box from member node positions.
221
+ * @param {Array<{x: number, y: number}>} positions - Node coordinates
222
+ * @param {Object} options
223
+ * @param {number} options.padding - Edge padding (default 20)
224
+ * @param {boolean} options.hasComment - Extra top space for a comment (default true)
225
+ * @returns {{x: number, y: number, w: number, h: number}}
226
+ */
227
+ function calculateGroupBounds(positions, options = {}) {
228
+ const { padding = 10, hasComment = true } = options;
229
+ const xs = positions.map(n => n.x);
230
+ const ys = positions.map(n => n.y);
231
+
232
+ const topCommentSpace = hasComment ? 30 : 0;
233
+
234
+ return {
235
+ x: Math.min(...xs) - padding,
236
+ y: Math.min(...ys) - padding - topCommentSpace,
237
+ w: Math.max(...xs) - Math.min(...xs) + 20, // 10px margin each side
238
+ h: Math.max(...ys) - Math.min(...ys) + 20 + topCommentSpace // 10px margin + comment
239
+ };
240
+ }
241
+
242
+ // Usage with add-nodes-to-group:
243
+ // const bounds = calculateGroupBounds(memberPositions);
244
+ // Then update the group:
245
+ // update-group(groupId, { properties: { x: bounds.x, y: bounds.y, w: bounds.w, h: bounds.h } });
246
+ ```
247
+
248
+ **Constants used:**
249
+ - Node width: **160px** (standard)
250
+ - Node height: **40px** (standard, 1-3 outputs), **60px** (4-6 outputs)
251
+ - Comment top space: **30px** (allows one line of text)
252
+ - Edge padding: **10px** (tight, minimum distance from group border to nodes)
253
+
254
+ ---
255
+
256
+ ## 7️⃣ Compact Layout Principles
257
+
258
+ These principles produce tighter, more readable flows than the baseline rules alone. Apply after the basic positioning rules above.
259
+
260
+ ### 7.1 Column Grid System
261
+
262
+ Align every node to a shared set of X columns. Nodes of the same functional role share the same column regardless of which chain they belong to. This creates a grid that is easier to scan and reduces total width.
263
+
264
+ | Column | X | Node types |
265
+ |--------|------|-----------------------------------------------|
266
+ | C0 | 120 | `inject`, `http in`, `comment` |
267
+ | C1 | 300 | `function` (Δx = 180 from C0) |
268
+ | C2 | 480 | `http response`, `template`, `debug` (mid-chain) |
269
+ | C3 | 660 | `http response` (extended chain, Δx = 180 from C2) |
270
+ | C4 | 840 | `change`, `switch` (branch processing, Δx = 240 from C3) |
271
+ | C5 | 1020 | `debug` (terminal, end of branch, Δx = 180 from C4) |
272
+
273
+ ```
274
+ Column alignment example (top-down view):
275
+
276
+ C0 (120) C1 (300) C2 (480) C3 (660) C4 (780) C5 (1000)
277
+ ──────────────────────────────────────────────────────────────────────
278
+ [HTTP In] → [Function] → [Response] [Debug] ← status chain
279
+ [HTTP In] → [Function] → [Template] → [Response] ← health chain
280
+ [Inject ] → [Function] → [Debug] ← test chain
281
+ [Comment] [Switch ] → [Change] → [Debug] ← alert chain
282
+ ↘ [Change] → [Debug]
283
+ ↘ [Delay ] → [Debug]
284
+ ```
285
+
286
+ ### 7.2 Switch Bridge Placement
287
+
288
+ When a switch node bridges two groups (source group → switch → target group with branches), position it close to its source, not centered among its branches:
289
+
290
+ ```javascript
291
+ /**
292
+ * Y for a switch bridging between groups.
293
+ * @param {number} sourceY - Y of the upstream node
294
+ * @param {number[]} branchYValues - Y coordinates of branch targets
295
+ * @returns {number} Switch Y position
296
+ */
297
+ function switchBridgeY(sourceY, branchYValues) {
298
+ const minBranch = Math.min(...branchYValues);
299
+ // Hug the top of the branches, but don't go above source + 40
300
+ return Math.max(sourceY + 40, minBranch - 20);
301
+ }
302
+
303
+ // Example: source at y=80, branches at [560, 660, 780]
304
+ // max(80+40, 560-20) = max(120, 540) = 540
305
+ // switch at y=540 ✓ (close to source, near top of branches)
306
+ ```
307
+
308
+ | Rule | Old (center) | New (bridge) |
309
+ |------|-------------|--------------|
310
+ | Switch Y | avg(branches) → 660 | max(source+40, min(branch)-20) → 540 |
311
+ | Visual effect | Switch floats mid-group | Switch hugs top, wire shorter |
312
+
313
+ ### 7.3 Debug-on-Response
314
+
315
+ When a debug node shares its X column with an `http response` or terminal node, place it **on the response**, offset vertically by −20px, rather than on the source function:
316
+
317
+ ```
318
+ ❌ Debug on function: ✅ Debug on response:
319
+ [Func]──[Resp] [Func]──[Resp]
320
+ ↘ [Debug] (y-40) [Debug] (y-20, shares X with Resp)
321
+ ```
322
+
323
+ ```javascript
324
+ function debugY(sourceNode, sameColumnNode) {
325
+ // If debug shares X with a terminal node, attach to terminal
326
+ if (sameColumnNode && isTerminal(sameColumnNode)) {
327
+ return sameColumnNode.y - 20;
328
+ }
329
+ // Otherwise, standard ±40px from source
330
+ return sourceNode.y - 40;
331
+ }
332
+ ```
333
+
334
+ ### 7.4 Tight Group Boxing
335
+
336
+ When groups have no internal comment, reduce the top padding:
337
+
338
+ ```javascript
339
+ function tightGroupBounds(positions, hasComment = true) {
340
+ const xs = positions.map(n => n.x);
341
+ const ys = positions.map(n => n.y);
342
+ const margin = 10;
343
+ const commentSpace = hasComment ? 30 : 0;
344
+ return {
345
+ x: Math.min(...xs) - margin,
346
+ y: Math.min(...ys) - margin - commentSpace,
347
+ w: Math.max(...xs) - Math.min(...xs) + margin * 2,
348
+ h: Math.max(...ys) - Math.min(...ys) + margin * 2 + commentSpace
349
+ };
350
+ }
351
+ ```
352
+
353
+ ### 7.5 Row Height Budget
354
+
355
+ Each functional row uses approximately **60px** (40px node + 20px clearance). When planning vertical layout, count rows:
356
+
357
+ ```
358
+ Row budget = (numberOfRows * 60) + (numberOfInterRowGaps * avgGapSize)
359
+
360
+ Example: 4 rows with 3 gaps
361
+ Rows: 4 × 60 = 240px
362
+ Gaps: (50 + 80 + 90) = 220px
363
+ Total: 460px vertical footprint
364
+ ```
365
+
366
+ ---
367
+
368
+ ## 🎯 Quick Reference Card
369
+
370
+ | Rule | Key Value |
371
+ |------|-----------|
372
+ | Horizontal spacing (node→node) | **170–190px** |
373
+ | Horizontal spacing (long names >25 chars) | **+8px per extra char** |
374
+ | Debug offset from parent | **±40px** vertical |
375
+ | Debug on terminal node | **−20px** from terminal Y |
376
+ | Debug terminal inline | Same row allowed |
377
+ | Comment above single node | **Δy = −30px** |
378
+ | Comment inside group | At group top (+30px in bounding box) |
379
+ | Branch point centering | **Average** of branch Y values |
380
+ | Switch bridging groups | **max(src+40, min(branch)−20)** |
381
+ | Unrelated group gap | **~100px** |
382
+ | Related group (same section) gap | **~80px** |
383
+ | Same-group row gap | **~50px** |
384
+ | First node position | **(x=120, y=80)** absolute |
385
+ | Node width (standard) | **160px** |
386
+ | Node height (standard) | **40px** (1-3 outs), **60px** (4-6 outs) |
387
+ | Edge padding (groups) | **10px** (tight) |
388
+ | Row height budget | **60px** per row (node + clearance) |
389
+
390
+ ---
391
+
392
+ ## 🔗 Related Skills
393
+
394
+ - **`nodered-flow-builder`** — Node dimension tables, `estimateNodeHeight()` formula, and basic positioning grid. Always consult before placing nodes.
395
+ - **`nodered-fundamentals`** — Core vocabulary: nodes, flows, wires, groups.