@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.
- package/LICENSE +201 -0
- package/README.md +162 -0
- package/index.js +133 -0
- package/package.json +58 -0
- package/resources/skills/nodered-flow-builder/SKILL.md +659 -0
- package/resources/skills/nodered-flow-layout/SKILL.md +395 -0
- package/resources/skills/nodered-flowfuse-dashboard/SKILL.md +941 -0
- package/resources/skills/nodered-fundamentals/SKILL.md +323 -0
- package/resources/skills/nodered-jsonata/SKILL.md +1039 -0
- package/resources/skills/nodered-mustache/SKILL.md +588 -0
- package/resources/skills/nodered-node-reference/SKILL.md +1020 -0
- package/resources/skills/nodered-node-reference/examples/common.json +113 -0
- package/resources/skills/nodered-node-reference/examples/network.json +107 -0
- package/resources/skills/nodered-node-reference/examples/parser.json +147 -0
- package/resources/skills/nodered-node-reference/examples/sequence.json +141 -0
- package/resources/skills/nodered-node-reference/examples/storage.json +104 -0
- package/resources/skills/nodered-patterns/SKILL.md +414 -0
- package/resources/skills/nodered-patterns/examples/error-handler.json +72 -0
- package/resources/skills/nodered-patterns/examples/http-endpoint.json +42 -0
- package/resources/skills/nodered-patterns/examples/mqtt-subscriber.json +47 -0
- package/resources/skills/nodered-patterns/examples/timer-flow.json +50 -0
- package/resources/skills/nodered-subflows/SKILL.md +261 -0
- package/resources/skills/nodered-uibuilder/SKILL.md +500 -0
- package/src/auth/api-key-verifier.js +36 -0
- package/src/auth/composite-verifier.js +59 -0
- package/src/auth/config.js +106 -0
- package/src/auth/oauth-clients-store.js +107 -0
- package/src/auth/oauth-provider.js +149 -0
- package/src/auth/oauth-token-store.js +312 -0
- package/src/nodered/auth.js +158 -0
- package/src/nodered/client.js +199 -0
- package/src/nodered/comms-client.js +500 -0
- package/src/renderer/colors.js +161 -0
- package/src/renderer/geometry.js +115 -0
- package/src/renderer/html-builder.js +571 -0
- package/src/renderer/index.js +51 -0
- package/src/renderer/ir-builder.js +161 -0
- package/src/renderer/layout.js +126 -0
- package/src/renderer/mermaid-builder.js +109 -0
- package/src/renderer/svg-builder.js +228 -0
- package/src/schemas/responses.js +283 -0
- package/src/server.js +844 -0
- package/src/skills/loader.js +84 -0
- package/src/staging-store.js +258 -0
- package/src/tools/add-nodes-to-group.js +216 -0
- package/src/tools/connect-nodes.js +115 -0
- package/src/tools/constants.js +45 -0
- package/src/tools/create-flow.js +87 -0
- package/src/tools/create-node.js +126 -0
- package/src/tools/create-subflow-instance.js +123 -0
- package/src/tools/create-subflow.js +101 -0
- package/src/tools/delete-context.js +60 -0
- package/src/tools/delete-flow.js +81 -0
- package/src/tools/delete-group.js +116 -0
- package/src/tools/delete-node.js +73 -0
- package/src/tools/delete-subflow.js +103 -0
- package/src/tools/deploy.js +94 -0
- package/src/tools/disconnect-nodes.js +158 -0
- package/src/tools/export-flow.js +161 -0
- package/src/tools/export-subflow.js +78 -0
- package/src/tools/flow-utils.js +376 -0
- package/src/tools/get-config-nodes.js +86 -0
- package/src/tools/get-context.js +76 -0
- package/src/tools/get-flow-diagram.js +99 -0
- package/src/tools/get-flow-nodes.js +116 -0
- package/src/tools/get-flows.js +74 -0
- package/src/tools/get-node-detail.js +77 -0
- package/src/tools/get-node-type-detail.js +92 -0
- package/src/tools/get-palette-nodes.js +63 -0
- package/src/tools/get-staging-status.js +34 -0
- package/src/tools/get-subflow-detail.js +110 -0
- package/src/tools/get-subflows.js +105 -0
- package/src/tools/import-flow.js +310 -0
- package/src/tools/inject-message.js +117 -0
- package/src/tools/install-node.js +31 -0
- package/src/tools/read-debug-messages.js +155 -0
- package/src/tools/refresh-staging.js +62 -0
- package/src/tools/remove-nodes-from-group.js +162 -0
- package/src/tools/render-staging.js +69 -0
- package/src/tools/response-utils.js +42 -0
- package/src/tools/search-nodes.js +134 -0
- package/src/tools/uninstall-node.js +31 -0
- package/src/tools/update-flow.js +95 -0
- package/src/tools/update-group.js +77 -0
- package/src/tools/update-node.js +132 -0
- package/src/tools/update-subflow.js +84 -0
- package/src/transport/http.js +252 -0
- package/src/transport/stdio.js +16 -0
- package/src/transport/ws-server.js +223 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nodered-subflows
|
|
3
|
+
description: >-
|
|
4
|
+
Comprehensive guide for working with Node-RED subflows via MCP tools.
|
|
5
|
+
Covers subflow vocabulary, discovery, creation from scratch, instantiation,
|
|
6
|
+
editing, export/import, deletion, common patterns, and limitations.
|
|
7
|
+
tools:
|
|
8
|
+
- get-subflows
|
|
9
|
+
- get-subflow-detail
|
|
10
|
+
- create-subflow
|
|
11
|
+
- update-subflow
|
|
12
|
+
- delete-subflow
|
|
13
|
+
- create-subflow-instance
|
|
14
|
+
- export-subflow
|
|
15
|
+
- import-flow
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Node-RED Subflows
|
|
19
|
+
|
|
20
|
+
Comprehensive guide for creating, managing, and using Node-RED subflows via MCP tools.
|
|
21
|
+
|
|
22
|
+
> **⚠️ Staging reminder:** All subflow operations (create, update, delete, instantiate) stage changes locally. Call `deploy()` after making changes to push them to the Node-RED runtime. Use `get-staging-status` to check what's pending.
|
|
23
|
+
|
|
24
|
+
> **Prerequisites:** Read `nodered-fundamentals` first for core vocabulary and `nodered-flow-builder` for basic flow construction patterns. See `nodered-flow-layout` for positioning rules when placing subflow instances on the canvas.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Subflow Vocabulary
|
|
29
|
+
|
|
30
|
+
A subflow is a **reusable, encapsulated flow** that can be instantiated multiple times across different flow tabs. It has three layers in the Node-RED JSON:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
34
|
+
│ SUBFLOW MODEL │
|
|
35
|
+
├──────────────────────────────────────────────────────────────────┤
|
|
36
|
+
│ │
|
|
37
|
+
│ 1. DEFINITION (type: "subflow") │
|
|
38
|
+
│ - id, name, info (markdown description) │
|
|
39
|
+
│ - in → input port definitions (where messages enter) │
|
|
40
|
+
│ - out → output port definitions (where messages exit) │
|
|
41
|
+
│ - category, color, icon → palette metadata │
|
|
42
|
+
│ │
|
|
43
|
+
│ 2. INTERNAL NODES (z = subflowId) │
|
|
44
|
+
│ - Regular nodes placed inside the subflow canvas │
|
|
45
|
+
│ - Wired together to form the internal logic │
|
|
46
|
+
│ - Managed with the same tools as flow tab nodes │
|
|
47
|
+
│ │
|
|
48
|
+
│ 3. INSTANCES (type: "subflow:<subflowId>") │
|
|
49
|
+
│ - Placed in flow tabs (z = tabId) │
|
|
50
|
+
│ - Each has its own name, env vars, and wire connections │
|
|
51
|
+
│ - Output wires auto-size to match definition's out ports │
|
|
52
|
+
│ │
|
|
53
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Key properties
|
|
57
|
+
|
|
58
|
+
| Concept | Field | Example |
|
|
59
|
+
|---------|-------|---------|
|
|
60
|
+
| Subflow ID | `id` | `"7b6d939fa36f99ef"` |
|
|
61
|
+
| Instance type | `type` | `"subflow:7b6d939fa36f99ef"` |
|
|
62
|
+
| Internal parent | `z` | `z = "7b6d939fa36f99ef"` |
|
|
63
|
+
| Instance location | `z` | `z = "c9a401de7611c87e"` (tab ID) |
|
|
64
|
+
| Input ports | `in` | Array of `{ x, y, wires: [{ id, port }] }` |
|
|
65
|
+
| Output ports | `out` | Array of `{ x, y, wires: [{ id, port }] }` |
|
|
66
|
+
| Instance env vars | `env` | Array of `{ name, value, type }` |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Workflow A — Discover Available Subflows
|
|
71
|
+
|
|
72
|
+
### Step 1: List all subflows
|
|
73
|
+
```
|
|
74
|
+
get-subflows()
|
|
75
|
+
```
|
|
76
|
+
Returns enriched summary of each subflow: name, description, input/output counts, internal node types, instance count and locations.
|
|
77
|
+
|
|
78
|
+
### Step 2: Inspect a subflow deeply
|
|
79
|
+
```
|
|
80
|
+
get-subflow-detail(subflowId: "<subflowId>")
|
|
81
|
+
```
|
|
82
|
+
Returns the full definition, all internal nodes (with sanitized configs), all instances and their locations, and a Mermaid diagram of the internal flow. **Always call this before instantiating or modifying a subflow** to understand what it does.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Workflow B — Create a Subflow from Scratch
|
|
87
|
+
|
|
88
|
+
Follow these steps in exact order:
|
|
89
|
+
|
|
90
|
+
### Step 1: Create the subflow definition (empty container)
|
|
91
|
+
```
|
|
92
|
+
create-subflow(name: "My Subflow", info: "Processes incoming data and routes by type")
|
|
93
|
+
```
|
|
94
|
+
Save the returned `subflowId` — you'll use it as `flowId` for internal nodes.
|
|
95
|
+
|
|
96
|
+
### Step 2: Populate internal nodes
|
|
97
|
+
```
|
|
98
|
+
create-node(type: "mqtt in", flowId: "<subflowId>", properties: { topic: "test/#", broker: "<brokerId>" }, x: 100, y: 100)
|
|
99
|
+
create-node(type: "switch", flowId: "<subflowId>", properties: { property: "topic", outputs: 3 }, x: 300, y: 100)
|
|
100
|
+
create-node(type: "debug", flowId: "<subflowId>", properties: { complete: "payload" }, x: 500, y: 100)
|
|
101
|
+
```
|
|
102
|
+
Use `<subflowId>` as `flowId` — the same `create-node` tool works for subflow internals.
|
|
103
|
+
|
|
104
|
+
### Step 3: Wire internal nodes
|
|
105
|
+
```
|
|
106
|
+
connect-nodes(fromNodeId: "<mqttId>", outputPort: 0, toNodeId: "<switchId>")
|
|
107
|
+
connect-nodes(fromNodeId: "<switchId>", outputPort: 0, toNodeId: "<debugId>")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Step 4: Define input/output ports
|
|
111
|
+
```
|
|
112
|
+
update-subflow(subflowId: "<subflowId>", updates: {
|
|
113
|
+
out: [
|
|
114
|
+
{ x: 600, y: 100, wires: [{ id: "<switchId>", port: 0 }] },
|
|
115
|
+
{ x: 600, y: 180, wires: [{ id: "<switchId>", port: 1 }] },
|
|
116
|
+
{ x: 600, y: 260, wires: [{ id: "<switchId>", port: 2 }] }
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
**Important:** Port wires must reference internal node IDs. The `x`/`y` values are the port position on the subflow's input/output bar in the UI.
|
|
121
|
+
|
|
122
|
+
### Step 5: Verify
|
|
123
|
+
```
|
|
124
|
+
get-subflow-detail(subflowId: "<subflowId>")
|
|
125
|
+
```
|
|
126
|
+
Check the Mermaid diagram shows correct internal wiring.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Workflow C — Instantiate a Subflow in a Flow Tab
|
|
131
|
+
|
|
132
|
+
### Basic instantiation
|
|
133
|
+
```
|
|
134
|
+
create-subflow-instance(
|
|
135
|
+
subflowId: "<subflowId>",
|
|
136
|
+
flowId: "<tabId>"
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
The tool auto-sizes output wires to match the subflow's output port count. Returns `nodeId`.
|
|
140
|
+
|
|
141
|
+
### With environment variables
|
|
142
|
+
```
|
|
143
|
+
create-subflow-instance(
|
|
144
|
+
subflowId: "<subflowId>",
|
|
145
|
+
flowId: "<tabId>",
|
|
146
|
+
name: "Sensor Processor",
|
|
147
|
+
env: [
|
|
148
|
+
{ name: "THRESHOLD", value: "42", type: "num" },
|
|
149
|
+
{ name: "API_URL", value: "https://example.com", type: "str" }
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
Environment variables are passed to internal nodes. Inside a function node, access them with `env.get("THRESHOLD")`.
|
|
154
|
+
|
|
155
|
+
### Wire the instance outputs
|
|
156
|
+
```
|
|
157
|
+
connect-nodes(fromNodeId: "<instanceId>", outputPort: 0, toNodeId: "<target1Id>")
|
|
158
|
+
connect-nodes(fromNodeId: "<instanceId>", outputPort: 1, toNodeId: "<target2Id>")
|
|
159
|
+
```
|
|
160
|
+
Each output port corresponds to one entry in the subflow's `out` array (0-indexed).
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Workflow D — Editing Subflows
|
|
165
|
+
|
|
166
|
+
### Editing metadata or ports
|
|
167
|
+
```
|
|
168
|
+
update-subflow(subflowId: "<subflowId>", updates: {
|
|
169
|
+
name: "New Name",
|
|
170
|
+
info: "Updated description",
|
|
171
|
+
out: [ ... new port definitions ... ]
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
Only allowed fields are updated: `name`, `info`, `category`, `color`, `icon`, `in`, `out`. All other fields are preserved.
|
|
175
|
+
|
|
176
|
+
### Editing internal nodes
|
|
177
|
+
Use the standard tools with `flowId` set to the subflow ID:
|
|
178
|
+
```
|
|
179
|
+
update-node(nodeId: "<internalNodeId>", properties: { ... })
|
|
180
|
+
connect-nodes(fromNodeId: "<a>", toNodeId: "<b>")
|
|
181
|
+
disconnect-nodes(fromNodeId: "<a>", toNodeId: "<b>")
|
|
182
|
+
create-node(type: "function", flowId: "<subflowId>", ...)
|
|
183
|
+
delete-node(nodeId: "<internalNodeId>")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Editing an instance
|
|
187
|
+
Instances are just nodes — use standard tools:
|
|
188
|
+
```
|
|
189
|
+
update-node(nodeId: "<instanceId>", properties: { name: "New Name", env: [...] })
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Workflow E — Exporting and Importing
|
|
195
|
+
|
|
196
|
+
### Export a subflow
|
|
197
|
+
```
|
|
198
|
+
export-subflow(subflowId: "<subflowId>")
|
|
199
|
+
```
|
|
200
|
+
Returns a JSON string containing the subflow definition, all internal nodes, and any referenced config nodes (e.g., mqtt-broker). Instances are NOT included — they belong to their parent flow tabs.
|
|
201
|
+
|
|
202
|
+
### Import (duplicate or restore)
|
|
203
|
+
```
|
|
204
|
+
import-flow(flowJson: "<exportedJson>", conflictStrategy: "regenerate")
|
|
205
|
+
```
|
|
206
|
+
Use `"regenerate"` to create a safe duplicate with new IDs. Use `"overwrite"` to replace an existing subflow by ID.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Workflow F — Deleting a Subflow
|
|
211
|
+
|
|
212
|
+
### Cascade delete (default, safest)
|
|
213
|
+
```
|
|
214
|
+
delete-subflow(subflowId: "<subflowId>")
|
|
215
|
+
```
|
|
216
|
+
Removes: the subflow definition + all internal nodes + all instances. The `previousState` response enables undo via `import-flow`.
|
|
217
|
+
|
|
218
|
+
### Delete definition only, keep instances
|
|
219
|
+
```
|
|
220
|
+
delete-subflow(subflowId: "<subflowId>", deleteInstances: false)
|
|
221
|
+
```
|
|
222
|
+
⚠️ **Dangerous**: instances become orphan nodes of type `subflow:<deletedId>`. They will appear as "unknown" in the Node-RED UI. Only use this when migrating instances to a new subflow.
|
|
223
|
+
|
|
224
|
+
### Recover from deletion
|
|
225
|
+
```
|
|
226
|
+
import-flow(flowJson: JSON.stringify(previousState.definition, ...previousState.internalNodes, ...previousState.instances), conflictStrategy: "regenerate")
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Common Patterns
|
|
232
|
+
|
|
233
|
+
### Parametrized subflow pattern
|
|
234
|
+
Create a subflow with env variables for configuration:
|
|
235
|
+
1. Define the subflow with a function node reading `env.get("PARAM")`
|
|
236
|
+
2. Instantiate with different `env` values per instance
|
|
237
|
+
3. Each instance behaves differently based on its env vars
|
|
238
|
+
|
|
239
|
+
### Subflow as data pipeline
|
|
240
|
+
```
|
|
241
|
+
[tab: Main Flow]
|
|
242
|
+
[mqtt in] → [subflow instance: Transform] → [debug]
|
|
243
|
+
│
|
|
244
|
+
[tab: Analytics] │
|
|
245
|
+
[http in] → [subflow instance: Transform] → [http response]
|
|
246
|
+
```
|
|
247
|
+
Same subflow, different contexts.
|
|
248
|
+
|
|
249
|
+
### When to use subflows vs link nodes vs tabs
|
|
250
|
+
- **Subflow**: Reusable logic with configurable parameters (env vars)
|
|
251
|
+
- **Link nodes**: Cross-tab message passing without duplication
|
|
252
|
+
- **Separate tabs**: Organizational separation, not functional reuse
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Limitations
|
|
257
|
+
|
|
258
|
+
- **No nested subflows**: Node-RED's UI does not support placing a subflow instance inside another subflow. While technically possible in JSON, the UI will not render it correctly.
|
|
259
|
+
- **UI refresh required**: Subflow changes made via MCP tools may not be immediately visible in the Node-RED editor. The user may need to refresh the browser.
|
|
260
|
+
- **Port wiring**: Input/output port `wires` in the subflow definition must reference internal node IDs. Invalid references will cause a deploy error.
|
|
261
|
+
- **Config node sharing**: Config nodes (mqtt-broker, etc.) referenced by subflow internal nodes are included in `export-subflow` but shared across instances at runtime.
|