npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mysterysd/node-red-mcp

v2.0.4

Published

MCP server for Node-RED flow management via the Admin API

Readme

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

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: [...] }

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]

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]

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