# Model Context Protocol (MCP) over HTTP

**URL:** <https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877>\
**Category:** General\
**Tags:** ai\
**Created:** [4 May 2025 11:42 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877 "2025-05-04T11:42:32Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![makerstorage](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/makerstorage/32/57594_2.png) [@makerstorage](https://discourse.nodered.org/u/makerstorage)\
**Post date:** [4 May 2025 11:42 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/1 "2025-05-04T11:42:32Z")

</div>

Hi,  
Antropic recently introduce MCP streamable-http protocol. Is there anybody out there can create an example flow with NR.

> **[GitHub - modelcontextprotocol/typescript-sdk: The official Typescript SDK for Model Context...](https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#streamable-http)**
>
> The official Typescript SDK for Model Context Protocol servers and clients

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [4 May 2025 11:46 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/2 "2025-05-04T11:46:29Z")

</div>

There is a contrib node named `node-red-contrib-mcp-protocol` but it has issues. It is meant to connect to an MCP server but I could not get it to work.

For what purpose are you asking here though?  
To make Node-RED an MCP Server? MCP Client? MCP Host?  
If its a server, is it to provide custom tools (tools generated at runtime in node-red flows that an MCP client can query? or to programmatically access/modify Node-RED flows? or to serve info/data like "what flow file is this node-red running"?

etc.

---

<div class="post-metadata">

**Author:** ![makerstorage](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/makerstorage/32/57594_2.png) [@makerstorage](https://discourse.nodered.org/u/makerstorage)\
**Post date:** [4 May 2025 13:48 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/3 "2025-05-04T13:48:08Z")

</div>

Thanks for the reply. I wasn’t able to get the `node-red-contrib-mcp-protocol` node working either. What I’m really looking for is a basic example of how to implement an MCP **server** using Node-RED. Even a simple "add tool" example would be enough to help me understand the flow and how the protocol is expected to work in this context.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [4 May 2025 14:13 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/4 "2025-05-04T14:13:49Z")

</div>

You will find `FastMCP` far easier (it uses The official Typescript SDK for Model Context Protocol under the hood)

Here is a demo to get you going

![Code_ThWPjsWLhS](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/5/e58469ed81e6590eb3da43d342b183ccccb0a51b.gif)

```auto
[{"id":"fd8aaf75d0b12524","type":"function","z":"f2a7cc4cf78e0527","name":"\"my-mcp-server\"","func":"/// nothing to see here - checkout the On Start and On Stop tabs","outputs":0,"timeout":0,"noerr":0,"initialize":"const { FastMCP, Tool, Content } = fastmcp\nconst { z } = zod\nnode.warn({z,FastMCP});\n\nconst PORT = 8080\nconst SERVER_NAME = \"my-mcp-server\"\n\nconst GreetingSchema = z.object({\n // name with min len of 1 and max len of 100\n name: z.string().min(1).max(100)\n});\n\n/**\n * MCP tool for greeting the user\n * @param name The name of the user\n */\nconst greeterTool = {\n name: 'greet_person',\n description: 'Greet person by name',\n parameters: GreetingSchema,\n annotations: {\n title: 'Greet Person',\n readOnlyHint: true\n },\n execute: async ({ name }) => {\n try {\n console.log('Fetching greeting for person:', name);\n const greetingMessage = `Hi there ${name}! Welcome to MCP Server running in Node-RED!`;\n return { text: greetingMessage, type: 'text' };\n } catch (error) {\n throw error;\n }\n },\n};\n\ntry {\n\n const transportType = 'sse'\n const server = new FastMCP({\n name: SERVER_NAME,\n version: '1.0.0'\n });\n\n server.addTool(greeterTool);\n context.set('server', server)\n\n server.start({\n transportType,\n sse: {\n endpoint: '/sse',\n port: PORT,\n },\n });\n const info = `http://<ip>:${PORT}/sse`\n node.status({ fill: \"green\", shape: \"dot\", text: `${SERVER_NAME} is running at ${info}` });\n\n} catch (error) {\n node.status({fill:\"red\",shape:\"ring\",text:\"Failed to start server\"});\n node.error(`Failed to start server ${SERVER_NAME}: ${error.message}`)\n}\n","finalize":"let server = context.get('server')\nif (server?.stop) {\n node.warn(\"Shutting down MCP Server\");\n server.stop()\n server = null\n}\n","libs":[{"var":"fastmcp","module":"fastmcp"},{"var":"zod","module":"zod"}],"x":1620,"y":320,"wires":[]}]

```

---

<div class="post-metadata">

**Author:** ![makerstorage](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/makerstorage/32/57594_2.png) [@makerstorage](https://discourse.nodered.org/u/makerstorage)\
**Post date:** [4 May 2025 15:20 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/5 "2025-05-04T15:20:30Z")

</div>

Thanks for the great Help!

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/7/c/7c446b50710da4b97f7fa33f8cea93d0ba148ede.png)

---

<div class="post-metadata">

**Author:** ![makerstorage](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/makerstorage/32/57594_2.png) [@makerstorage](https://discourse.nodered.org/u/makerstorage)\
**Post date:** [4 May 2025 16:53 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/7 "2025-05-04T16:53:23Z")

</div>

```auto

[
    {
        "id": "ebd9e8f81e63a3e5",
        "type": "function",
        "z": "c1a5f1f03dbac4de",
        "name": "\"my-mcp-server\"",
        "func": "/// nothing to see here - checkout the On Start and On Stop tabs",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "const { FastMCP, Tool, Content } = fastmcp;\nconst { z } = zod;\n\nconst PORT = 8080;\nconst SERVER_NAME = \"my-mcp-server\";\n\n// === Define tool metadata ===\nconst toolDefinitions = [\n {\n name: \"get_weather\",\n description: \"Get current temperature for a given location.\",\n schema: z.object({\n location: z.string().describe(\"City and country e.g. Bogotá, Colombia\")\n })\n },\n {\n name: \"send_email\",\n description: \"Send an email to a specified recipient with a message body.\",\n schema: z.object({\n to: z.string().describe(\"Email address of the recipient.\"),\n body: z.string().describe(\"The content of the email body.\")\n })\n }\n];\n\n// === Create tools from definitions ===\nconst tools = toolDefinitions.map(def => ({\n name: def.name,\n description: def.description,\n parameters: def.schema,\n annotations: {\n title: def.name.replace(/_/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase()),\n readOnlyHint: true\n },\n execute: async (args) => {\n return new Promise((resolve, reject) => {\n try {\n const msg = {\n payload: {\n id: Date.now(),\n name: def.name,\n arguments: args\n },\n _mcpDone: resolve,\n _mcpFail: reject\n };\n node.send([msg, null]); // Output to dispatcher flow\n } catch (err) {\n reject(err);\n }\n });\n }\n}));\n\ntry {\n const transportType = 'sse';\n\n const server = new FastMCP({\n name: SERVER_NAME,\n version: '1.0.0'\n });\n\n // Register tools\n for (const tool of tools) {\n server.addTool(tool);\n }\n\n context.set('server', server);\n\n server.start({\n transportType,\n sse: {\n endpoint: '/sse',\n port: PORT,\n },\n });\n\n const info = `http://<ip>:${PORT}/sse`;\n node.status({ fill: \"green\", shape: \"dot\", text: `${SERVER_NAME} running at ${info}` });\n\n} catch (error) {\n node.status({ fill: \"red\", shape: \"ring\", text: \"Failed to start server\" });\n node.error(`Failed to start server ${SERVER_NAME}: ${error.message}`);\n}\n",
        "finalize": "let server = context.get('server')\nif (server?.stop) {\n node.warn(\"Shutting down MCP Server\");\n server.stop()\n server = null\n}\n",
        "libs": [
            {
                "var": "fastmcp",
                "module": "fastmcp"
            },
            {
                "var": "zod",
                "module": "zod"
            }
        ],
        "x": 810,
        "y": 440,
        "wires": [
            [
                "5ba9094de54738db"
            ]
        ]
    },
    {
        "id": "5ba9094de54738db",
        "type": "function",
        "z": "c1a5f1f03dbac4de",
        "name": "Prepare dispatch",
        "func": "// each msg.payload is one tool-call object\nmsg.callId = msg.payload.id;\nmsg.args = msg.payload.arguments;\nmsg.target = msg.payload.name; // drives the dynamic Link Call\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1050,
        "y": 440,
        "wires": [
            [
                "29dcde5c9a626a57"
            ]
        ]
    },
    {
        "id": "29dcde5c9a626a57",
        "type": "link call",
        "z": "c1a5f1f03dbac4de",
        "name": "Dispatch (dynamic)",
        "links": [],
        "linkType": "dynamic",
        "timeout": "30",
        "x": 1330,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "6e928b60e13c562d",
        "type": "link in",
        "z": "c1a5f1f03dbac4de",
        "name": "get_weather",
        "links": [],
        "x": 755,
        "y": 640,
        "wires": [
            [
                "2330e7aef7e4978a"
            ]
        ]
    },
    {
        "id": "2330e7aef7e4978a",
        "type": "function",
        "z": "c1a5f1f03dbac4de",
        "name": "stub get_weather",
        "func": "const location = msg.args?.location || 'unknown';\nconst result = `Weather in ${location}: sunny 25°C`;\n\nif (msg._mcpDone) {\n msg._mcpDone({ text: result, type: \"text\" });\n}\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 955,
        "y": 640,
        "wires": [
            [
                "73abc3922f507f3d"
            ]
        ]
    },
    {
        "id": "73abc3922f507f3d",
        "type": "link out",
        "z": "c1a5f1f03dbac4de",
        "name": "return get_weather",
        "mode": "return",
        "links": [],
        "x": 1155,
        "y": 640,
        "wires": []
    },
    {
        "id": "e9857db914735441",
        "type": "link in",
        "z": "c1a5f1f03dbac4de",
        "name": "send_email",
        "links": [],
        "x": 755,
        "y": 740,
        "wires": [
            [
                "8cbcce9b477e879c"
            ]
        ]
    },
    {
        "id": "8cbcce9b477e879c",
        "type": "function",
        "z": "c1a5f1f03dbac4de",
        "name": "stub send_email",
        "func": "const to = msg.args?.to || 'nobody';\nconst body = msg.args?.body || '';\nconst result = `Pretend email sent to ${to} with body: \"${body}\"`;\n\nif (msg._mcpDone) {\n msg._mcpDone({ text: result, type: \"text\" });\n}\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 955,
        "y": 740,
        "wires": [
            [
                "783f93e0c906606a"
            ]
        ]
    },
    {
        "id": "783f93e0c906606a",
        "type": "link out",
        "z": "c1a5f1f03dbac4de",
        "name": "return send_email",
        "mode": "return",
        "links": [],
        "x": 1155,
        "y": 740,
        "wires": []
    }
]

```

---

<div class="post-metadata">

**Author:** ![JpEncausse](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/jpencausse/32/2561_2.png) [@JpEncausse](https://discourse.nodered.org/u/JpEncausse)\
**Post date:** [19 June 2025 12:59 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/8 "2025-06-19T12:59:15Z")

</div>

Hello I was wondering if there is alternative or native answer ?

I want to keep my HTTP or Websocket node, provide the payload to a whitebox (the MCP SDK) then handle tools and send the response back.

1. I don't want to instanciate an "MCP Server" that will, underhood, open port and do some similar stuff to HTTP or Websocket node. Unless @knolleary states it's official.

2. I don't want to instanciate a library that underhood run Python, that's horrible architecture.

May be I misunderstood, but it's only JSON exchange over a specific protocole official libraries should split the server part from the logical part ?

The implementation [seems really weak](https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#without-session-management-stateless):

```auto
const server = getServer(); 
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
      sessionIdGenerator: undefined,
});

```

- They build a `server` object to declare tools and routes, thats fine even if they think in term of pseudo-URL
- They rely on a `transport` object that mix server concepts like session and business concepts.

Thanks for clarification

---

<div class="post-metadata">

**Author:** ![bakman2](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bakman2/32/6207_2.png) [@bakman2](https://discourse.nodered.org/u/bakman2)\
**Post date:** [19 June 2025 16:09 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/9 "2025-06-19T16:09:31Z")

</div>

MCP is a protocol and part of its 'nature' is that MCP communicates using SSE (server-send events), if you want to keep using ws/http, you can use tools instead as you can define a tool function to make a request over websockets or http(s) .

---

<div class="post-metadata">

**Author:** ![JpEncausse](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/jpencausse/32/2561_2.png) [@JpEncausse](https://discourse.nodered.org/u/JpEncausse)\
**Post date:** [19 June 2025 16:40 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/10 "2025-06-19T16:40:55Z")

</div>

Hum, I don't have enought knowledge on that, it seems server-send-events is on top HTTP protocole, so it should work with regular http node ?

> you can use tools instead as you can define a tool function to make a request over websockets or http(s)

Hum, I thought you have to declare tool to the MCP server ? like transport.

May be I mis-understood MCP. I thought I can

- declare an HTTP-in EndPoint receive queries from clients push it to a "MCP Manager"
- the manager handle the protocole
- the manager trigger tools, aka a callback function that would do node.send() somehow
- it should be "easy to call from a node manager.send() something

Ahhhh I think I understand, in order to manager.send() it requires an access to req/res and it is where Node-RED had limits and I need to use node that wrap http in/out protocole.

---

<div class="post-metadata">

**Author:** ![bakman2](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bakman2/32/6207_2.png) [@bakman2](https://discourse.nodered.org/u/bakman2)\
**Post date:** [20 June 2025 06:03 UTC](https://discourse.nodered.org/t/model-context-protocol-mcp-over-http/96877/11 "2025-06-20T06:03:41Z")

</div>

> [@JpEncausse](#):
>
> I thought you have to declare tool to the MCP server ?

The terminology is a bit confusing (for me at least) - in the LLM you configure MCP tools - which are MCP 'clients' that connect to 'servers' - this uses JSON-RCP over SSE. See the [architecture](https://modelcontextprotocol.io/docs/concepts/architecture)
