263 lines
8.6 KiB
Markdown
263 lines
8.6 KiB
Markdown
# nodered-mcp
|
|
|
|
A Python MCP server for Node-RED flow management, built on [FastMCP](https://github.com/jlowin/fastmcp) and [Pydantic](https://docs.pydantic.dev/).
|
|
|
|
Gives language models full read/write access to a Node-RED instance through the [Admin HTTP API](https://nodered.org/docs/api/admin/), with structured Pydantic responses, incremental flow patching, layout linting, and flexible authentication (token, basic, OAuth2).
|
|
|
|
## Compared to node-red-mcp-server
|
|
|
|
This project was inspired by [karavaev-evgeniy/node-red-mcp-server](https://github.com/karavaev-evgeniy/node-red-mcp-server) (TypeScript/npm). Key differences:
|
|
|
|
| | node-red-mcp-server (TS) | nodered-mcp (Python) |
|
|
|---|---|---|
|
|
| Runtime | Node.js / npm | Python 3.11+ / uv |
|
|
| Framework | Custom MCP SDK | FastMCP |
|
|
| Responses | Raw JSON strings | Typed Pydantic models |
|
|
| Auth | Token only | Token, Basic, OAuth2 |
|
|
| Deployment types | `full` only | `full`, `nodes`, `flows`, `reload` |
|
|
| Flow mutations | Replace entire flow | `patch_flow` with granular ops (add/remove/update/rewire) |
|
|
| Layout checks | None | `lint_flow` detects overlaps, spacing, wire crossings |
|
|
| Auto-lint | N/A | `update_flow` and `patch_flow` append warnings automatically |
|
|
| Tool count | 19 | 22 |
|
|
|
|
## Installation
|
|
|
|
Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/):
|
|
|
|
```bash
|
|
git clone <repo-url> && cd nodered-mcp
|
|
uv sync
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Minimal — token auth (default)
|
|
export NODE_RED_URL=http://localhost:1880
|
|
export NODE_RED_TOKEN=your-api-token
|
|
uv run nodered-mcp
|
|
```
|
|
|
|
## Configuration
|
|
|
|
All configuration is via environment variables with the `NODE_RED_` prefix.
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `NODE_RED_URL` | `http://localhost:1880` | Node-RED instance URL |
|
|
| `NODE_RED_TOKEN` | `""` | Bearer token (for `token` auth method) |
|
|
| `NODE_RED_API_VERSION` | `v1` | API version header |
|
|
| `NODE_RED_AUTH_METHOD` | `token` | Auth method: `token`, `basic`, or `oauth` |
|
|
| `NODE_RED_AUTH_USERNAME` | `""` | Username for `basic` or `oauth` auth |
|
|
| `NODE_RED_AUTH_PASSWORD` | `""` | Password/app password for `basic` or `oauth` auth |
|
|
| `NODE_RED_OAUTH_TOKEN_URL` | `""` | OAuth2 token endpoint URL |
|
|
| `NODE_RED_OAUTH_CLIENT_ID` | `""` | OAuth2 client ID |
|
|
|
|
## Authentication
|
|
|
|
### Token (default)
|
|
|
|
Static bearer token sent with every request. This is the simplest method and matches how `node-red-mcp-server` works:
|
|
|
|
```bash
|
|
NODE_RED_URL=http://localhost:1880
|
|
NODE_RED_TOKEN=your-api-token
|
|
```
|
|
|
|
### Basic Auth
|
|
|
|
HTTP Basic authentication, useful when Node-RED sits behind a forward auth proxy.
|
|
|
|
```bash
|
|
NODE_RED_AUTH_METHOD=basic
|
|
NODE_RED_AUTH_USERNAME=admin
|
|
NODE_RED_AUTH_PASSWORD=your-password
|
|
```
|
|
|
|
### Basic Auth with Authentik
|
|
|
|
If your Node-RED instance sits behind [Authentik](https://goauthentik.io/) as a forward auth proxy, the basic auth method works well. Authentik intercepts requests, validates credentials, and sets session cookies before forwarding to Node-RED.
|
|
|
|
1. **Create an Authentik application** for your Node-RED instance using the Forward Auth (single application) provider.
|
|
|
|
2. **Configure your reverse proxy** (Traefik, nginx, Caddy) to use Authentik's forward auth endpoint. For example, with Traefik:
|
|
|
|
```yaml
|
|
# docker-compose.yml (Traefik labels on the Node-RED service)
|
|
labels:
|
|
- "traefik.http.routers.nodered.middlewares=authentik@docker"
|
|
```
|
|
|
|
3. **Create an Authentik service account** (or use an existing user) and generate an app password under the user's token settings. This avoids MFA prompts that would block API access.
|
|
|
|
4. **Configure nodered-mcp**:
|
|
|
|
```bash
|
|
NODE_RED_URL=https://nodered.yourdomain.com
|
|
NODE_RED_AUTH_METHOD=basic
|
|
NODE_RED_AUTH_USERNAME=service-account
|
|
NODE_RED_AUTH_PASSWORD=the-app-password-from-authentik
|
|
```
|
|
|
|
The basic auth credentials are sent to Authentik's proxy, which validates them and sets cookies. Subsequent requests use the session cookie, so Node-RED itself doesn't need auth enabled.
|
|
|
|
### OAuth2 Client Credentials
|
|
|
|
Fetches a short-lived JWT via OAuth2 client credentials grant. The token is cached and auto-refreshed with a 30-second expiry buffer. On a 401 response, the server retries once with a fresh token.
|
|
|
|
Works with Authentik out of the box — every application has a token endpoint and `client_id`:
|
|
|
|
```bash
|
|
NODE_RED_AUTH_METHOD=oauth
|
|
NODE_RED_AUTH_USERNAME=service-account
|
|
NODE_RED_AUTH_PASSWORD=service-password
|
|
NODE_RED_OAUTH_TOKEN_URL=https://auth.yourdomain.com/application/o/token/
|
|
NODE_RED_OAUTH_CLIENT_ID=your-client-id
|
|
```
|
|
|
|
## MCP Client Configuration
|
|
|
|
### Claude Desktop
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"nodered": {
|
|
"command": "uv",
|
|
"args": ["--directory", "/path/to/nodered-mcp", "run", "nodered-mcp"],
|
|
"env": {
|
|
"NODE_RED_URL": "http://localhost:1880",
|
|
"NODE_RED_TOKEN": "your-token"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Claude Code
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"nodered": {
|
|
"command": "uv",
|
|
"args": ["--directory", "/path/to/nodered-mcp", "run", "nodered-mcp"],
|
|
"env": {
|
|
"NODE_RED_URL": "http://localhost:1880",
|
|
"NODE_RED_TOKEN": "your-token"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Tools
|
|
|
|
### Flow Tools (12)
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `get_flows` | Get all flows with summary statistics |
|
|
| `update_flows` | Replace all flows with deployment type control (`full`/`nodes`/`flows`/`reload`) |
|
|
| `get_flow` | Get a single flow by ID |
|
|
| `update_flow` | Update a single flow (auto-lints after save) |
|
|
| `list_tabs` | List all flow tabs (workspaces) |
|
|
| `create_flow` | Create a new flow tab |
|
|
| `delete_flow` | Delete a flow tab |
|
|
| `get_flows_state` | Get runtime state (started/stopped) |
|
|
| `set_flows_state` | Start or stop the flow runtime |
|
|
| `get_flows_formatted` | Get flows grouped by tabs/nodes/subflows with statistics |
|
|
| `visualize_flows` | Markdown-formatted per-tab structural overview |
|
|
| `patch_flow` | Incremental operations: `add_nodes`, `remove_nodes`, `update_node`, `rewire`, `set_label`, `set_info`, `set_disabled` (auto-lints after save) |
|
|
|
|
### Node Tools (6)
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `inject` | Trigger an inject node |
|
|
| `get_nodes` | List installed node modules |
|
|
| `get_node_info` | Detailed info about a node module |
|
|
| `toggle_node_module` | Enable or disable a node module |
|
|
| `find_nodes_by_type` | Find all nodes of a given type |
|
|
| `search_nodes` | Search nodes by name or any property |
|
|
|
|
### Layout Tools (1)
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `lint_flow` | Check a flow for node overlaps, insufficient spacing, and wire-through-node crossings. Optionally scoped to specific node IDs. |
|
|
|
|
### Settings Tools (2)
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `get_settings` | Get Node-RED runtime settings |
|
|
| `get_diagnostics` | Get runtime diagnostics (Node.js version, OS, modules) |
|
|
|
|
### Utility Tools (1)
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `api_help` | Node-RED Admin API endpoint reference |
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/nodered_mcp/
|
|
├── server.py # FastMCP server, config, client init
|
|
├── config.py # Pydantic Settings (env vars)
|
|
├── client.py # Async httpx wrapper with auth
|
|
├── models/
|
|
│ ├── base.py # BaseApiModel with from_api()
|
|
│ ├── flow.py # Node, FlowTab, Flow, FlowState
|
|
│ ├── node.py # NodeModule, NodeSet
|
|
│ ├── responses.py # FlowList, FlowSummary, Settings, etc.
|
|
│ └── layout.py # LayoutIssue, LayoutReport
|
|
└── tools/
|
|
├── flows.py # Flow CRUD + patch + auto-lint
|
|
├── nodes.py # Node management + search
|
|
├── layout.py # Layout lint checks
|
|
├── settings.py # Settings + diagnostics
|
|
└── utility.py # API help reference
|
|
```
|
|
|
|
The layer diagram is simple:
|
|
|
|
```
|
|
MCP Tools (thin async functions, return Pydantic models)
|
|
↓
|
|
NodeRedClient (async httpx, returns raw dicts)
|
|
↓
|
|
Node-RED Admin HTTP API
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
make all # format + lint + test
|
|
make check # lint + test (no format)
|
|
make test # uv run pytest tests/ -v
|
|
make lint # uv run ruff check src/ tests/
|
|
make format # uv run ruff format src/ tests/
|
|
```
|
|
|
|
Run a single test file:
|
|
|
|
```bash
|
|
uv run pytest tests/tools/test_layout.py -v
|
|
```
|
|
|
|
Run with coverage:
|
|
|
|
```bash
|
|
uv run pytest tests/ -v --cov=src/nodered_mcp --cov-report=term-missing
|
|
```
|
|
|
|
Always use `uv run` — never bare `python` or `pytest`.
|
|
|
|
See [conventions.md](conventions.md) for detailed code patterns (model layering, tool conventions, client architecture).
|
|
|
|
## License
|
|
|
|
MIT
|