Skip to content

SilentDemonSD/node-red-ai-mcp

Repository files navigation

node-red-mcp

npm version Node.js TypeScript License PRs Welcome

A production-ready MCP server that connects AI assistants (Claude, Copilot, etc.) to the Node-RED Admin API.

Inspect flows, manage nodes, analyze graph topology, capture debug output, apply JSON patches, and rollback changes — all through natural language.


Architecture

graph TB
    subgraph "AI Client"
        A[Claude Desktop]
        B[VS Code MCP]
        C[Custom Client]
    end

    subgraph "MCP Server @mysterysd/node-red-mcp"
        direction LR
        T[Transport Layer<br/>stdio / SSE / streamableHttp]
        S[Server Factory<br/>McpServer]
        T --> S
        S --> Tools[38 Tools<br/>flows / nodes / graph / auth / runtime]
        S --> Res[7 Resources<br/>settings / flows / graph / registry]
        S --> Prompts[3 Prompts<br/>analyze / repair / refactor]
        Tools --> GE[Graph Engine<br/>DAG analysis / cycles / search]
        Tools --> NR[Node Registry<br/>persistent name-to-ID lookup]
        Tools --> Snap[Snapshots<br/>20-deep ring buffer per flow]
        Tools --> Debug[Debug Capture<br/>WebSocket /comms]
    end

    subgraph "Node-RED Instance"
        NR_API[Admin REST API<br/>:1880]
        NR_WS[WebSocket<br/>:1880/comms]
    end

    A & B & C -->|MCP Protocol| T
    S -->|axios| NR_API
    Debug -->|ws| NR_WS
Loading

Data Flow

sequenceDiagram
    participant AI as AI Assistant
    participant MCP as node-red-mcp
    participant NR as Node-RED

    AI->>MCP: flows-create(label, nodes)
    MCP->>MCP: auto-generate node IDs
    MCP->>MCP: auto-layout positions
    MCP->>NR: POST /flow
    NR-->>MCP: { id, rev }
    MCP->>MCP: refresh NodeRegistry
    MCP-->>AI: { status, flowId }

    AI->>MCP: flows-add-node(flowId, node)
    MCP->>NR: GET /flow/:id
    MCP->>MCP: snapshot current state
    MCP->>MCP: append node, validate wires
    MCP->>NR: PUT /flow/:id
    MCP->>MCP: refresh NodeRegistry
    MCP-->>AI: { status, nodeId }

    AI->>MCP: nodes-resolve(query)
    MCP->>MCP: lookup in NodeRegistry
    MCP-->>AI: { matches: [...] }
Loading

Features

  • 🚀 3 Transport Modes — stdio (default), SSE, streamable HTTP
  • 🔌 38 MCP Tools — full coverage of flows, nodes, runtime, auth, inject, graph analysis, and debug capture
  • 🧩 5 Node-Level Mutation Tools — add, remove, update, rewire, and move individual nodes without rewriting entire flows
  • 📊 Graph Engine — auto-builds directed acyclic graph (DAG) from flow topology, detects cycles, sources, sinks, and computes node categories
  • 🗃️ Node Registry — persistent, auto-synced hot DB for looking up node IDs by name, type, label, and flow
  • 🔍 Semantic Search — query flows by node name, type, topic, URL, or any metadata
  • 🐛 Live Debug Capture — WebSocket-based capture of Node-RED debug output, combined with inject or standalone
  • 🔧 JSON Patch — RFC 6902 compliant patch engine for incremental flow edits
  • 📸 Snapshots — in-memory 20-entry ring buffer per flow for rollback
  • 🔗 7 Resources + 3 Prompts — inspect runtime settings, diagnostics, flows, graph, registry, and get AI-assisted analysis

Table of Contents


Installation

npm install -g @mysterysd/node-red-mcp

Or run directly without installing:

npx @mysterysd/node-red-mcp

Prerequisites

  • Node.js >= 18
  • Node-RED instance running with the Admin API enabled (default: http://localhost:1880)

Quick Start

# 1. Set your Node-RED URL (default: http://localhost:1880)
export NODE_RED_URL=http://my-nodered:1880

# 2. Start the MCP server in stdio mode
npx @mysterysd/node-red-mcp

Then configure your MCP client:

Tip

The server auto-refreshes the Node Registry on startup, so all your nodes are immediately searchable by name.

Claude Desktop / VS Code

{
  "mcpServers": {
    "node-red": {
      "command": "npx",
      "args": ["@mysterysd/node-red-mcp"],
      "env": {
        "NODE_RED_URL": "http://localhost:1880",
        "NODE_RED_ACCESS_TOKEN": "your-token"
      }
    }
  }
}

opencode

{
  "mcpServers": {
    "node-red": {
      "command": "node",
      "args": ["path/to/dist/index.js", "stdio"],
      "env": {
        "NODE_RED_URL": "http://localhost:1880"
      }
    }
  }
}

Configuration

All configuration is via environment variables:

Variable Default Description
NODE_RED_URL http://localhost:1880 Base URL of the Node-RED Admin API
NODE_RED_TOKEN Bearer token for API auth (takes precedence over NODE_RED_ACCESS_TOKEN)
NODE_RED_ACCESS_TOKEN Alternative name for the bearer token
NODE_RED_USERNAME Username for password-based auth
NODE_RED_PASSWORD Password for password-based auth
MCP_SERVER_PORT 3001 (SSE), 3002 (streamable HTTP) HTTP server port for HTTP transports
PORT 3001 (SSE), 3002 (streamable HTTP) Fallback port (overridden by MCP_SERVER_PORT)
NODE_REGISTRY_PATH ./node-registry.json File path for persistent Node Registry

Note

Copy .env.example to .env as a reference for all available environment variables.

Authentication

The server supports two auth methods:

  1. Token-based (recommended):

    export NODE_RED_TOKEN="your-bearer-token"
  2. Password-based (auto-login on first request):

    export NODE_RED_USERNAME="admin"
    export NODE_RED_PASSWORD="your-password"

If neither is set, the server will attempt unauthenticated requests (Node-RED's default for local-only deployments).

Warning

Avoid using NODE_RED_TOKEN in shared or version-controlled config files. Use environment-specific secrets whenever possible.

Usage

# stdio mode (default — for Claude Desktop, VS Code, etc.)
node-red-mcp
node-red-mcp stdio

# SSE mode (HTTP server on :3001)
node-red-mcp sse

# Streamable HTTP mode (HTTP server on :3002)
node-red-mcp streamableHttp

Transport Modes

Mode Protocol Best For
stdio stdin/stdout JSON-RPC Claude Desktop, VS Code MCP extensions
sse Server-Sent Events Remote or containerized setups
streamableHttp HTTP POST + DELETE Stateless proxies, load-balanced deployments

Debug Capture

Two tools provide real-time debug output capture via Node-RED WebSocket (/comms):

Tool Description
node-red-inject (with waitForDebug) Inject + capture — fires inject, listens, returns both result and debug messages in one call
node-red-debug-listen Standalone listener — connects, subscribes to debug, captures messages for N seconds with optional node/flow filtering

Example (one-call inject + verify):

// node-red-inject({ nodeId: "my-inject", waitForDebug: 5 })
→ { status: "injected", debug: [...], debugCount: 3 }

Both tools handle Node-RED's array-batched WebSocket format automatically.


Tools Reference

All 38 tools are registered with the MCP server. Each returns JSON output.

Auth

Tool Description
node-red-auth-get-scheme Inspect the active Node-RED admin auth scheme
node-red-auth-login Exchange credentials for a bearer token
node-red-auth-revoke Revoke an existing bearer token

Runtime

Tool Description
node-red-runtime-get-settings Read runtime settings
node-red-runtime-get-diagnostics Read runtime diagnostics
node-red-runtime-get-flow-state Read runtime flow state
node-red-runtime-set-flow-state Update runtime flow state
node-red-debug-listen Capture debug messages via WebSocket for a duration (optionally filtered by node/flow)

Flows

Tool Description
node-red-flows-list List active flow tabs and metadata
node-red-flows-get Get a single flow by id or label
node-red-flows-create Create a new flow tab with nodes (auto-generates IDs, auto-layouts positions, remaps wires)
node-red-flows-update Replace an existing flow tab (auto-layouts positions)
node-red-flows-patch Apply JSON Patch (RFC 6902) operations to a flow
node-red-flows-delete Delete a flow tab
node-red-flows-clone Clone an existing flow tab
node-red-flows-rollback Rollback a flow to a previous snapshot
node-red-inject Trigger an inject node by its ID (optionally waitForDebug to capture debug output in one call)

Node-Level Mutations

Tool Description
node-red-flows-add-node Add a single node to an existing flow tab. Auto-generates ID if omitted, validates wire targets, positions intelligently.
node-red-flows-remove-node Remove a single node from a flow tab. Cleans up wire references from all remaining nodes.
node-red-flows-update-node Update specific properties of a single node by ID. Deep-merges the provided properties onto the existing node. Reports which keys changed.
node-red-flows-rewire-node Replace all wire connections for a specific node. Validates all target IDs exist in the flow.
node-red-flows-move-node Move a single node to a specific visual position (x, y) within a flow tab.

Graph

Tool Description
node-red-graph-analyze Analyze topology, dependencies, and graph health
node-red-graph-summary Summary statistics and risk assessment
node-red-graph-visualize Generate a human-readable graph view
node-red-graph-dependencies Resolve upstream and downstream dependencies for a node
node-red-graph-query Search nodes by semantic query
node-red-graph-pack Context pack for semantic search with neighbor expansion
node-red-graph-export Export the full flow graph in serializable format

Nodes

Tool Description
node-red-nodes-list List installed node modules and node sets
node-red-nodes-install Install a node module from npm
node-red-nodes-get-module Inspect a specific node module
node-red-nodes-toggle-module Enable or disable a node module
node-red-nodes-remove-module Remove a node module
node-red-nodes-get-set Inspect a specific node set within a module
node-red-nodes-toggle-set Enable or disable a node set
node-red-nodes-resolve Look up node IDs by name, type, description, or flow label using the Node Registry

Resources

URI Description
node-red://runtime/settings Current runtime settings (JSON)
node-red://runtime/diagnostics Runtime diagnostics (JSON)
node-red://flows All active flows (raw JSON)
node-red://nodes All installed nodes (JSON)
node-red://graph Full graph snapshot (serializable format)
node-red://flow/{id} Single flow by ID (template)
node-red://registry Node Registry snapshot — all indexed nodes with names, types, flow labels, and categories

Prompts

Prompt Description
analyze-flow Analyze a flow for risks, dependencies, and graph structure
repair-flow Draft a repair plan for invalid or broken flow wiring
refactor-flow Suggest a graph-aware refactor plan for a flow

Node Registry

The Node Registry is a persistent, auto-synced hot database that indexes every node across all flows.

graph LR
    subgraph "Node Registry"
        NR[(registry.json)]
        L[lookupInRegistry]
        GF[getRegistryForFlow]
        GS[getRegistrySnapshot]
    end

    MC[flows-create] -->|refresh| NR
    MU[5 Mutation Tools] -->|refresh| NR
    FU[flows-update] -->|refresh| NR
    ST[Server Start] -->|initial load| NR
    NR -->|query| RS[nodes-resolve tool]
    NR -->|expose| RES[node-red://registry resource]
Loading

How it works

  1. Startup: refreshRegistry(client) fetches all flows via Admin API and builds an index
  2. Persistence: Index saved to NODE_REGISTRY_PATH (default: ./node-registry.json) as JSON
  3. Auto-sync: Every mutation tool (add, remove, update, rewire, move) and flows-create/update triggers a refresh
  4. Lookup: Search by name, type, ID, or flow label with fuzzy matching and result ranking
  5. Categories: Each node is categorized as source, debug, transform, network, messaging, storage, template, dashboard, config, or other

Example query:

// nodes-resolve({ query: "Temperature", flowLabel: "Factory Floor" })
→ {
  "matches": [
    { "id": "abc123", "type": "inject", "name": "Temperature Sensor",
      "flowId": "...", "flowLabel": "Factory Floor", "category": "source" }
  ]
}

Client Library

The client library (src/client/index.ts) provides a standalone NodeRedClient class you can use programmatically:

import { NodeRedClient } from "@mysterysd/node-red-mcp/client";

const client = new NodeRedClient({
  baseUrl: "http://localhost:1880",
  accessToken: process.env.NODE_RED_ACCESS_TOKEN,
});

// List all flows
const flows = await client.getFlows();

// Get a specific flow
const flow = await client.getFlow("flow-id");

// Install a node
await client.installNode({ module: "node-red-contrib-something" });

API

class NodeRedClient {
  baseUrl: string;                         // Public getter (for WebSocket URL construction)
  constructor(options: ClientOptions);

  // Auth
  getAuthScheme(): Promise<AuthScheme>;
  login(credentials?: AuthCredentials): Promise<TokenResponse>;
  revoke(token?: string): Promise<unknown>;

  // Runtime
  getSettings(): Promise<Record<string, unknown>>;
  getDiagnostics(): Promise<Record<string, unknown>>;
  getFlowState(): Promise<unknown>;
  setFlowState(state: unknown): Promise<unknown>;

  // Flows
  getFlows(): Promise<FlowsResponse>;
  getFlow(id: string): Promise<FlowDocument>;
  createFlow(flow: FlowDocument): Promise<FlowDocument>;
  updateFlow(id: string, flow: FlowDocument): Promise<FlowDocument>;
  deleteFlow(id: string): Promise<unknown>;

  // Nodes
  listNodes(): Promise<unknown>;
  installNode(payload: Record<string, unknown>): Promise<unknown>;
  getNodeModule(module: string): Promise<unknown>;
  toggleNodeModule(module: string, enabled: boolean): Promise<unknown>;
  removeNodeModule(module: string): Promise<unknown>;
  getNodeSet(module: string, set: string): Promise<unknown>;
  toggleNodeSet(module: string, set: string, enabled: boolean): Promise<unknown>;

  // Inject
  inject(nodeId: string): Promise<unknown>;
}

Graph Engine

The graph engine (src/graph/) is a standalone library for building and analyzing Node-RED flow topologies:

import { buildGraph, formatGraph } from "@mysterysd/node-red-mcp/graph/engine";
import { queryGraph } from "@mysterysd/node-red-mcp/graph/search";
import { applyPatch } from "@mysterysd/node-red-mcp/graph/patch";
graph TD
    RAW[Raw Flows<br/>from Admin API] --> BG[buildGraph]
    BG --> FG[FlowGraph]
    FG --> VA[graph-analyze]
    FG --> VS[graph-visualize]
    FG --> DP[graph-dependencies]
    FG --> QY[queryGraph<br/>semantic search]
    FG --> PK[graph-pack<br/>context pack]
    FG --> SM[summarizeGraph]
    FG --> EX[graph-export]
Loading

Graph Types

interface FlowGraph {
  rev: string;
  tabs: FlowTab[];
  nodes: FlowNode[];
  nodeById: Map<string, FlowNode>;
  adjacency: Map<string, string[]>;            // forward edges
  reverseAdjacency: Map<string, Set<string>>;  // backward edges
  edges: GraphEdge[];
  sources: string[];       // nodes with 0 in-degree
  sinks: string[];         // nodes with 0 out-degree
  cycles: string[][];      // detected cycles
  categories: Map<string, NodeCategory>;
}

Functions

Function Description
buildGraph(flowsResponse) Build a FlowGraph from raw Node-RED API response
categorizeNode(node) Classify node as source, debug, transform, subflow, config, dashboard, or other
isConfigNode(node) Check if a node is a config node (no position, no wires)
collectClosure(map, start) BFS/DFS from a start node to collect all reachable nodes
graphToSerializable(graph) Convert graph to plain JSON-safe format
formatGraph(graph, focusId?) Produce a human-readable topology view
findCycles(adjacency) Detect all cycles in a directed graph
buildSemanticIndex(graph) Build a searchable text index over all nodes
queryGraph(graph, query, flowId?) Search nodes by semantic query (returns scored results)
collectRelevantSubgraph(graph, index, query, opts) Find matching nodes + expand to neighbors
summarizeGraph(graph) Generate a summary with counts, categories, risky nodes
applyPatch(document, operations) Apply RFC 6902 JSON Patch operations

Development

# Clone and install
git clone https://github.com/mysterysd/node-red-mcp.git
cd node-red-mcp
npm install

# Build
npm run build

# Watch mode
npm run watch

# Test (with coverage)
npm test

# Lint
npm run prettier:check
npm run prettier:fix

Project Structure

src/
├── config.ts          # Centralized env var config
├── client/            # NodeRedClient — Admin API wrapper
├── graph/             # Graph engine (types, engine, search, patch, registry)
│   └── registry.ts    # NodeRegistry — persistent name-to-ID hot DB
├── tools/
│   ├── auth/          # 3 tools: get-scheme, login, revoke
│   ├── runtime/       # 5 tools: settings, diagnostics, flow-state, debug-listen
│   ├── flows/         # 14 tools: list, get, create, update, patch, delete, clone,
│   │                  #   rollback, inject, add-node, remove-node, update-node,
│   │                  #   rewire-node, move-node
│   │   └── mutation-utils.ts  # Shared mutateFlow() utility
│   ├── graph/         # 7 tools: analyze, summary, visualize, dependencies,
│   │                  #   query, pack, export
│   └── nodes/         # 8 tools: list, install, get-module, toggle-module,
│                       #   remove-module, get-set, toggle-set, resolve
├── resources/         # 7 MCP resource handlers (incl. registry)
├── prompts/           # 3 MCP prompt templates
├── server/            # McpServer factory
├── transports/        # stdio, SSE, streamableHttp
├── __tests__/         # 171 unit tests (17 files)
└── index.ts           # CLI entry point

Test Stats

  • 171 unit tests across 17 test files
  • ~77% overall coverage (core modules at 100%)
  • 25 integration tests against live Node-RED
  • Coverage includes: all 5 mutation tools (94–100%), registry (90%), resolve (100%)

Docker

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev
EXPOSE 3002
ENV NODE_RED_URL=http://nodered:1880
CMD ["node", "dist/index.js", "streamableHttp"]

Build and run:

docker build -t node-red-mcp .
docker run -e NODE_RED_URL=http://host.docker.internal:1880 -p 3002:3002 node-red-mcp

License

MIT © Mystery SD

About

Create node-RED Flows, Logic entirely via AI Agents, Simple Prompt to modular flows in matter of seconds ! with Graphical Context for Bigger Flows !

Topics

Resources

License

Stars

Watchers

Forks

Contributors