Skip to content

feat(core,server): expose the per-request envelope on the request handler context#2231

Open
felixweinberger wants to merge 4 commits into
mainfrom
fweinberger/m2-ctx-envelope
Open

feat(core,server): expose the per-request envelope on the request handler context#2231
felixweinberger wants to merge 4 commits into
mainfrom
fweinberger/m2-ctx-envelope

Conversation

@felixweinberger
Copy link
Copy Markdown
Contributor

@felixweinberger felixweinberger commented Jun 1, 2026

🗺️ 2026-07-28 Spec Implementation — milestone tracker · This PR: M2 (2/2): the per-request envelope on the handler context
Stacked on #2230 (Server retains the negotiated protocol version). Review/merge that first.

Exposes the per-request envelope on the request handler context: ctx.mcpReq.protocolVersion (both sides) and ctx.client.{capabilities, info} (server side). Handlers can finally answer "what protocol version am I serving, who is calling, and what can they do."

Motivation and Context

Today a request handler cannot know any of this: the negotiated protocol version is discarded (fixed in #2230), and client capabilities/info are only reachable through connection-scoped getters on the Server instance, not from handler context.

This is the keystone of the per-request envelope groundwork for the 2026-07-28 spec release (#2184): under the draft spec, protocol version and client facts travel on every request (_meta), and handlers/SDK internals need one place to read them that works identically in both eras. This PR makes that place exist, sourced from the 2025 handshake (the only source today); the 2026 _meta sourcing plugs into the same fields when per-request envelope support lands.

API shape:

  • ctx.mcpReq.protocolVersion: string — on BaseContext (request fact, both server and client handlers)
  • ctx.client: { capabilities, info } — on the server context only (peer facts; the client's peer is a server, whose facts come from getServerCapabilities())
  • Capabilities are declarations, not authorization — documented in the JSDoc: this exists so a server doesn't ask a client to do something it can't; it must never gate access to tools/resources/data.

How Has This Been Tested?

  • New unit tests across core (protocol-version seam, pre-init default, both subclass paths), server (ctx.client population and pre-init edge), and client (client-side handler reads the version).
  • 2 new e2e requirements (protocol:envelope:ctx-version-readable, protocol:envelope:ctx-capabilities-readable) with scenarios covering: the negotiated version (including a pinned older version), capabilities/info matching what the client declared, empty-capabilities clients, and a client-side sampling handler reading the version.
  • Full e2e matrix: 0 unexpected failures. All workspace gates clean.

Breaking Changes

None. Additive only.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

  • The protocol-version seam: a protected get negotiatedProtocolVersion() on Protocol, overridden by Server/Client to surface their retained negotiated version; _onrequest centralizes the pre-initialize fallback.
  • The relationship between ctx.client.* and the existing connection-scoped getClientCapabilities()/getClientVersion() is documented in both JSDoc and the e2e manifest; the structural supersession is recorded when 2026 connections become possible (the connection-scoped getters have no meaning without a handshake).
  • Part of the version-negotiation groundwork tracked in Implement SEP-2575: Make MCP Stateless #2184.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 1, 2026

⚠️ No Changeset found

Latest commit: dbab53e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 1, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@2231

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/codemod@2231

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@2231

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server-legacy@2231

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@2231

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/fastify@2231

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@2231

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@2231

commit: dbab53e

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 1, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@2231

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/codemod@2231

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@2231

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@2231

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/fastify@2231

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@2231

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@2231

commit: 968b22a

@felixweinberger felixweinberger force-pushed the fweinberger/m2-ctx-envelope branch 3 times, most recently from 43d0c8a to 2fbd605 Compare June 1, 2026 15:40
Base automatically changed from fweinberger/m2-server-negotiated-version to main June 1, 2026 15:56
@felixweinberger felixweinberger force-pushed the fweinberger/m2-ctx-envelope branch 2 times, most recently from 2f09caf to 5be01e3 Compare June 1, 2026 16:15
@felixweinberger
Copy link
Copy Markdown
Contributor Author

Trimmed 4 of the 7 unit tests after a mutation-based audit (break each behavior, check which layers catch it). Removed: the synthetic-subclass seam test, the capabilities/info passthrough test, the pinned-older-version test (defective — it pinned the SDK default, so an always-emit-default bug passed it), and the {}-capabilities-shape test — every mutation they catch is also caught by the e2e cells across all transports. Kept the 3 unit tests that guard pre-handshake state, which is wire-unobservable: 2 of 7 mutations produced zero e2e failures and only these tests caught them.

@felixweinberger felixweinberger force-pushed the fweinberger/m2-ctx-envelope branch from 4190607 to dbab53e Compare June 1, 2026 16:49
@felixweinberger felixweinberger marked this pull request as ready for review June 1, 2026 18:59
@felixweinberger felixweinberger requested a review from a team as a code owner June 1, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant