Version Packages - #2849
Version Packages#2849
Conversation
|
|
||
| ### Patch Changes | ||
|
|
||
| - [#2597](https://github.com/modelcontextprotocol/typescript-sdk/pull/2597) [`7f7a94c`](https://github.com/modelcontextprotocol/typescript-sdk/commit/7f7a94c22017e121a960e071bb50ec75e34450bd) Thanks [@arimu1](https://github.com/arimu1)! - Treat hostnames ending in `.localhost` as loopback for the SEP-2207 token-endpoint https guard (RFC 6761 §6.3), so host-based multi-tenant local OAuth works. The SDK does not resolve the name itself: `*.localhost` reaches the local machine only if the system resolver follows RFC 6761. |
There was a problem hiding this comment.
🟡 (optional) Consumers reading the 2.1.1 release notes are not told that DCR registration now sends application_type: 'native' for http://*.localhost redirect URIs. The entry at packages/client/CHANGELOG.md:7 scopes the change to the "SEP-2207 token-endpoint https guard", but the same isLoopbackHost helper also drives deriveApplicationType at packages/client/src/client/auth.ts:911, so 2.1.0 sent 'web' and 2.1.1 sends 'native' for those clients. Fix: amend the client 2.1.1 entry (the changeset is already consumed) to state that *.localhost also counts as loopback for the SEP-837 application_type default, and that consumers whose AS enforces OIDC DCR §2 native redirect rules should set clientMetadata.application_type explicitly.
Why this was flagged
A client using dynamic client registration with redirect_uris: ['http://app.localhost:3000/callback'] and no explicit clientMetadata.application_type goes through resolveClientMetadata, which calls deriveApplicationType (packages/client/src/client/auth.ts:902-914). That function returns 'native' whenever isLoopbackHost(url.hostname) is true (auth.ts:911), and isLoopbackHost now also matches hostname.endsWith('.localhost') (auth.ts:870). On 2.1.0 the same redirect URI produced application_type: 'web'; on 2.1.1 the DCR body carries 'native'. The fix commit tests this deliberately (packages/client/test/client/auth.test.ts adds ['http://tenant.example.localhost:3300/callback', 'native']), so it is intended, but the changelog text this PR adds at packages/client/CHANGELOG.md:7 describes only the token-endpoint https guard. An authorization server that applies OIDC DCR §2 (native clients may only register custom-scheme or literal localhost/127.0.0.1/[::1] http redirect URIs) can now reject a registration that 2.1.0 completed, and the operator has no release-note pointer…
Verification: nit — triggered whenever a consumer reads the 2.1.1 client changelog to learn what changed for DCR clients with http://*.localhost redirect URIs. The mechanism is verified: isLoopbackHost (packages/client/src/client/auth.ts:867-876, widened in 7f7a94c to add hostname.endsWith('.localhost')) has two callers — the https guard at auth.ts:883 and deriveApplicationType at auth.ts:911 (`if…
1d41a10 to
61c25ab
Compare
61c25ab to
5b78282
Compare
There was a problem hiding this comment.
LGTM — mechanical changesets release commit; the regenerated version since my last look adds the codemod 2.1.1 entry for #2582 and nothing else.
What was reviewed:
- Version bumps vs .changeset/config.json: core, client, server, server-legacy and codemod are in one
fixedgroup, so core's empty 2.1.1 section and the cascaded "Updated dependencies" entries are expected, not a missing changeset. - CHANGELOG entries match the three consumed changeset files verbatim; the
f091897link in the codemod entry is the actual merge commit of #2582. - All internal
@ modelcontextprotocol/corereferences areworkspace:*/workspace:^, so no manifest range needed editing.
Extended reasoning...
The diff is the changesets-generated release commit 5b78282: it deletes three consumed changeset files, bumps six package.json version fields (client/codemod/core/server/server-legacy 2.1.0 to 2.1.1, core-internal 2.0.1 to 2.0.2) and prepends the matching CHANGELOG sections; no source code, auth, crypto or transport paths are touched. The two points a reader might question are both explained by repository config and history: .changeset/config.json places core, client, server, server-legacy and codemod in a single fixed group (so core is bumped in lockstep with an empty section), and f091897 is the merge commit of #2582 that sits directly under this commit in git log. The change since my prior run is the regeneration that added the codemod entry after #2582 merged; the changelog text is byte-identical to the deleted changeset files. CODEOWNERS only has a repo-wide catch-all plus /src/ auth paths that do not exist in this monorepo layout, and the hunt ran dry with no findings, so a bot-authored mechanical bump like this is the canonical approve case; my earlier optional nit on the client changelog wording remains open in its own thread and does not affect correctness of the release.
Still open from earlier reviews (1):
- Unresolved: 1 minor or pre-existing.
5b78282 to
0be89bd
Compare
0be89bd to
66d13df
Compare
2287fcf to
7a62264
Compare
7a62264 to
f962648
Compare
f962648 to
2e99013
Compare
2e99013 to
b7890f6
Compare
b7890f6 to
51cfd8e
Compare
51cfd8e to
1380783
Compare
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
1380783 to
652a53d
Compare
There was a problem hiding this comment.
I reviewed the regenerated 2.2.0 version of this release PR and found no bugs; since merging it publishes a minor release of the whole fixed group, a human should still confirm the release timing.
What was reviewed:
- All 12 deleted changesets appear exactly once in the right package CHANGELOG, and every cited commit SHA/PR number matches a commit in history since the previous "Version Packages" (9517506); the only commit since then without a changeset is the CODEOWNERS chore (#2884).
- The group-wide 2.1.0 -> 2.2.0 minor is driven solely by expected-issuer-deprecate-omission (client/core minor) through the fixed group in .changeset/config.json; server, server-legacy and codemod ride along per that config.
- Middleware packages (express/hono/node/fastify) correctly stay unbumped: they depend on server via workspace:^ and onlyUpdatePeerDependentsWhenOutOfRange is set, matching how the 2.1.0 release left them.
Extended reasoning...
The diff is a single changesets-generated commit: it deletes 12 .changeset files, adds CHANGELOG sections to client, core, server, server-legacy, codemod and core-internal, and bumps package.json versions (fixed group 2.1.0 -> 2.2.0, core-internal 2.0.1 -> 2.0.2) with no source, dependency-range or middleware version changes. It touches no runtime code, so no injection, auth or data-exposure surface is changed by the diff itself, though the release it publishes ships a new AuthorizationServerMismatchError throw in fetchToken() as a minor. I verified every changeset-to-changelog mapping, commit SHA and bump level against .changeset/config.json and the git history, and found them consistent. Defer rather than approve because the repo's catch-all CODEOWNERS covers these paths and merging is itself the release decision, which belongs to a maintainer; the previous felixweinberger review was dismissed and no objection remains outstanding.
Still open from earlier reviews (1):
- Unresolved: 1 minor or pre-existing.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@modelcontextprotocol/client@2.2.0
Minor Changes
#2887
edd12e2Thanks @maxisbey! - ConstructingClientCredentialsProvider,PrivateKeyJwtProvider,StaticPrivateKeyJwtProviderorCrossAppAccessProviderwithoutexpectedIssueris deprecated: the constructor logs oneconsole.warnand that call signature is marked@deprecated. Behaviour is otherwise unchanged. Pass theissuerof the authorization server the credentials were registered with.fetchToken()throwsAuthorizationServerMismatchError, before sending anything, when the provider's client information is bound to a different authorization server than the one it is called with. TheAuthorizationServerMismatchErrormessage no longer assumes the authorization-code callback; its fields are unchanged.OAuthTokensSchemaandOAuthClientInformationSchemaaccept the optionalissuerstamp, so a provider that reads storage back through them keeps it.auth()overwrites it on every save.Patch Changes
#2885
9dd722fThanks @claude! - Sending a notification on a closed connection no longer produces a briefly unhandled promise rejection (seen asunhandledrejectionon Cloudflare Workers) in addition to the returned rejection.#2883
c0f7aecThanks @claude! - Fix a type-check failure for CommonJS TypeScript projects introduced in 2.1.0:dist/index.d.ctsimported types fromjose, which is ESM-only, sotscwithmodule: node16/node18andskipLibCheck: falsefailed with TS1479. The twojosetypes used by the DPoP API (CryptoKey,JWK) are now inlined into the declaration files. No runtime change.#2768
efebf5bThanks @web-abin! - Correct the JSDoc for insecure OAuth token endpoints. The TLS requirement comes from the MCP authorization specification's OAuth 2.1 communication-security rules, not SEP-2207, which covers OIDC-flavored refresh-token guidance. Documentation only; no runtime behavior change.#2729
a4ae2f9Thanks @claude! - Correct theregisterClient@deprecatednotice: Dynamic Client Registration was deprecated by spec PR fix(spec): freeze 2026-07-28 release references #2858 (Client ID Metadata Documents), not SEP-2577 (which deprecates roots, sampling, and logging). The notice now also names the earliest possible removal date under the feature lifecycle policy (2027-07-28) and clarifies that theclient_id_metadata_document_supportedgating lives in the built-inauth()flow —registerClientcalled directly always sends the registration request. Documentation only; no runtime behavior change.#2862
e780e13Thanks @SyedTashfin! - Preserve_metaoninput_requiredresults. The 2026-07-28 decode seam rebuilt the payload frominputRequestsandrequestStateonly, so result-level metadata a server sent on aninput_requiredresult (includingio.modelcontextprotocol/serverInfo) was dropped before anallowInputRequired: truecaller could see it.Result._metais a result-level field, soinput_requiredcarries it exactly like any other result.#2886
ef39308Thanks @claude! -listTools(),listPrompts(),listResources()andlistResourceTemplates()called without a cursor now follownextCursoruntil the server stops sending one, instead of stopping silently with a short list when a cursor repeats; a page that has the same items and the samenextCursoras the page before it ends the walk and is not added twice, andlistMaxPagesstill caps the walk.#2642
cfa09dbThanks @claude! - FixClient.listen()rejections escaping as process-level unhandled rejections. The internalopeningpromise could reject (ack timeout, transport close, server cancel, caller abort) whilelisten()was still serially awaitingtransport.send(...), so no rejection handler was attached yet — the rejection surfaced as anunhandledRejectionthat caller-side handling cannot prevent, and a send that never settles (e.g. a stdio write parked on'drain') leftlisten()suspended forever even though the ack timer had already fired.listen()now suspends on theopeningstate machine directly and routes send failures into it, so every termination path rejects the returned promise and nothing escapes.#2597
7f7a94cThanks @arimu1! - Treat hostnames ending in.localhostas loopback for the SEP-2207 token-endpoint https guard (RFC 6761 §6.3), so host-based multi-tenant local OAuth works. The SDK does not resolve the name itself:*.localhostreaches the local machine only if the system resolver follows RFC 6761.Updated dependencies [
edd12e2]:@modelcontextprotocol/core@2.2.0
Minor Changes
#2887
edd12e2Thanks @maxisbey! - ConstructingClientCredentialsProvider,PrivateKeyJwtProvider,StaticPrivateKeyJwtProviderorCrossAppAccessProviderwithoutexpectedIssueris deprecated: the constructor logs oneconsole.warnand that call signature is marked@deprecated. Behaviour is otherwise unchanged. Pass theissuerof the authorization server the credentials were registered with.fetchToken()throwsAuthorizationServerMismatchError, before sending anything, when the provider's client information is bound to a different authorization server than the one it is called with. TheAuthorizationServerMismatchErrormessage no longer assumes the authorization-code callback; its fields are unchanged.OAuthTokensSchemaandOAuthClientInformationSchemaaccept the optionalissuerstamp, so a provider that reads storage back through them keeps it.auth()overwrites it on every save.@modelcontextprotocol/codemod@2.2.0
Patch Changes
f091897Thanks @axits-lab! - Thev1-to-v2codemod now writes rewritten imports where the first v1 import stood, not at the top of the file, so a license header,// @ts-nocheck,/// <reference>or a'use client'/'use server'/'use strict'directive above it stays in place. Known gap: when a later step of the codemod replaces or removes the import (for example a file whose only SDK import isErrorCodeorStreamableHTTPError), the new import can still land above or inside the header, and a/** */header can be removed. Files already migrated with codemod 2.1.0 or earlier are not repaired; check the top of those files.@modelcontextprotocol/server@2.2.0
Patch Changes
#2885
9dd722fThanks @claude! - Sending a notification on a closed connection no longer produces a briefly unhandled promise rejection (seen asunhandledrejectionon Cloudflare Workers) in addition to the returned rejection.#2778
e3fb9edThanks @vjymisal0! - Fix a stack overflow increateMcpHandlerwhen the factory returns the same server instance for more than one request. Returning a fresh instance per request is still required.#2651
c55efa6Thanks @sushantkumar23! -createMcpHandlernow ends asubscriptions/listenstream right after the acknowledgement when it honored none of the requested notification types, instead of holding the stream open with nothing to deliver. The client receives the acknowledgement and then theresultType: "complete"result. Streams that honor at least one type are unchanged.Updated dependencies [
edd12e2]:@modelcontextprotocol/server-legacy@2.2.0
Patch Changes
edd12e2]:@modelcontextprotocol/core-internal@2.0.2
Patch Changes
edd12e2]: