refactor!: extract LegacyServer/LegacyClient; NEW Server/Client compose them#2134
Draft
felixweinberger wants to merge 5 commits into
Draft
refactor!: extract LegacyServer/LegacyClient; NEW Server/Client compose them#2134felixweinberger wants to merge 5 commits into
felixweinberger wants to merge 5 commits into
Conversation
git mv server.ts legacyServer.ts + class rename. Zero body changes. Importers updated to `LegacyServer as Server` so behavior is identical and the rename shows as a true file rename in history.
…rver `Server` no longer extends `Protocol`. It owns the 2026 stateless dispatch path (subscriptions, statelessHandlers, _dispatchStateless, _buildDispatchServerContext, _ondiscover) and the dual-mode surface (connect/close/transport/onclose/onerror, _fanoutNotify, send*ListChanged), lifted verbatim from the sectioned `LegacyServer` with field reads re-pointed at `this.config.*` / `this._legacy.*`. `LegacyServer` keeps the session-dependent block. Both share one handler registry via `_legacy._dispatch`/`_legacy.setRequestHandler`. `server.legacy` is the explicit escape hatch. LegacyServer body changes are limited to: - `_assertSession()` guard inserted at top of createMessage / elicitInput / listRoots / sendLoggingMessage / ping - ctor: dropped `subscriptions` init + `server/discover` registration (lifted to NEW Server) Adds `SdkErrorCode.SessionRequired`.
git mv client.ts legacyClient.ts + class rename. Zero body changes. Importers updated to `LegacyClient as Client` so behavior is identical and the rename shows as a true file rename in history.
…ient `Client` no longer extends `Protocol`. It owns the 2026 stateless send path (_isStateless/_buildMeta/_withMeta/_collect/_send/_negotiate/ subscribe/_listChangedLoop) and the typed request methods (callTool/listTools/getPrompt/listPrompts/readResource/listResources/ listResourceTemplates/complete/setLoggingLevel), lifted verbatim from the sectioned `LegacyClient` with field reads re-pointed at `this.config.*` / `this._legacy.*`. `LegacyClient` keeps the session-dependent block. Both share one handler registry via `_legacy._dispatch`/`_legacy.setRequestHandler`; negotiated server state lives on `_legacy` (single source of truth) and is written by both `_negotiate` (via `_setNegotiated`) and `_initialize`. `client.legacy` is the explicit escape hatch. LegacyClient body changes are limited to: - `_assertSession()` guard inserted at top of ping / subscribeResource / unsubscribeResource / sendRootsListChanged - ctor: dropped now-unused `_enforceStrictCapabilities` / `_pendingListChangedConfig` / `_cachedToolOutputValidators` fields (lifted to NEW Client) - `_initialize` / `_setupListChangedHandler` visibility widened to @internal
Integration tests that exercise the pre-2026 session-dependent surface (server.createMessage / elicitInput / listRoots / sendLoggingMessage / ping / getClientCapabilities / getClientVersion / oninitialized / createElicitationCompletionNotifier / notification / request / setNotificationHandler; client.sendRootsListChanged / ping / subscribeResource / unsubscribeResource / request / notification) now go through `.legacy`. LegacyTestClient unchanged: it extends NEW Client and pins versions, so `connect()` skips the discover probe exactly as before. clientSend statelessClient() helper updated to set `_transport` / negotiated state on `c.legacy` (private state moved there).
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
This was referenced May 20, 2026
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.
Extracts
LegacyServer/LegacyClient(theProtocol-derived, session-stateful classes) and introduces NEWServer/Clientthat own the 2026 stateless path and compose the legacy class via.legacy.Motivation and Context
After #2131 the sectioned
Server/Clientcarry both the 2026 stateless surface and the pre-2026 session-dependent surface side by side. That works, but nothing in the type system tells a caller which methods are session-only. This PR makes the boundary structural:Server/Clientno longer extendProtocol. They expose the 2026 stateless surface plus the version-agnostic typed request methods.createMessage/elicitInput/listRoots/sendLoggingMessage/pingon the server;subscribeResource/unsubscribeResource/sendRootsListChanged/pingon the client; rawrequest/notification/setNotificationHandleron both) live only onLegacyServer/LegacyClient, reachable via the explicit.legacyescape hatch._assertSession()guards the legacy methods at runtime and throwsSdkErrorCode.SessionRequiredwith a migration URL when called on a 2026 connection.Both classes share one handler registry and negotiated state (single source of truth on the legacy instance), so behavior is identical to the sectioned base; this PR just moves the seam into the type system.
How Has This Been Tested?
pnpm typecheck:all && lint:fix:all && build:all && test:all && docs:checkall green. Integration tests updated to call session-dependent methods through.legacy.Breaking Changes
Server/Clientno longer extendProtocol. Session-dependent methods move to.legacy.*. Seedocs/migration.md.Types of changes
Checklist
Additional context
Reviewable per-commit:
refactor(server): rename Server -> LegacyServer (verbatim)—git mv+ class rename, zero body changesrefactor(server)!: NEW Server class (Protocol-free) composes LegacyServerrefactor(client): rename Client -> LegacyClient (verbatim)—git mv+ class rename, zero body changesrefactor(client)!: NEW Client class (Protocol-free) composes LegacyClienttest: adapt tests to .legacy getter for session-dependent methods