Initial commit: Node-RED MCP server (Python/FastMCP)
22-tool MCP server for Node-RED flow management with Pydantic models, incremental flow patching, layout linting, and flexible auth (token, basic, OAuth2). 137 tests, full ruff compliance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
260
README.md
Normal file
260
README.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# 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
|
||||
|
||||
A common setup is Node-RED behind [Authentik](https://goauthentik.io/) as a forward auth proxy. 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.
|
||||
|
||||
```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.example.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
|
||||
Reference in New Issue
Block a user