Skip to main content
Glama
Node-RED Object Action MCP

Node-RED Object Action MCP

by sammachin
README.md
# node-red-object-action-mcp

Expose your Node-RED flows as **MCP (Model Context Protocol) servers**. Declare an
*object* (e.g. `office lights`) with free-text *actions* (`turn on`, `turn off`, …) and
*readable values* (`status`, `brightness`), and an AI agent speaking MCP can call them as
tools — driving your flow and reading values back out of it.

The server speaks JSON-RPC 2.0 over the **Streamable HTTP** transport (MCP protocol
`2025-06-18`) and is mounted on Node-RED's own runtime HTTP server, so no extra port or
process is needed.

## Nodes

| Node | Palette label | Purpose |
|------|---------------|---------|
| `object-action-mcp-endpoint` | *(config)* | Mounts an MCP server at a path (e.g. `/mcp`). Multiple endpoints can coexist. |
| `object-action-mcp-object` | **object** | One object + N free-text actions. Each action is a tool and an output; a call fires a `msg` from that output. |
| `object-action-mcp-read` | **object read** | One object + N readable values. Each value is a tool and an output; a call emits a `msg` whose value is returned by a response node. |
| `object-action-mcp-response` | **mcp response** | Returns `msg.payload` to the waiting MCP tool call. |

## How it works

```
                                   ┌─────────────────────────────┐
  MCP client ──HTTP POST /mcp──▶   │  object-action-mcp-endpoint │
  (agent)    ◀──JSON-RPC result    └─────────────────────────────┘
                                         │ tools/call        ▲
                                         ▼                   │ resolve
             ┌──────────────┐      ┌───────────┐      ┌──────────────┐
             │  object/read │──msg─▶│ your flow │──msg─▶│ mcp response │
             └──────────────┘      └───────────┘      └──────────────┘
```

- **Actions** (object node) can be configured **per action** to either:
  - return an **instant ack** (fire-and-forget — the configured ack text is returned at once), or
  - **wait for a response** node (the call is held open until a response node returns a result).
- **Reads** (object read node) always wait for a response node to supply the value.
- **Parameters:** actions and reads can declare typed parameters (`string` / `number` /
  `boolean`, optional/required). They become the tool's MCP `inputSchema`, and the call
  arguments arrive on `msg.payload`.

The emitted `msg` carries `object`, `action`/`read`, `tool`, and an opaque `_oamcp`
correlation handle. **Pass `_oamcp` through unchanged** so the response node can match the
value back to the right call. The response node returns `msg.payload` (a string is sent as
text; an object is also returned as `structuredContent`); set `msg.isError = true` to return
it as a tool error.

## Install

From your Node-RED user directory (`~/.node-red`):

```bash
npm install node-red-object-action-mcp
```

or, for local development, install from a checkout:

```bash
cd ~/.node-red
npm install /path/to/node-red-object-action-mcp
```

Restart Node-RED. The nodes appear under the **mcp** palette category.

## Quick start

1. Drag an **object** node onto a flow, add an **endpoint** config (path `/mcp`), set the
   object to `office lights`, and add actions `turn on` and `turn off` (instant ack).
2. Wire each action output to a debug (or your device logic).
3. Drag an **object read** node on the same endpoint, object `office lights`, value
   `status`; wire it through a function that sets `msg.payload` into an **mcp response** node.
4. Deploy.

An importable version of this flow is in [`examples/office-lights.json`](examples/office-lights.json).

### Try it with curl

```bash
# initialize
curl -s http://localhost:1880/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

# list tools
curl -s http://localhost:1880/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# call an action
curl -s http://localhost:1880/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"office_lights_turn_on","arguments":{}}}'

# read a value
curl -s http://localhost:1880/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"office_lights_status","arguments":{}}}'
```

### Try it with the MCP Inspector

```bash
npx @modelcontextprotocol/inspector
```

Choose the **Streamable HTTP** transport and connect to `http://localhost:1880/mcp`.

## Tool names

Tool names are derived from the object and action/value names, lowercased with non
`[a-z0-9_-]` characters replaced by `_` (e.g. `office lights` + `turn on` →
`office_lights_turn_on`). Duplicate names on one endpoint are rejected (the later node shows a
red status and a warning in the log).

## Authentication

Each endpoint can optionally require a **bearer token** (set *Auth = Bearer token* on the
config node). Clients then send `Authorization: Bearer <token>`. You can also rely on
Node-RED's own `httpNodeAuth`/`httpNodeMiddleware` settings for the whole HTTP server.

> **Note:** MCP endpoints are served on Node-RED's runtime HTTP server. Expose them only on
> trusted networks, or put them behind TLS and the bearer token (or a reverse proxy).

## Notes & limitations

- Implements the tools surface only (`initialize`, `tools/list`, `tools/call`, `ping`). No
  resources, prompts, or server-initiated streaming; `GET` returns `405` by design.
- Sessions are lenient: an `Mcp-Session-Id` is issued on `initialize` but not required on
  later requests, so the server works statelessly.
- JSON-RPC batching is intentionally unsupported (it was removed in MCP `2025-06-18`).
- Zero runtime dependencies.

## Development

```bash
npm install
npm test
```

## License

MIT © Sam Machin