Spec-compliant MCP server over Streamable HTTP (official SDK) + Node 18 install fix + modernized nodes#9
Open
vvzvlad wants to merge 11 commits into
Open
Conversation
Drop the node-red peerDependency (auto-installed node-red@4.1 -> validate-npm-package-name@7, which requires Node >=20 and caused EBADENGINE on Node 18). Declare host compat via the node-red.version field instead. Add @modelcontextprotocol/sdk ^1.29, bump uuid to 11, add dev deps (node-red, node-red-node-test-helper, mocha, chai). Bump to 2.0.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RED-independent module exposing attachMcpStreamableEndpoint(app, {serverInfo, listTools,
callTool, logger}). Stateless Streamable HTTP on the official SDK low-level Server: per-request
Server+transport (sessionIdGenerator undefined), raw JSON Schema tools (no Zod), POST /mcp real
transport, GET/DELETE -> 405, JSON-RPC parse-error handler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the hand-rolled POST /mcp method switch (initialize/tools_list/tools_call/*_tool) and the fake /sse heartbeat. Wire the real MCP endpoint via lib/mcp-streamable. Keep CORS (now with mcp-session-id / mcp-protocol-version headers + exposed Mcp-Session-Id), /health, the full lifecycle (start/stop/restart/autoStart/status, port 8001 default), and the executeToolFlow bridge (mcp-tool-execute -> mcp-tool-response, 30s timeout). Modernize Node-RED API: close(removed, done), input(msg, send, done), node.error, and RED.auth.needsPermission on the admin endpoint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mcp-tool-registry: close(removed, done) signature, input(msg, send, done) with done callback, safer payload access; tool-register/unregister event contract unchanged. Protect the admin endpoints (/mcp-servers, /mcp-tools/:serverUrl) with RED.auth.needsPermission. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three suites run with a real @modelcontextprotocol/sdk Client over StreamableHTTPClientTransport: (1) lib-level listTools/callTool incl. raw JSON Schema passthrough; (2) Node-RED e2e proving the registry -> server -> flow bridge via node-red-node-test-helper; (3) error cases (unknown tool -> isError, malformed JSON -> JSON-RPC parse error, GET /mcp -> 405). Stop gitignoring test/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code review found that adding a fresh node.on('input') per executeToolFlow corrupts
Node-RED 4.x done-accounting (_expectedDoneCount/_complete) and leaks listeners under
concurrent calls. Replace with one persistent input handler dispatching on a node._pending
Map keyed by executionId. Reject and clear pending calls on close. Restart now starts in the
stopServer callback (port already released) instead of a fixed 1s delay. Drop dead close-arg
normalization in both nodes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 lib edge-case tests (normalizeResult variants, multi-tool listing, schema defaulting, MCP-shape passthrough, dynamic add/remove, concurrent lib calls, throwing tool) and a Node-RED e2e concurrent-call test that exercises the node._pending dispatch path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per editor review: add paletteLabel to the MCP Flow Server and MCP Tool Registry nodes, and validate serverPort via parseInt so non-numeric input is rejected predictably. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rewrite the README intro around the real MCP Flow Server, add JS and Python standard-client connection examples, a migration note, updated requirements (Node >=18, Node-RED >=3) and dependency list. Add a 2.0.0 CHANGELOG entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g for flow) New mcp-flow-server checkbox `optimisticAck` (default off). When on, a tool call replies "ok" to the MCP client immediately after emitting mcp-tool-execute, without waiting for an mcp-tool-response from the flow — fire-and-forget device control, no response wiring needed. Adds an e2e test (22 passing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The
mcp-flow-servernode currently implements a bespokePOST /mcpJSON-RPC switch that is not MCP-spec-compliant: there is no Streamable HTTP transport, no session handling, and/sseis a plain heartbeat rather than the MCP SSE transport. As a result, standard MCP clients (the official Python/TypeScript SDK clients, etc.) cannot talk to it. This PR turns it into a real, spec-compliant MCP server while keeping the visual, flow-based tool definition.Changes
@modelcontextprotocol/sdk(low-levelServer+StreamableHTTPServerTransport, stateless). New Node-RED-independent modulelib/mcp-streamable.js;mcp-flow-server.jsdelegates to it. Tools are read from the registry on everytools/list, so dynamic add/remove keeps working withoutlistChanged.StreamableHTTPClientTransportand the Pythonmcpstreamablehttp_client(initialize+tools/list+tools/call), including inside a real Node-RED 4.x Docker container on Node 18.EBADENGINE): dropped thenode-redpeerDependency that pulled a Node-20-only transitive (validate-npm-package-name@7); slimmed deps;engines.node >= 18.close(removed, done)/input(msg, send, done)withdone(),node.error(err, msg), admin endpoints behindRED.auth.needsPermission(...), port validation, palette labels.executionIdinstead of a per-callinputlistener (fixes listener leak / done-accounting under concurrent calls); added a concurrency test.node-red-node-test-helper, mocha/chai).Notes
Tested on Node 18.20.8 / Node-RED 4.1.x. The cosmetic color change is a separate commit and can be dropped if you'd prefer. Happy to adjust scope.