Skip to content

fix(server): preserve forwarded params and support modern low-level connections - #3057

Open
steipete wants to merge 2 commits into
modelcontextprotocol:mainfrom
steipete:fix/forward-notifications-modern-transport
Open

steipete wants to merge 2 commits into
modelcontextprotocol:mainfrom
steipete:fix/forward-notifications-modern-transport

Conversation

@steipete

Copy link
Copy Markdown

Fixes #3055.

A gateway cannot forward a change notification faithfully through ServerEventBus: the event carries only a kind/URI, so the listen router drops custom _meta and extension params. A manually connected Server also rejects a correctly classified modern request because advertising supported versions does not bind the instance's serving revision.

Keep optional notification params on bus events. The listen router retains those fields while stamping its own subscription ID, and resource events use their authoritative URI for filtering and delivery. Add an optional ServerConnectOptions argument to Server.connect and McpServer.connect so a custom serving entry can bind a supported modern revision before transport startup. Clear that explicit binding on close; existing legacy connections keep their initialization flow.

Ten public API regressions cover all four change notification kinds over real HTTP, both server classes, unsupported revisions, and a later legacy connection. All ten fail on the unmodified source and pass with this change. Includes documentation and a server patch changeset.

Validation on macOS with Node 24:

  • pnpm lint:all passes from a fresh checkout.
  • pnpm build:all and pnpm typecheck:all pass.
  • node scripts/smoke-dist-types.mjs passes for ESM and CommonJS without skipLibCheck.
  • pnpm test:all: all 271 test files report passed, with 7,207 tests passed and 141 existing expected failures. It exits nonzero on an unhandled 200 ms REQUEST_TIMEOUT rejection from scenarios/protocol.test.ts.

The protocol timeout rejection also occurs without this change. An earlier full run reported two unhandled rejections; replaying only scenarios/protocol.test.ts against unmodified upstream commit 9bc40e903fde15534ec06ff4c1347d47000dbe16 reproduced both (358 passed, 50 expected failures, two unhandled errors). The latest full run reports one of those rejections. No timeout, assertion, expected failure, or lint rule was changed.

@steipete
steipete requested a review from a team as a code owner October 20, 2026 05:11
@changeset-bot

changeset-bot Bot commented Oct 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d9e4be

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/server Patch
@modelcontextprotocol/client Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch
@modelcontextprotocol/core-internal Patch

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T05:18:44.707328Z 6d9e4be PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Oct 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@3057

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@3057

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@3057

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@3057

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@3057

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@3057

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@3057

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@3057

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@3057

commit: 6d9e4be

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d9e4be81f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (!supported.includes(options.protocolVersion)) {
throw new UnsupportedProtocolVersionError({ supported, requested: options.protocolVersion });
}
this._negotiatedProtocolVersion = options.protocolVersion;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Notify the transport of the explicitly selected revision

When options is used with a custom transport that implements setProtocolVersion (for example, one that uses the selected revision to configure framing or headers), this branch updates only the Server and never calls that transport hook. The normal server initialize path calls this.transport?.setProtocolVersion(...), and modern client negotiation likewise propagates its choice, so an explicitly connected server and its transport can retain different version state for the whole connection; propagate options.protocolVersion to the transport as part of this path.

Useful? React with 👍 / 👎.

throw new UnsupportedProtocolVersionError({ supported, requested: options.protocolVersion });
}
this._negotiatedProtocolVersion = options.protocolVersion;
this._explicitProtocolVersion = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset and backfill identity on explicit modern connects

When a Server that completed a legacy initialize is closed and then reconnected through this explicit-modern path, _clientCapabilities and _clientVersion still belong to the old client because this path changes only the protocol version and modern connections never run _oninitialize to overwrite them. getClientCapabilities() and getClientVersion() explicitly remain functional for 2026 requests via envelope backfilling, but the manual connection path never performs the equivalent of seedClientIdentityFromEnvelope, so modern handlers can observe the previous client's identity (or undefined on a fresh instance); clear connection-scoped identity and backfill it from the validated envelope.

Useful? React with 👍 / 👎.

@claude claude Bot added the v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes label Oct 20, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Ideas, requests and plans for v2 of the SDK which will incorporate major changes and fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v2: preserve forwarded notification params and support modern low-level serving

1 participant