@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,659 @@
1
+ ---
2
+ name: nodered-flow-builder
3
+ description: >-
4
+ Step-by-step operational guide for building, editing, testing, and debugging Node-RED flows using MCP tools.
5
+ tools:
6
+ - refresh-staging
7
+ - deploy
8
+ - get-staging-status
9
+ - get-flows
10
+ - get-config-nodes
11
+ - get-subflows
12
+ - get-flow-nodes
13
+ - get-flow-diagram
14
+ - get-node-detail
15
+ - create-flow
16
+ - update-flow
17
+ - delete-flow
18
+ - create-node
19
+ - update-node
20
+ - delete-node
21
+ - create-subflow
22
+ - update-subflow
23
+ - delete-subflow
24
+ - create-subflow-instance
25
+ - add-nodes-to-group
26
+ - remove-nodes-from-group
27
+ - update-group
28
+ - connect-nodes
29
+ - disconnect-nodes
30
+ - export-flow
31
+ - import-flow
32
+ - inject-message
33
+ - read-debug-messages
34
+ - get-context
35
+ - search-nodes
36
+ ---
37
+
38
+ # Node-RED Flow Builder
39
+
40
+ Step-by-step operational guide for building, editing, testing, and debugging Node-RED flows via MCP tools. Follow the numbered sequences exactly.
41
+
42
+ > **Prerequisites:** Read `nodered-fundamentals` first for core vocabulary. Use `nodered-node-reference` for node type properties.
43
+
44
+ ---
45
+
46
+ ## 🔄 ALWAYS Sync Before Editing
47
+
48
+ **Before starting ANY workflow** (create, edit, delete, import), you MUST sync the staging state with the server. Follow this two-step sequence in order:
49
+
50
+ ### Step 0a: Refresh staging (MANDATORY — do this FIRST)
51
+ ```
52
+ refresh-staging()
53
+ ```
54
+
55
+ ⚠️ **CRITICAL:** This is the very first operation before ANY editing session. It discards any stale un-deployed changes and re-fetches the latest flow state from Node-RED. This prevents version mismatch errors (HTTP 409) when deploying. If you skip this step and the Node-RED editor has been used to modify flows, your deploy will fail and ALL your staged changes will be lost.
56
+
57
+ > **When to refresh:** Always call `refresh-staging` at the start of a new editing session. After deploy, staging is automatically synced — no manual refresh needed.
58
+
59
+ ### Step 0b: Read current state
60
+ ```
61
+ get-flows()
62
+ get-config-nodes()
63
+ get-subflows()
64
+ ```
65
+
66
+ This returns the current list of flow tabs, their IDs, labels, node counts, lock status, config nodes, and subflows. Use this information to:
67
+ - Confirm the target flow exists and get its correct `flowId`
68
+ - Check if a flow is **locked** before attempting edits (locked flows reject modifications)
69
+ - Identify which flow to work on when the user refers to it by name
70
+ - Discover existing config nodes (groups, brokers, etc.) and subflows
71
+
72
+ **After every `deploy`**, the staging store automatically re-fetches flows from the server — you do NOT need to call `refresh-staging` or `get-flows` manually after deploy. The internal state is already synced.
73
+
74
+ ---
75
+
76
+ ## Workflow A — Build a Flow from Scratch
77
+
78
+ The primary workflow for creating a new flow. Follow these steps in order:
79
+
80
+ ### Step 1: Create a flow tab
81
+ ```
82
+ create-flow(label: "My Flow", info: "Description of what this flow does")
83
+ ```
84
+ Save the returned `flowId` — you need it for every subsequent step.
85
+
86
+ ### Step 2: Create nodes (one by one)
87
+ ```
88
+ create-node(type: "inject", flowId: "<flowId>", properties: { name: "Trigger", payload: "hello", payloadType: "str" }, x: 100, y: 100)
89
+ create-node(type: "function", flowId: "<flowId>", properties: { name: "Transform", func: "msg.payload = msg.payload.toUpperCase();\nreturn msg;", outputs: 1 }, x: 300, y: 100)
90
+ create-node(type: "debug", flowId: "<flowId>", properties: { name: "Output", complete: "payload", targetType: "msg" }, x: 500, y: 100)
91
+ ```
92
+ Save all returned `nodeId` values.
93
+
94
+ **💡 Setting a node description:** Add `info` to the `properties` object:
95
+ ```
96
+ create-node(type: "ping", flowId: "<flowId>", properties: { name: "Ping", host: "192.168.1.1", info: "Pings the main server" }, x: 300, y: 100)
97
+ ```
98
+ The `info` field corresponds to the **Description** shown in the Node-RED editor UI. When a user asks to "add a description" or "describe the node", they mean setting `info`.
99
+
100
+ **💡 Creating comment nodes:** Use `type: "comment"` to annotate flows. The `name` field is a **short label** (1-3 words, visible on canvas). Use `info` for detailed documentation (tooltip on hover):
101
+ ```
102
+ create-node(type: "comment", flowId: "<flowId>", properties: { name: "My Section", info: "Detailed notes about this section of the flow" }, x: 100, y: 300)
103
+ ```
104
+
105
+ **💡 Switch nodes — always set `outputs` explicitly:**
106
+ - **switch:** Set `outputs` to `rules.length`. Without it, the editor defaults to 1 visible port even if wires are connected. Also set `repair: false` and `checkall: "false"` (string, not boolean).
107
+
108
+ ### Step 3: Wire nodes together
109
+ ```
110
+ connect-nodes(fromNodeId: "<injectId>", outputPort: 0, toNodeId: "<functionId>")
111
+ connect-nodes(fromNodeId: "<functionId>", outputPort: 0, toNodeId: "<debugId>")
112
+ ```
113
+ **Critical:** Always wire after creating nodes. An unwired node is isolated and will never receive messages.
114
+
115
+ ### Step 4: Verify the flow
116
+ ```
117
+ get-flow-diagram(flowId: "<flowId>")
118
+ ```
119
+ Review the Mermaid diagram. Confirm all nodes appear and wires connect as expected.
120
+
121
+ ### Step 5: Deploy changes
122
+ **CRITICAL — Changes are staged, not live!** All create-node, connect-nodes, update-node, etc. operations stage changes in a local workspace. They are NOT active until you deploy:
123
+ ```
124
+ deploy(deployType: "nodes")
125
+ ```
126
+ Default deploy type is `"nodes"` (least disruptive — only modified nodes restart). Use `"flows"` to restart modified flow tabs, or `"full"` for a complete restart.
127
+
128
+ Check what's pending before deploying:
129
+ ```
130
+ get-staging-status()
131
+ ```
132
+
133
+ **🔄 Post-deploy sync:** The deploy tool automatically refreshes all flows from the server after a successful deploy. The staging store is always in sync with Node-RED after deploy completes — no manual refresh needed.
134
+
135
+ ### Step 6: Test the flow
136
+ ```
137
+ inject-message(nodeId: "<injectId>")
138
+ read-debug-messages(nodeName: "<debugNodeName>", last: 5)
139
+ ```
140
+
141
+ **⚠️ Important:** You MUST deploy before testing. `inject-message` will error if there are undeployed changes.
142
+
143
+ ---
144
+
145
+ ## Port Numbering
146
+
147
+ All output ports are **0-indexed**. Port 0 is the leftmost output on the node.
148
+
149
+ ```
150
+ ┌──────────┐
151
+ │ switch │── port 0 (output 1)
152
+ │ │── port 1 (output 2)
153
+ │ │── port 2 (output 3)
154
+ └──────────┘
155
+ ```
156
+
157
+ **Example — switch node with 3 routes:**
158
+ ```
159
+ connect-nodes(fromNodeId: "<switchId>", outputPort: 0, toNodeId: "<route1Id>")
160
+ connect-nodes(fromNodeId: "<switchId>", outputPort: 1, toNodeId: "<route2Id>")
161
+ connect-nodes(fromNodeId: "<switchId>", outputPort: 2, toNodeId: "<route3Id>")
162
+ ```
163
+
164
+ **Batch wiring** (wire multiple ports in one call):
165
+ ```
166
+ connect-nodes(fromNodeId: "<switchId>", connections: [
167
+ { outputPort: 0, toNodeId: "<route1Id>" },
168
+ { outputPort: 1, toNodeId: "<route2Id>" },
169
+ { outputPort: 2, toNodeId: "<route3Id>" }
170
+ ])
171
+ ```
172
+
173
+ > ⚠️ **Never set `wires` in `update-node` properties.** Wiring is managed exclusively through `connect-nodes` and `disconnect-nodes`.
174
+
175
+ ---
176
+
177
+ ## Coordinate Grid & Node Dimensions
178
+
179
+ Node positions on the canvas follow a grid pattern to keep flows readable and avoid overlapping nodes. Understanding node dimensions is critical for calculating proper coordinates.
180
+
181
+ ### Standard Node Dimensions
182
+
183
+ Based on empirical measurements from Node-RED editor:
184
+
185
+ | Node Type | Width (px) | Height (px) | Notes |
186
+ |-----------|------------|-------------|-------|
187
+ | **Standard function/debug/inject** | ~160 | ~40 | Short names (≤20 chars) |
188
+ | **Long name function** | ~320+ | ~40 | Names >30 chars expand width |
189
+ | **Config nodes** | ~180 | ~40 | Slightly wider than standard |
190
+ | **Function w/ 1-3 outputs** | ~160 | ~40 | Height stays at ~40px |
191
+ | **Function w/ 4-5 outputs** | ~160 | ~60 | Height jumps ~20px at 4+ outputs |
192
+ | **Higher outputs (6+)** | ~160 | ~80+ | Extrapolated; verify empirically |
193
+
194
+ **Key observations:**
195
+ - All nodes have consistent **width** (~160px for standard names) regardless of output count
196
+ - Width varies based on node name length, NOT number of outputs
197
+ - Multiple output ports stack vertically on the right side — they do NOT expand width
198
+ - Height is stable (~40px) for 1-3 outputs, then jumps to ~60px at 4+ outputs
199
+ - Ports are positioned ~15px from left/right edges
200
+ - Minimum safe horizontal spacing between connected nodes: **120px center-to-center** (160px recommended)
201
+ - Minimum safe vertical spacing between rows: **40px center-to-center** for 1-3 outputs, **60px** for 4+ outputs
202
+
203
+ **Important:** When placing nodes with many outputs (4+), increase vertical spacing to avoid port overlap with adjacent rows. Use +60px Y delta instead of +40px.
204
+
205
+ ### 📐 Height Formula (by output count)
206
+
207
+ Node height is **fixed at ~40px** for up to 3 outputs, then increases in ~20px steps for every additional group of 3 outputs:
208
+
209
+ ```javascript
210
+ /**
211
+ * Estimate a function node's height based on output count.
212
+ * @param {number} outputs - Number of output ports (1-based)
213
+ * @returns {number} Estimated height in pixels
214
+ */
215
+ function estimateNodeHeight(outputs) {
216
+ const baseHeight = 40; // minimum height for 1-3 outputs
217
+ const stepHeight = 20; // extra height per group of 3 additional outputs
218
+ const groupSize = 3; // outputs per height group
219
+ const extra = Math.max(0, Math.floor((outputs - 1) / groupSize));
220
+ return baseHeight + extra * stepHeight;
221
+ }
222
+ ```
223
+
224
+ **Quick reference:**
225
+ | Outputs | Height | ΔY to next row |
226
+ |---------|--------|----------------|
227
+ | 1–3 | 40px | +40px |
228
+ | 4–6 | 60px | +60px |
229
+ | 7–9 | 80px | +80px |
230
+ | 10–12 | 100px | +100px |
231
+
232
+ ```javascript
233
+ // When placing the next row, use the taller node's height:
234
+ const nextY = currentY + estimateNodeHeight(currentOutputs);
235
+ ```
236
+
237
+ ### 🎯 Positioning Guidelines
238
+
239
+ **📍 CRITICAL: First Node Position**
240
+
241
+ The first node in ANY flow MUST start at coordinates **(x=120, y=80)**. This is the absolute top-left starting point — nothing should be placed above y=80 or to the left of x=120. (When using groups with comments, the first node may shift down by ~30-50px to accommodate the group header.)
242
+
243
+ | Convention | X | Y | Use Case |
244
+ |------------|---|---|----------|
245
+ | **FIRST NODE (mandatory)** | **120** | **80** | **Absolute starting point** |
246
+ | Inline next node | +**180** | same Y | Sequential processing nodes (170-190px range) |
247
+ | Debug adjacent | +**10** | ±**40** | Debug alongside predecessor, above or below |
248
+ | Branch down (1-3 outs) | same X | +**60** | Alternative path from switch |
249
+ | Branch down (4+ outs) | same X | +**80** | Extra space for taller nodes |
250
+ | Branch row | +180 | +60 | Alternative row for complex branches |
251
+
252
+ ```
253
+ ─────────────────────────────────────────────────────────┐
254
+ │ CANVAS BOUNDARY │
255
+ │ ┌─────────────────────────────────────────────────────┐ │
256
+ │ │ │ │
257
+ │ │ [120,80] ← FIRST NODE STARTS HERE │ │
258
+ │ │ ↓ │ │
259
+ │ │ Row 1: [inject]──(+200)──[function]──(+200)──[debug] │
260
+ │ │ │ │ │
261
+ │ │ Row 2: └─(+200, +60)──[debug:error] │
262
+ │ │ │ │
263
+ │ │ ⚠️ Nothing above y=80 or left of x=120 │ │
264
+ │ └─────────────────────────────────────────────────────┘ │
265
+ ─────────────────────────────────────────────────────────┘
266
+ ```
267
+
268
+ **Why (120, 80)?**
269
+ - Provides adequate margin from canvas edges
270
+ - Matches Node-RED's default grid alignment
271
+ - Ensures consistent positioning across all flows
272
+ - Leaves room for node icons and ports without clipping
273
+
274
+ > 📐 **For advanced layout rules** (debug placement, branch-point centering, group spacing, comment positioning, bounding-box calculations), see **`nodered-flow-layout`**.
275
+
276
+ ### 📐 Calculating Coordinates for Long Names
277
+
278
+ When placing nodes with long names, calculate width dynamically:
279
+
280
+ ```javascript
281
+ // Estimate node width based on name length
282
+ function estimateNodeWidth(nodeName) {
283
+ const baseWidth = 160; // minimum width for short names
284
+ const charWidth = 8; // approximate pixels per character
285
+ const padding = 40; // icon + ports padding
286
+
287
+ const textWidth = nodeName.length * charWidth;
288
+ return Math.max(baseWidth, textWidth + padding);
289
+ }
290
+
291
+ // Example calculations:
292
+ estimateNodeWidth("function 1") // → 160px
293
+ estimateNodeWidth("This is a very long function name") // → ~320px
294
+ ```
295
+
296
+ **Placement strategy for long-name nodes:**
297
+ 1. Calculate estimated width using formula above
298
+ 2. Add extra horizontal spacing: `nextX = currentX + estimatedWidth + 40`
299
+ 3. This ensures cables remain visible and nodes don't overlap
300
+
301
+ ### ️ Important Rules
302
+
303
+ - **Never place two nodes at the same (x, y)** — they visually overlap
304
+ - **Keep connected nodes within ~400px horizontally** for readability
305
+ - **Account for name length** when positioning — long names need more space
306
+ - **Locked flows**: `create-node`, `update-node`, `delete-node`, `connect-nodes`, and `disconnect-nodes` all refuse to modify nodes in a locked flow. Check flow lock status with `get-flows`.
307
+
308
+ ### 🔍 Real-World Example
309
+
310
+ From actual Node-RED deployment analysis:
311
+
312
+ ```
313
+ Flow layout with 8 function nodes:
314
+
315
+ Row 1 (y=80):
316
+ • function 1: x=120, y=80
317
+ • function 2: x=240, y=80 ← Δx = 120px (standard spacing)
318
+
319
+ Row 2 (y=120):
320
+ • function 3: x=120, y=120
321
+ • function 4: x=240, y=120 ← Δx = 120px
322
+
323
+ Row 3 (y=160):
324
+ • function 5: x=120, y=160
325
+ • function 6: x=240, y=160 ← Δx = 120px
326
+
327
+ Row 4 (y=200):
328
+ • Long name node: x=200, y=200
329
+ • function 7: x=400, y=200 ← Δx = 200px (extra space for long name)
330
+ ```
331
+
332
+ **Takeaway:** Standard nodes use 120px spacing, but long-name nodes require 200px+ to prevent overlap.
333
+
334
+ ### 🧪 Multi-Output Node Height Test
335
+
336
+ Empirical test with function nodes (1-5 outputs) placed at y=80 with debug reference nodes below:
337
+
338
+ ```
339
+ Test flow: "Node Dimensions Test"
340
+
341
+ Row 1 (y=80): [1 output] [2 outputs] [3 outputs] [4 outputs] [5 outputs]
342
+ x=120 x=280 x=440 x=600 x=760
343
+
344
+ Row 2 (debug): [Debug 1] [Debug 2] [Debug 3] [Debug 4] [Debug 5]
345
+ y=120 y=120 y=120 y=140 y=140
346
+ ```
347
+
348
+ **Results:**
349
+ | Outputs | Function Y | Debug Y | ΔY (height) | Node Height |
350
+ |---------|-----------|---------|-------------|-------------|
351
+ | 1 | 80 | 120 | 40px | ~40px |
352
+ | 2 | 80 | 120 | 40px | ~40px |
353
+ | 3 | 80 | 120 | 40px | ~40px |
354
+ | 4 | 80 | 140 | 60px | ~60px |
355
+ | 5 | 80 | 140 | 60px | ~60px |
356
+
357
+ **Key findings:**
358
+ - **Width is constant** (~160px) regardless of output count — extra ports stack vertically
359
+ - **Height is stable** (~40px) for 1-3 outputs
360
+ - **Height jumps** to ~60px at 4+ outputs
361
+ - **Horizontal spacing**: 160px between centers works for all output counts
362
+ - **Vertical spacing**: Use +40px for 1-3 outputs, +60px for 4+ outputs
363
+
364
+ **Placement rule for multi-output nodes:**
365
+ ```javascript
366
+ // Use the estimateNodeHeight() formula from the Height section above
367
+ const nextY = currentY + estimateNodeHeight(currentOutputs);
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Workflow B — Import from JSON
373
+
374
+ Use `import-flow` when you have a pre-built flow JSON (from `export-flow` or an example file):
375
+
376
+ ### Step 1: Choose a conflict strategy
377
+ - **`regenerate`** (default, safest): Remaps all IDs to new UUIDs. Always creates a duplicate — safe, no overwrites.
378
+ - **`overwrite`**: Replaces nodes with matching IDs. Use to update an existing flow with new configuration.
379
+
380
+ ### Step 2: Import
381
+ ```
382
+ import-flow(flowJson: "<json>", conflictStrategy: "regenerate")
383
+ ```
384
+
385
+ ### Step 3: (Optional) Target a specific tab
386
+ ```
387
+ import-flow(flowJson: "<json>", targetFlowId: "<existingFlowId>")
388
+ ```
389
+ This injects all non-tab nodes into the target tab. The tab node in the JSON is discarded.
390
+
391
+ **When to prefer import over build-from-scratch:**
392
+ - Duplicating or migrating an existing flow between instances
393
+ - Applying a known-good template from examples
394
+ - Restoring a flow from a backup (exported with `export-flow`)
395
+
396
+ ---
397
+
398
+ ## Workflow C — Edit an Existing Node
399
+
400
+ ### Step 1: Read current state
401
+ ```
402
+ get-node-detail(nodeId: "<nodeId>")
403
+ ```
404
+ Review the returned node object to understand current configuration.
405
+
406
+ ### Step 2: Apply changes
407
+ ```
408
+ update-node(nodeId: "<nodeId>", properties: { name: "New Name", func: "return msg;" })
409
+ ```
410
+ Only include fields you want to change. Omitted fields are preserved.
411
+
412
+ **💡 Adding or updating a description:** Set the `info` property — the **Description** field in the Node-RED editor UI:
413
+ ```
414
+ update-node(nodeId: "<nodeId>", properties: { info: "This node filters messages with temperature > 30°C" })
415
+ ```
416
+
417
+ ### Step 3: Verify
418
+ ```
419
+ get-node-detail(nodeId: "<nodeId>")
420
+ ```
421
+ Confirm the changes took effect.
422
+
423
+ **🛑 Never include `wires`, `id`, or `z` in `properties`.** The tool silently ignores them.
424
+
425
+ ---
426
+
427
+ ## Workflow D — Delete
428
+
429
+ ### Delete a single node
430
+ ```
431
+ delete-node(nodeId: "<nodeId>")
432
+ ```
433
+ Node-RED automatically cleans up dangling wire references on deploy. The `previousState` in the response contains the full node object — save it if you need to undo.
434
+
435
+ ### Delete an entire flow tab
436
+ ```
437
+ delete-flow(flowId: "<flowId>")
438
+ ```
439
+ Returns `previousState` with ALL nodes in the tab. Refuses to delete locked flows.
440
+
441
+ **Undo a deletion:** Use `import-flow` with the `previousState` from the delete response and strategy `"regenerate"`.
442
+
443
+ ---
444
+
445
+ ## 🐛 Build & Debug Step-by-Step (RECOMMENDED)
446
+
447
+ **When building a new flow, validate each node's output before adding the next node.** This catches format errors, type mismatches, and missing properties early — before you've built 10 nodes on top of a broken foundation.
448
+
449
+ ### The Golden Rule
450
+ > After EVERY processing node you add, wire it to a `debug` node, deploy, inject, and verify the output. Only then add the next node.
451
+
452
+ ### Step-by-step pattern
453
+
454
+ ```
455
+ // 1. Create first processing node + debug after it
456
+ create-node(type: "inject", flowId: "<fid>", properties: { name: "Start", payload: "test", payloadType: "str" }, x: 100, y: 100)
457
+ create-node(type: "debug", flowId: "<fid>", properties: { name: "Debug1", complete: "true", targetType: "full" }, x: 300, y: 100)
458
+ connect-nodes(fromNodeId: "<injectId>", toNodeId: "<debug1Id>")
459
+
460
+ // 2. Deploy — changes are NOT live until you do this
461
+ deploy()
462
+
463
+ // 3. Inject and check output format
464
+ inject-message(nodeId: "<injectId>")
465
+ read-debug-messages(last: 1)
466
+
467
+ // 4. If output looks good, remove the debug node (or keep it) and add next node
468
+ delete-node(nodeId: "<debug1Id>")
469
+ create-node(type: "function", flowId: "<fid>", properties: { name: "Process", func: "msg.payload = msg.payload * 2;\nreturn msg;", outputs: 1 }, x: 300, y: 100)
470
+ connect-nodes(fromNodeId: "<injectId>", toNodeId: "<functionId>")
471
+ create-node(type: "debug", flowId: "<fid>", properties: { name: "Debug2", complete: "true", targetType: "full" }, x: 500, y: 100)
472
+ connect-nodes(fromNodeId: "<functionId>", toNodeId: "<debug2Id>")
473
+
474
+ // 5. Deploy again and verify
475
+ deploy()
476
+ inject-message(nodeId: "<injectId>")
477
+ read-debug-messages(last: 1)
478
+
479
+ // 6. Repeat until flow is complete
480
+ ```
481
+
482
+ **Why this matters:**
483
+ - A `function` node might return `undefined` or the wrong type — you catch it immediately
484
+ - A `change` node might not set the right property — you see it in debug output
485
+ - An `http request` might return unexpected JSON structure — you can adjust before building downstream nodes
486
+ - You avoid debugging a 10-node chain where the error is in node #2
487
+
488
+ **⚠️ NEVER FORGET TO DEPLOY.** Every edit is staged. If `inject-message` errors with "undeployed changes", call `deploy` first.
489
+
490
+ ---
491
+
492
+ ## Debug Workflow
493
+
494
+ Follow this numbered sequence whenever a flow doesn't behave as expected:
495
+
496
+ ### 1️⃣ Ensure debug nodes are active
497
+ ```
498
+ get-flow-nodes(flowId: "<flowId>", nodeType: "debug")
499
+ ```
500
+ Check that each debug node has `active: true`. If not:
501
+ ```
502
+ update-node(nodeId: "<debugId>", properties: { active: true })
503
+ ```
504
+
505
+ ### 2️⃣ Record a timestamp
506
+ Note the current time (e.g., `Date.now()`) before triggering. This gives you an `after` filter anchor for `read-debug-messages`.
507
+
508
+ ### 3️⃣ Trigger the flow
509
+ ```
510
+ inject-message(nodeId: "<injectId>")
511
+ ```
512
+ Use `nodeId` (not name) when possible — it's unambiguous.
513
+
514
+ ### 4️⃣ Wait briefly then read
515
+ Node-RED processes messages asynchronously. Wait ~500ms-1s, then:
516
+ ```
517
+ read-debug-messages(nodeName: "<debugNodeName>", after: <timestamp>, limit: 10)
518
+ ```
519
+ The `after` filter ensures you only see messages from this test run.
520
+
521
+ ### 5️⃣ Analyze the output
522
+ Check `payload`, `topic`, and any custom properties. Is the value what you expected? Is the type correct?
523
+
524
+ ### 6️⃣ Fix and repeat
525
+ Make the needed changes (via `update-node`, function `func` edit, or wiring fix), then repeat from step 2.
526
+
527
+ ---
528
+
529
+ ## Debug Node Configuration
530
+
531
+ | Property | Values | Description |
532
+ |----------|--------|-------------|
533
+ | `active` | `true/false` | Enable/disable output. Disabled nodes collect nothing. |
534
+ | `complete` | `"true"` (full msg), `"false"` (payload only), or property path like `"payload.temperature"` | What to display |
535
+ | `targetType` | `"full"` or `"msg"` | Use `"msg"` when `complete` is a property path |
536
+ | `console` | `true/false` | Also log to Node-RED server console |
537
+ | `tosidebar` | `true/false` | Display in debug sidebar (required for `read-debug-messages`) |
538
+
539
+ **Recommended debug node for flow validation:**
540
+ ```
541
+ { active: true, complete: "true", targetType: "full", tosidebar: true, console: false }
542
+ ```
543
+ This captures the entire message — you can inspect all properties during debugging.
544
+
545
+ ---
546
+
547
+ ## inject-message Usage
548
+
549
+ Trigger an inject node to start a message through the flow:
550
+
551
+ ```
552
+ // By nodeId (preferred — always unique)
553
+ inject-message(nodeId: "<injectId>")
554
+
555
+ // By name (convenient, but fails if duplicate names exist)
556
+ inject-message(name: "Trigger")
557
+ ```
558
+
559
+ **Important notes:**
560
+ - The target node MUST be of type `"inject"`. Other node types will error.
561
+ - Injection is asynchronous — the command returns immediately, the message flows through Node-RED in the background.
562
+ - Use `flowId` to scope name-based lookups: `inject-message(name: "Trigger", flowId: "<flowId>")`.
563
+ - If multiple inject nodes share the same name without a `flowId`, the tool returns an error listing the matching IDs — pick one.
564
+
565
+ ---
566
+
567
+ ## read-debug-messages Filters
568
+
569
+ | Filter | Type | Use Case |
570
+ |--------|------|----------|
571
+ | `nodeId` | string | Pinpoint output from a specific debug node |
572
+ | `nodeName` | string | Match by name (case-insensitive substring) |
573
+ | `keyword` | string | Search within stringified message payload |
574
+ | `after` | timestamp (ms) | Only messages after this time (use `Date.now()` before trigger) |
575
+ | `before` | timestamp (ms) | Only messages before this time |
576
+ | `last` | number | Return the N most recent matching messages |
577
+ | `limit` | number | Return the first N matching messages (default 50) |
578
+
579
+ `last` and `limit` are **mutually exclusive** — use one or the other, not both.
580
+
581
+ **Typical debug read:**
582
+ ```
583
+ read-debug-messages(nodeName: "Output", after: 1717526400000, last: 5)
584
+ ```
585
+
586
+ ---
587
+
588
+ ## Common Debug Patterns
589
+
590
+ ### "Is my flow even triggered?"
591
+ ```
592
+ inject-message(nodeId: "<injectId>")
593
+ read-debug-messages(nodeName: "<firstDebugName>", last: 1)
594
+ ```
595
+ If no message appears: check wiring (especially `connect-nodes`), verify the inject node fires, and confirm the debug node is on the same tab.
596
+
597
+ ### "What is the payload at this point?"
598
+ Place a temporary debug node after the suspect node:
599
+ ```
600
+ create-node(type: "debug", flowId: "<flowId>", properties: { name: "Checkpoint", complete: "true", targetType: "full" }, x: <afterNodeX>, y: <sameY>)
601
+ connect-nodes(fromNodeId: "<suspectNodeId>", outputPort: 0, toNodeId: "<checkpointId>")
602
+ ```
603
+ Trigger, read, diagnose, then delete the checkpoint node.
604
+
605
+ ### "Is my context value set?"
606
+ ```
607
+ get-context(scope: "flow", id: "<flowId>", key: "myKey")
608
+ ```
609
+ If `null`: your function node's `context.set()` or `flow.set()` isn't executing. Check that the function node is actually receiving messages.
610
+
611
+ ### "Why is my function node not working?"
612
+ ```
613
+ get-node-detail(nodeId: "<functionId>")
614
+ ```
615
+ Review the `func` property. Common issues: missing `return msg;`, syntax errors, wrong property names.
616
+
617
+ ---
618
+
619
+ ## Verification
620
+
621
+ After building or modifying a flow, always verify:
622
+
623
+ ### 1. Topology check
624
+ ```
625
+ get-flow-diagram(flowId: "<flowId>")
626
+ ```
627
+ Confirm the Mermaid diagram shows correct wiring — every node connected as intended, no dangling branches.
628
+
629
+ ### 2. Inventory check
630
+ ```
631
+ get-flow-nodes(flowId: "<flowId>")
632
+ ```
633
+ Verify node count, types, and names. Look for:
634
+ - Unnamed nodes (`"name": ""`) — give them descriptive names
635
+ - Wrong node types — did you accidentally pick the wrong type?
636
+ - Missing nodes — did all your `create-node` calls succeed?
637
+
638
+ ### 3. Wire check
639
+ Look at the `wires` arrays in `get-flow-nodes` output. Each source node should have its expected targets listed.
640
+
641
+ ### 4. Functional test
642
+ Inject a message and read debug output. Confirm the payload transforms as expected at each step.
643
+
644
+ ---
645
+
646
+ ## Common Mistakes
647
+
648
+ | Mistake | Symptom | Fix |
649
+ |---------|---------|-----|
650
+ | **Not wiring after create** | New node never receives messages | Always call `connect-nodes` after `create-node`. Use the INSERT pattern: disconnect A→B, connect A→new, connect new→B |
651
+ | **Wrong port index** | Message goes to wrong output | Ports are 0-indexed. Switch output 1 = port 0, output 2 = port 1, output 3 = port 2 |
652
+ | **Setting wires in update-node** | Wires silently ignored | Use `connect-nodes`/`disconnect-nodes` exclusively for wiring |
653
+ | **Overlapping coordinates** | Nodes visually stack in editor | Use the grid: first at (100,100), inline +200 X, branch +100 Y |
654
+ | **Forgetting `return msg;`** in function | Message stops at function node | Every function must `return msg;` (or `return [msg1, msg2]` for multi-output). `return null;` explicitly stops the message |
655
+ | **`node.send()` without `return`** | Unpredictable behavior, duplicate messages | After calling `node.send()` in async code, ALWAYS `return;` immediately |
656
+ | **Debug node with `active: false`** | No output visible | Set `active: true` on all debug nodes used for testing |
657
+ | **Reading debug too soon** | `read-debug-messages` returns empty | Wait ~500ms after `inject-message` before reading; Node-RED processes asynchronously |
658
+ | **Deleting without backup** | Cannot undo accidental deletion | Use `export-flow` before destructive operations; `delete-*` responses include `previousState` for recovery |
659
+ | **Modifying locked flow** | Tool returns error | Check `get-flows` for `locked: true`; locked flows must be unlocked in the editor first |