@modelcontextprotocol/[email protected]:弃用无 issuer 构造器
@modelcontextprotocol/[email protected]
推荐理由
v2.2.0 引入了 OAuth 构造器的弃用警告和 listTools 分页逻辑的关键变更,直接影响 MCP 客户端的鉴权配置与数据获取完整性。依赖此 SDK 的开发者需立即检查 Provider 初始化代码并调整分页处理逻辑,避免后续版本升级导致运行时错误或静默数据丢失。
Minor Changes
次要更改
- #2887 edd12e2 Thanks @maxisbey! - Constructing ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider or CrossAppAccessProvider without expectedIssuer is deprecated: the constructor logs one console.warn and that call signature is marked @deprecated. Behaviour is otherwise unchanged. Pass the issuer of the authorization server the credentials were registered with.
- fetchToken() throws AuthorizationServerMismatchError, before sending anything, when the provider's client information is bound to a different authorization server than the one it is called with. The AuthorizationServerMismatchError message no longer assumes the authorization-code callback; its fields are unchanged.
- OAuthTokensSchema and OAuthClientInformationSchema accept the optional issuer stamp, so a provider that reads storage back through them keeps it. auth() overwrites it on every save.
- #2887 edd12e2 感谢 @maxisbey!- 在不提供 expectedIssuer 的情况下构造 ClientCredentialsProvider、PrivateKeyJwtProvider、StaticPrivateKeyJwtProvider 或 CrossAppAccessProvider 已被弃用:构造函数会记录一条 console.warn,且该调用签名已标记为 @deprecated。其他行为保持不变。请传入凭证注册时所使用的授权服务器的 issuer。
- 当提供者的客户端信息绑定的授权服务器与调用时指定的授权服务器不同时,fetchToken() 会在发送任何内容之前抛出 AuthorizationServerMismatchError。AuthorizationServerMismatchError 的消息不再假设使用授权码回调;其字段保持不变。
- OAuthTokensSchema 和 OAuthClientInformationSchema 接受可选的 issuer 时间戳,因此通过它们读取存储的提供者会保留它。auth() 在每次保存时都会覆盖它。
Patch Changes
补丁更改
- #2885 9dd722f Thanks @claude! - Sending a notification on a closed connection no longer produces a briefly unhandled promise rejection (seen as unhandledrejection on Cloudflare Workers) in addition to the returned rejection.
- #2883 c0f7aec Thanks @claude! - Fix a type-check failure for CommonJS TypeScript projects introduced in 2.1.0: dist/index.d.cts imported types from jose, which is ESM-only, so tsc with module: node16/node18 and skipLibCheck: false failed with TS1479. The two jose types used by the DPoP API (CryptoKey, JWK) are now inlined into the declaration files. No runtime change.
- #2768 efebf5b Thanks @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 a4ae2f9 Thanks @claude! - Correct the registerClient @deprecated notice: Dynamic Client Registration was deprecated by spec PR #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 the client_id_metadata_document_supported gating lives in the built-in auth() flow — registerClient called directly always sends the registration request. Documentation only; no runtime behavior change.
- #2862 e780e13 Thanks @SyedTashfin! - Preserve _meta on input_required results. The 2026-07-28 decode seam rebuilt the payload from inputRequests and requestState only, so result-level metadata a server sent on an input_required result (including io.modelcontextprotocol/serverInfo) was dropped before an allowInputRequired: true caller could see it. Result._meta is a result-level field, so input_required carries it exactly like any other result.
- #2886 ef39308 Thanks @claude! - listTools(), listPrompts(), listResources() and listResourceTemplates() called without a cursor now follow nextCursor until 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 same nextCursor as the page before it ends the walk and is not added twice, and listMaxPages still caps the walk.
- #2642 cfa09db Thanks @claude! - Fix Client.listen() rejections escaping as process-level unhandled rejections. The internal opening promise could reject (ack timeout, transport close, server cancel, caller abort) while listen() was still serially awaiting transport.send(...), so no rejection handler was attached yet — the rejection surfaced as an unhandledRejection that caller-side handling cannot prevent, and a send that never settles (e.g. a stdio write parked on 'drain') left listen() suspended forever even though the ack timer had already fired. listen() now suspends on the opening state machine directly and routes send failures into it, so every termination path rejects the returned promise and nothing escapes.
- #2597 7f7a94c Thanks @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.
- Updated dependencies [edd12e2]:
- @modelcontextprotocol/[email protected]
- #2885 9dd722f 感谢 @claude!- 在已关闭的连接上发送通知不再会产生短暂的未处理 Promise 拒绝(在 Cloudflare Workers 中显示为 unhandledrejection),除了返回的拒绝之外。
- #2883 c0f7aec 感谢 @claude!- 修复 2.1.0 版本中引入的 CommonJS TypeScript 项目的类型检查失败:dist/index.d.cts 从 jose 导入类型,而 jose 仅支持 ESM,因此在使用 module: node16/node18 和 skipLibCheck: false 时,tsc 因 TS1479 而失败。DPoP API 使用的两个 jose 类型(CryptoKey、JWK)现已内联到声明文件中。无运行时更改。
- #2768 efebf5b 感谢 @web-abin!- 更正不安全 OAuth 令牌端点的 JSDoc。TLS 要求来自 MCP 授权规范的 OAuth 2.1 通信安全规则,而非 SEP-2207(后者涵盖 OIDC 风格的刷新令牌指南)。仅为文档更改;无运行时行为更改。
- #2729 a4ae2f9 感谢 @claude!- 更正 registerClient 的 @deprecated 通知:动态客户端注册已由规范 PR #2858(客户端 ID 元数据文档)弃用,而非 SEP-2577(后者弃用根目录、采样和日志记录)。通知现在还列出了根据功能生命周期政策的最早可能移除日期(2027-07-28),并澄清 client_id_metadata_document_supported 的门控逻辑位于内置 auth() 流程中——直接调用 registerClient 始终会发送注册请求。仅为文档更改;无运行时行为更改。
- #2862 e780e13 感谢 @SyedTashfin!- 在 input_required 结果上保留 _meta。2026-07-28 的 decode seam 仅从 inputRequests 和 requestState 重建 payload,因此服务器在 input_required 结果上发送的结果级元数据(包括 io.modelcontextprotocol/serverInfo)在 allowInputRequired: true 的调用者看到之前就被丢弃了。Result._meta 是一个结果级字段,因此 input_required 会像其他任何结果一样原样携带它。
- #2886 ef39308 感谢 @claude!- 不带 cursor 调用的 listTools()、listPrompts()、listResources() 和 listResourceTemplates() 现在会遵循 nextCursor 直到服务器停止发送,而不是在 cursor 重复时静默停止并返回短列表;如果某页包含与前一页相同的项且 nextCursor 也相同,则结束遍历且不重复添加该页,同时 listMaxPages 仍限制遍历次数。
- #2642 cfa09db 感谢 @claude!- 修复 Client.listen() 拒绝被提升为进程级未处理拒绝的问题。内部打开 promise 可能在 listen() 仍在串行等待 transport.send(...) 时发生拒绝(确认超时、传输关闭、服务器取消、调用者中止),此时尚未附加拒绝处理器——该拒绝表现为 caller 侧处理无法阻止的 unhandledRejection,而一个永远无法结算的 send(例如阻塞在 'drain' 上的 stdio 写入)导致 listen() 永远挂起,尽管确认计时器已经触发。listen() 现在直接在打开状态机上挂起,并将 send 失败路由到其中,因此每条终止路径都会拒绝返回的 promise,没有任何内容逃逸。
- #2597 7f7a94c 感谢 @arimu1!- 将主机名以 .localhost 结尾的情况视为环回地址,用于 SEP-2207 token-endpoint https 守卫(RFC 6761 §6.3),从而支持基于主机的多租户本地 OAuth。SDK 本身不解析名称:*.localhost 仅在系统解析器遵循 RFC 6761 时才能到达本地机器。
- 更新依赖项 [edd12e2]:
- @modelcontextprotocol/[email protected]
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力