feat(server)\!: drop McpHttpHandler.node; ship toNodeHandler(handler) in @modelcontextprotocol/node#2331
feat(server)\!: drop McpHttpHandler.node; ship toNodeHandler(handler) in @modelcontextprotocol/node#2331felixweinberger wants to merge 1 commit into
Conversation
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
f9069fa to
c8779f8
Compare
761bd17 to
e230a8e
Compare
🦋 Changeset detectedLatest commit: daf43d3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🦋 Changeset detectedLatest commit: c8779f8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
e230a8e to
ee14814
Compare
c8779f8 to
d6e2be7
Compare
There was a problem hiding this comment.
Both points from my earlier review (the package README's Exports section and the loss of error reporting in the adapter's catch) are addressed in the latest revision — toNodeHandler now takes an optional onerror with tests, and the README documents the new exports. I didn't find any new issues, but since this is a breaking change to the public handler interface spanning five packages, it should still get a human maintainer's sign-off.
Extended reasoning...
Overview
This PR removes the .node(req, res, parsedBody?) face from McpHttpHandler and ships the Node adapter as a standalone toNodeHandler(handler, { onerror? }) in @modelcontextprotocol/node. The conversion logic (nodeRequestToFetchRequest, backpressure-aware response writing, the 500/-32603 fallback) moves verbatim from createMcpHandler.ts into the new toNodeHandler.ts, the six .node-face unit tests are re-homed to the node package with additional onerror coverage, eight examples plus the integration/conformance fixtures are swept to the new pattern, and the migration docs/changesets are updated. The latest commits also resolve the two nits from my prior review: the node package README now lists toNodeHandler and its types, and the adapter accepts an onerror callback restoring observability parity with the removed face (with tests pinning that the callback fires and that a throwing reporter doesn't alter the 500 response).
Security risks
Low. The adapter performs no auth decisions of its own — req.auth remains pass-through to handler.fetch as authInfo, never derived from headers, matching the prior .node face exactly. HTTP/2 pseudo-header skipping and the body re-serialization behavior are copied unchanged. The error fallback maps adapter failures to -32603/500 (consistent with the repo's error-handling conventions) and the onerror callback is wrapped so it cannot alter the response.
Level of scrutiny
High. This is a breaking change to a public interface (McpHttpHandler), drops two type exports from @modelcontextprotocol/server, and triggers major bumps across five packages per the changeset bot. While the implementation is largely a mechanical relocation with strong test coverage (the moved tests assert the same conversion semantics, plus new closed-handler and onerror cases), the decision to remove a shipped API surface and relocate it to a sibling package is a design/API-ownership call that the maintainers should explicitly endorse — it is exactly the kind of decision the repo's review conventions reserve for human judgment.
Other factors
The bug-hunting pass found no issues this round. Migration docs (migration.md, migration-SKILL.md) and both changesets accurately describe the new behavior including the onerror option, and the only remaining handler.node reference is the intentional historical mapping in the skill doc. Test gates reported in the description (full package suites, integration, e2e, conformance) are consistent with the diff's relocated coverage.
d6e2be7 to
7327fb9
Compare
There was a problem hiding this comment.
Both of my earlier comments (the missing onerror reporting in toNodeHandler and the package README exports) are addressed in the latest revision and I found no further issues — but since this removes McpHttpHandler.node from the public API (major bump across server/express/fastify/hono), it still warrants a maintainer's review of the design before merging.
Extended reasoning...
Overview
This PR removes the .node(req, res, parsedBody?) face from McpHttpHandler and ships the Node adapter as toNodeHandler(handler, { onerror? }) in @modelcontextprotocol/node. The adapter body (request conversion, pre-parsed-body handling, req.auth pass-through, HTTP/2 pseudo-header skipping, write backpressure, 500/-32603 fallback) is moved verbatim from createMcpHandler.ts, the Node*Like types move packages, and every .node(...) call site across examples, integration tests, and the conformance fixture is swept to the new wrapper. The diff also carries a stacked change making ctx.mcpReq.log() emit request-related with a per-request _meta.logLevel filter on 2026-era requests.
Changes since my last review
The latest revision adds a ToNodeHandlerOptions.onerror callback (with tests covering the report-then-500 path and a throwing reporter), restoring the observability parity I flagged, and the @modelcontextprotocol/node README's Exports section and the migration docs now document toNodeHandler. Both prior inline comments are resolved.
Security risks
Low. The adapter forwards req.auth as pass-through authInfo exactly as the removed .node face did and performs no token derivation from headers; no auth, crypto, or validation logic changes. The behavior-relevant surface is the error fallback and header conversion, both relocated unchanged and covered by the relocated unit tests.
Level of scrutiny
High by policy rather than by bug risk: this is a breaking removal of a public API method with major changesets across five packages, plus a logging-semantics change (ctx.mcpReq.log riding the in-flight exchange and the spec-mandated absent-key suppression on 2026-era requests). Those are SDK-surface design decisions a maintainer should sign off on, even though the implementation is largely a mechanical relocation with tests re-homed and one new test added.
Other factors
Test coverage looks thorough — the six .node-face tests moved to the node package plus new onerror tests, integration and conformance fixtures updated, and the e2e log-from-handler scenario adjusted for the new _meta.logLevel opt-in. The migration guide and changesets accurately describe the shipped behavior, including the onerror option.
ee14814 to
cf76ed1
Compare
7327fb9 to
bb7a7ca
Compare
bb7a7ca to
8e9d981
Compare
8e9d981 to
daf43d3
Compare
…dler) in @modelcontextprotocol/node
createMcpHandler now returns the web-standards-idiomatic { fetch, close, notify, bus } shape — what Workers/Bun/Deno expect from `export default`. The duck-typed .node(req, res, parsedBody?) face is removed; Node frameworks (Express, Fastify, plain node:http) wrap the handler once with toNodeHandler(handler) from @modelcontextprotocol/node, which converts the Node request to a web-standard Request, calls handler.fetch, and writes the Response back honoring write backpressure.
NodeIncomingMessageLike / NodeServerResponseLike move from @modelcontextprotocol/server to @modelcontextprotocol/node. The Node-conversion helpers and the .node-face unit tests move with them (toNodeHandler.test.ts).
Consumers updated: examples/server (dualEraStreamableHttp, multiRoundTrip), test/integration createMcpHandler over real HTTP, test/conformance everythingServer.
BREAKING CHANGE: McpHttpHandler.node is removed. Use toNodeHandler(handler) from @modelcontextprotocol/node.
daf43d3 to
f954a2d
Compare
Removes the
.nodeface fromMcpHttpHandlerand ships the Node adapter as a free functiontoNodeHandler(handler)in@modelcontextprotocol/node. Stacked onfweinberger/on-small-fixes.Motivation and Context
{ fetch, close, notify, bus }is the web-standards-idiomatic handler shape (Workers / Bun / Denoexport default). Carrying a Node-specific.node(req, res, body?)method on the same interface couples the runtime-neutral entry to a Nodehttpadapter and forces every host to take the Node types.This PR:
McpHttpHandler.nodefrom the interface and implementationNodeIncomingMessageLike/NodeServerResponseLikeand thenodeRequestToFetchRequestbody verbatim intopackages/middleware/node/src/toNodeHandler.tsastoNodeHandler(handler): (req, res, parsedBody?) => Promise<void>overhandler.fetch(structurally typedFetchLikeMcpHandler, so hand-wiredisLegacyRequestcompositions also adapt)fetchNode*Liketype re-exports from@modelcontextprotocol/server; addstoNodeHandler+ 4 types to@modelcontextprotocol/node.node(...)call site inexamples/(8 sites acrossharness.ts,legacy-routing,bearer-auth,oauth,oauth-client-credentials,json-response,stateless-legacy,subscriptions) and the integration/conformance fixtures; six@mcp-examples/*packages gain a@modelcontextprotocol/nodeworkspace depHow Has This Been Tested?
.node-face unit tests +nodeRequestResponsefixture re-homed topackages/middleware/node/test/toNodeHandler.test.ts(+1 new closed-handler 500 test); server suite keeps the detach-safe-fetch test only — pure relocation following the implementation, no assertions weakened.fetchonly — no change neededjson-schema-2020-12,server-sse-polling)Breaking Changes
Yes.
McpHttpHandler.nodeis removed. Migration:migration.md+migration-SKILL.mdcarry the before/after; the existingcreate-mcp-handler.mdchangeset is reworded and a newhandler-drop-node-face.mdchangeset is added (server major / node minor).Types of changes
Checklist
Additional context
The only remaining
handler.nodereference is the intentional historical mapping indocs/migration-SKILL.md.