2025 年 5 月時点 stdio 標準入出力 MCP サーバーをブリッジにして Node-RED が MCP クライアントと連携するメモ

2025 年 5 月時点 stdio 標準入出力 MCP サーバーをブリッジにして Node-RED が MCP クライアントと連携するメモ

2025 年 5 月時点 stdio 標準入出力 MCP サーバーをブリッジにして Node-RED が MCP クライアントと連携するメモです。

背景

あくまで2025 年 5 月時点の仕様での試してみたお話です。ここから仕様が変わったり、より良いアプローチが見つかるかもしれません。

現時点で HTTP + SSE な仕様 Protocol Revision: 2024-11-05 Transports から Streamable HTTP な仕様 Protocol Revision: 2025-03-26 Transports への移行中なので、このあたりで連携するトライをする前に、仕様が落ち着いていて対応クライアントアプリも Claude Desktop や Cline などがあって試しやすい stdio 標準入出力 MCP サーバーをブリッジにして Node-RED が MCP クライアントと連携してみます。

こんな仕組みです。

ブリッジする stdio 標準入出力 MCP サーバーを TypeScript で作る

Node.js v18.19.0 で動かしています。fetch 関数を素直に動かすためです。これ以上のバージョンであれば fetch 関数は問題なく動きます。

モジュールのインストール

今回はサーバーとはいえ HTTP サーバーは作らないので express 系は入れません。

modelcontextprotocol/typescript-sdk: The official Typescript SDK for Model Context Protocol servers and clients

こちらをもとに、

npm install @modelcontextprotocol/sdk zod

npm install -D typescript ts-node @types/node

を実行してモジュールの準備をします。

stdio-node-red-bridge.ts 用意

こちらの記事の Running Your Server の stdio のソースを元に足し算のツールの入り口と Node-RED への実際の足し算の処理をつなげる stdio-node-red-bridge.ts で用意します。

import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "Demo",
  version: "1.0.0"
});

// Add an addition tool
server.tool(
  "add",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => {
    
    // Node-RED にブリッジ
    const res = await fetch("http://127.0.0.1:1880/add", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ a, b }),
    });

    if (!res.ok) {
      throw new Error(`Node-RED error: ${res.status} ${res.statusText}`);
    }
    const json = await res.json() as { result: number };
    return {
      content: [
        { type: "text", text: String(json.result) },
      ],
    };

  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

package.json に "type": "module" 加える

{
  "name": "mcp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^22.15.9",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.11.0",
    "zod": "^3.24.4"
  }
}

package.json はこんな構成です。

"type": "module" を加えます。

npm run で動かせるようにする

package.json の scripts 項目に

"stdio-node-red-bridge": "node --no-warnings --loader ts-node/esm stdio-node-red-bridge.ts",

を TypeScript で stdio-node-red-bridge.ts を実行するコマンドを加えます。

{
  "name": "mcp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
	"stdio-node-red-bridge": "node --no-warnings --loader ts-node/esm stdio-node-red-bridge.ts",
	"test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^22.15.9",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.11.0",
    "zod": "^3.24.4"
  }
}

こんな風に加えました。

--no-warnings がついている理由は、

(node:67812) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)

という ExperimentalWarning が、環境によって(といっても私の環境がですが)毎回出てしまうので、消すために入れています。

tsconfig.json

tsconfig.json も準備して、この後、TypeScript を動かす設定もしておきます。

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "types": ["node"],
    "strict": true
  },
  "include": ["**/*.ts"]
}

Node-RED でフローを作る

このようなフローを作りました。

stdio 標準入出力 MCP サーバーから足し算ツールの情報を受け付ける http in ノードです。

POST リクエストで /add というパスから stdio 標準入出力 MCP サーバーから足し算する値を受信します。

実際の足し算をする function ノードです。

// 足し算
const result = msg.payload.a + msg.payload.b;

msg.payload = {};
msg.payload.result = result;

return msg;

ごくシンプルに足し算をして result 値に入れて返しています。

http response ノードは、デフォルトの設定のままでシンプルに返答します。

今回の JSON フローデータです。

[{"id":"c764f7c40134fe66","type":"http in","z":"6b422e4d3cdf58d8","name":"","url":"/add","method":"post","upload":false,"swaggerDoc":"","x":140,"y":140,"wires":[["9ae546d31023c8e0","76fd73c7e1f6391f"]]},{"id":"e69eb256b6f3ad6c","type":"http response","z":"6b422e4d3cdf58d8","name":"","statusCode":"","headers":{},"x":550,"y":140,"wires":[]},{"id":"9ae546d31023c8e0","type":"function","z":"6b422e4d3cdf58d8","name":"足し算","func":"// 足し算\nconst result = msg.payload.a + msg.payload.b;\n\nmsg.payload = {};\nmsg.payload.result = result;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":140,"wires":[["e69eb256b6f3ad6c"]]},{"id":"76fd73c7e1f6391f","type":"debug","z":"6b422e4d3cdf58d8","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":340,"y":100,"wires":[]}]

stdio で動かしてみる

Node-RED を http://127.0.0.1:1880/ で起動しつつ、TypeScript のコードをつくったプロジェクトフォルダで、

npm run stdio-node-red-bridge

を実行します。

起動しました。

{"jsonrpc":"2.0","method":"tools/call","params":{"name":"add","arguments":{"a":4,"b":3}},"id":1}

足し算ツールをする JSON データを入力して実行します。

Node-RED 側に無事にデータが送られて計算処理がされます。

実行結果が返ってきて足し算結果が無事に表示されました。

Claude Desktop で動かしてみる

では Node-RED を起動しっぱなしにして Claude Desktop に今回の MCP サーバーを設定したので、動かしてみます。

"node-red-bridge": {
      "command": "npm",
      "args": [
        "--prefix",
        "~~~プロジェクトのパス~~~\\mcp",
        "run",
        "stdio-node-red-bridge",
        "--silent"
      ]
    }

Claude Desktop での MCP サーバー設定はこんな設定です。~~~プロジェクトのパス~~~ は配置したフォルダに合わせて設置します。--silent は起動ログを出さないパラメータです。もし類似した足し算ツールが他の検証で入っていたらオフにしておくほうが混乱が少なくていいでしょう。

うまく設定できたらこのように「3と4で足し算したいです」と話しかけます。

無事、ツールが認識されて MCP サーバーへの実行内容が準備され、実行許可が求められます。

許可をすると無事 MCP サーバーに届き結果が返ってきます!もちろん Node-RED にブリッジされ計算処理が行われています。

MCP クライアント(Claude Desktop)から見ると、MCP サーバーはスキーマ定義の交換とツール使用時の Node-RED へのブリッジを担当している関係となり、Node-RED はブリッジ後の処理全般を行っている役割分担になります。