@modelcontextprotocol/client 2.0 版本演进全解读:从 v1 单包到多包 SDK 的关键变更与迁移指南
2026/9/15 21:50:54 网站建设 项目流程

@modelcontextprotocol/client 2.0 版本演进全解读:从 v1 单包到多包 SDK 的关键变更与迁移指南

【免费下载链接】typescript-sdkThe official TypeScript SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-sdk

导读

packages/client/CHANGELOG.md完整记录了@modelcontextprotocol/client从 2.0.0-alpha.0 到 2.0.0 稳定版的全部演进历程。作为 MCP(Model Context Protocol)TypeScript SDK v2 的客户端包,它承载了协议版本协商、2026-07-28 规范修订支持、响应缓存、OAuth 增强、跨包错误品牌化等重大能力升级。阅读本文,你将系统掌握 v2 客户端的架构变化、每个版本的功能细节、破坏性变更,以及从 v1 迁移时需要关注的全部要点。

一、版本发布脉络:从 alpha 到稳定版

@modelcontextprotocol/client的 2.0.0 经历了完整的预发布链条,每个阶段都有明确的技术主题:

  • 2.0.0-alpha.0/alpha.1:客户端与服务器包的初始拆分,移除非规范传输(WebSocket),新增 OAuth 发现与AuthProvider
  • 2.0.0-alpha.2:任务编排重构、标准 Schema 支持;
  • 2.0.0-alpha.3:移除 2025-11 实验性任务(TaskManager),转向 Extensions Track,新增SdkHttpError与自定义方法支持;
  • 2.0.0-alpha.4:协议版本协商、响应缓存、2026-07-28 wire 对齐等大特性集中落地;
  • 2.0.0-beta.1 ~ beta.5:CommonJS 构建、Content-Type 严格校验、错误品牌化、PriorDiscovery缓存判定等收尾工作;
  • 2.0.0(稳定版):正式支持 MCP 2026-07-28 规范修订(见 迁移指南 与 2026-07-28 修订支持指南)。
npm install @modelcontextprotocol/client

注意:TypeScript ≥ 6.0 不再自动包含@types/*,由于发布声明文件引用了Buffer,需要在tsconfig.jsoncompilerOptions中显式添加"types": ["node"]

二、协议版本协商(Version Negotiation)与时代探测

2.1 三种模式:legacy / auto / pin

v2 客户端引入可选的协议版本协商,默认行为保持 v1 完全一致。核心配置在ClientOptions.versionNegotiation

  • mode: 'legacy'(默认):执行与 v1.x 逐字节相同的 2025 连接序列,不探测、不发新请求头;
  • mode: 'auto':连接时先用server/discover探测服务器,拿到明确现代证据则进入现代时代(2026-07-28+),否则保守回退到传统initialize握手;
  • mode: { pin: '2026-07-28' }:精确协商到固定修订版本,不回退,任何不匹配都响亮失败。

探测策略位于probe: { timeoutMs?, maxRetries? },超时默认继承标准请求超时;maxRetries(默认 0)只控制超时重发,规范强制的-32022纠正性续接不计入重试次数。实现细节见 versionNegotiation.ts 中的resolveVersionNegotiationnegotiateEra

2.2 传输感知的探测结果判定

探测结果由纯函数分类器 probeClassifier.ts 的classifyProbeOutcome映射为四类判定:现代时代、-32022纠正续接、传统回退、类型化连接错误。关键语义:

  • stdio 超时/子进程退出= 传统服务器信号(如基于官方 Rust SDK、rmcp 构建的服务器会在任何 pre-initialize 请求时退出),回退到initialize
  • HTTP 超时= 部署中服务器的沉默是故障而非传统信号,抛出类型化SdkError(RequestTimeout)
  • 401/403= 认证状态从来不是时代证据,以SdkHttpErrorClientHttpAuthentication/ClientHttpForbidden)类型化拒绝,绝不触发传统回退;
  • 网络中断= 类型化连接错误,不转换为时代判定。

一个值得注意的细节:SDK 自带的 stdio 传输在一次性兄弟进程上运行探测(stderr 丢弃、探测后回收),调用方传输只启动一次;自定义 stdio 形状传输则原地探测。探测窗口会保存调用方预设的onmessage/onerror/onclose,在探测期间转发错误与关闭事件,结束后恢复——避免连接前设置的处理函数被静默清除。

2.3connect({ prior }):零往返重连

2.0.0 新增ConnectOptions.prior,接受新导出类型PriorDiscovery

  • { kind: 'modern', discover }:直接采用先前获得的DiscoverResult,零往返连接,callTool()立即可用;
  • { kind: 'legacy' }:跳过探测直接执行传统initialize握手,适合已知为传统服务器的场景,且不把客户端钉死在mode: 'legacy'——停止提供判定后connect()会回退到配置的协商模式。

新鲜度由提供方负责:过期的传统判定对已升级服务器会静默成功,宿主必须在自己的存储中记录缓存判定的日期并在策略时限后停止提供。持久化 blob 通道已加固:prior: null视为缺失、modern 分支的discover载荷在状态变更前做 schema 校验、无法识别的形状抛出类型化SdkError(EraNegotiationFailed)而非TypeError(见 client.ts 的validatePrior)。配套的client.getDiscoverResult()使网关可以探测一次、持久化 blob、喂给每个 worker——该值可安全经过JSON.stringify往返。相关测试见 probeClassifier.test.ts 与 versionNegotiation.test.ts。

三、2026-07-28 Wire 对齐与破坏性类型变更

3.1serverInfo迁入_meta(spec PR #3002)

2026-07-28 wire 与规范最终修订对齐后:

  • DiscoverResult不再声明 body 上的serverInfo,服务器改为在每个 2026 时代响应上盖章_meta['io.modelcontextprotocol/serverInfo'](spec SHOULD;处理函数自写的值优先);
  • 每请求信封的clientInfo从必选降级为 SHOULD(present-but-malformed 仍校验失败);
  • 客户端只从 discover 结果的_meta读取服务器身份;未盖章身份的服务器是匿名的,getServerVersion()返回undefined,响应缓存按每连接代理分区;
  • 新公共常量SERVER_INFO_META_KEY'io.modelcontextprotocol/serverInfo')。

修复前,客户端会硬拒绝符合规范的DiscoverResult(缺 bodyserverInfo导致解析失败、探测误判为传统服务器),从而对 go-sdk v1.7.0-pre.3 这类纯现代服务器产生硬连接失败——这正是本修订要消除的互操作问题。

3.2 错误码重编号与协议错误对齐

2026-07-28 协议错误码按规范重新编号(仅影响该草稿修订,2025 时代与 SDK 惯用的-32001不受影响):

  • HeaderMismatch-32001-32020
  • MissingRequiredClientCapability-32003-32021
  • UnsupportedProtocolVersion-32004-32022

配套新增类型化错误类:MissingRequiredClientCapabilityError(2026 HTTP 入口在分发前拒绝未声明客户端能力的请求,返回 HTTP 400)与UnsupportedProtocolVersionErrorProtocolError.fromError均能识别。新增SdkErrorCode.MethodNotSupportedByProtocolVersion:向协商时代未定义该方法的对端发送(如向 2026-07-28 对端发tasks/get)时,在触达传输前就本地抛出。

3.3 会话 ID 语义收紧

Streamable HTTP 客户端传输不再给包含initialize请求的 POST 附加会话 ID(新会话"不带会话 ID 开始"),且只从成功的 initialize 响应的mcp-session-id响应头捕获会话 ID。这修复了一个实际缺陷:传统服务器用带会话 ID 的错误响应回答版本探测时,会污染回退的 initialize 握手。会话轮换的唯一合法机制是 404 + 重新初始化(规范"MAY terminate the session at any time"),而非活动会话上的头交换。

3.4Content-Type严格校验

POST 的Content-Type媒体类型不是application/json时统一拒绝415 Unsupported Media Type,由子串匹配改为解析媒体类型。application/json; charset=utf-8(含application/json;这类畸形参数段)继续可用。新导出的isJsonContentType(header)助手供传输与框架适配器作者使用——组合导出构件(classifyInboundRequestPerRequestHTTPServerTransport)的自定义入口必须自行应用。

四、响应缓存:JSON 文档存储与 SEP-2549 缓存提示

4.1 存储格式变更(破坏性)

响应缓存从structuredClone隔离的活动对象图改为JSON 序列化文档(写时序列化、读时解析)。同样的变更隔离,但不再依赖structuredClone全局——它在 jest+jsdom、Node < 17 下缺失,曾导致每次缓存写都抛入 store-error 吞噬、静默禁用会话的缓存与输出 schema 查找。无 JSON 表示的值现在响亮失败到错误汇,外部 store 中不可解码的文档被报告、丢弃并按未命中读取。

自定义ResponseCacheStore的迁移CacheEntry.value(及set()的条目值)现在是string——原样持久化与返回,用JSON.parse检查。旧 SDK 版本持久化的条目失败解码一次(报告、丢弃),下次 fetch 时重写。相关测试见 responseCache.test.ts 与 responseCacheCodec.test.ts。

4.2 缓存提示(SEP-2549)与三态cacheMode

客户端现在遵循服务器盖章的ttlMs/cacheScope缓存提示(listTools()listPrompts()listResources()listResourceTemplates()readResource()):仍然新鲜的持有条目零往返直接返回。新增CacheableRequestOptions.cacheMode

  • 'use'(默认):新鲜条目直接服务;
  • 'refresh':总是抓取并重新存储;
  • 'bypass':抓取但不查询也不写入缓存。

行为是提示驱动的 opt-in:发送ttlMs: 0(本 SDK 服务器盖章的保守默认)的服务器看到逐字节相同的行为——每次调用都抓取。服务器提供的ttlMs上限钳制为 24 小时(MAX_CACHE_TTL_MS),一个服务器无法把条目钉死到无限期。

4.3 可插拔 store 与分区隔离

新导出的ResponseCacheStoreCacheKeyCacheEntryCacheScopeMaybePromiseInMemoryResponseCacheStore构成完整的缓存扩展面。条目自动按已连接服务器身份分区(serverInfoJSON.stringify无冲突编码),'private'作用域的条目叠加ClientOptions.cachePartition(设为你的主体标识符,如认证 subject)。分区编码JSON.stringify([serverIdentity, principal])从构造上防冲突:恶意服务器无法用serverInfo.name/version构造字符串渗入其他服务器的命名空间或主体的槽位。notifications/resources/updated按 URI 逐条驱逐resources/read缓存,list_changed通知驱逐对应列表。InMemoryResponseCacheStore现为有界存储({ maxEntries },默认 512,最旧优先驱逐;resources/read键空间计入上限,列表单例方法豁免)。

4.4 输出 schema 验证生命周期变化

所有时代的行为变更:输出 schema 验证器编译改为惰性——在首次callTool()时针对缓存的tools/list条目编译,而非在listTools()内急切编译。listTools()不再因不可编译的outputSchema抛错(每个工具保持列出,编译失败按工具捕获);对受影响工具调用callTool()会在请求发出前抛ProtocolError(InvalidParams, "Tool 'X' has an invalid outputSchema: …")——输出 schema 验证从不静默跳过。可插拔jsonSchemaValidator提供方因此观察到的是callTool时的编译,而非listTools时。

五、跨包错误品牌化:instanceof跨 bundle 生效

SDK 错误类(ProtocolError及其类型化子类、SdkError/SdkHttpErrorOAuthError,以及客户端的SseErrorUnauthorizedError和 OAuth 客户端流错误家族)的instanceof现在可跨独立打包的 SDK 副本工作。类通过稳定品牌匹配(Symbol.hasInstance+ 注册表符号)而非原型同一性:同时使用@modelcontextprotocol/client@modelcontextprotocol/server的进程(网关、宿主或进程内测试)可以拿任一包构造的错误去对照另一包再导出的类做检查。要点:

  • 跨 bundle 匹配要求两份副本都处于本版本或之后;
  • 品牌断言的是身份而非字段形状——跨版本读取字段要保持防御性;
  • 附带效果:外部 bundle 的SdkError用作中止原因时原样重抛,不再包装为RequestTimeout
  • 品牌化层级额外暴露静态守卫X.isInstance(value),读取同一品牌并在 TypeScript 中收窄类型;
  • UnauthorizedError现在把error.name设为'UnauthorizedError'(此前为'Error');
  • 版本协商探测现在识别UnauthorizedError并原样传播:对认证门控服务器connect()以原始UnauthorizedError拒绝(此前包装为SdkError(EraNegotiationFailed)cause)——运行finishAuth()后重连,重试探测携带 token。

六、多轮请求:input_required自动完成

2026-07-28 时代,服务器通过回答tools/callprompts/getresources/readinput_required结果来获取客户端输入(elicitation、sampling、roots),而非发送服务器→客户端请求。客户端默认自动完成这些内嵌请求:分发给已注册的 elicitation/sampling/roots 处理器,然后用收集到的inputResponses、不透明requestState的逐字节回显和新请求 id 重试原调用,最多inputRequired.maxRounds轮(默认 10;耗尽抛类型化InputRequiredRoundsExceededError并携带最后结果)。client.callTool()及其兄弟方法继续返回普通结果类型。

ClientOptions.inputRequiredautoFulfillmaxRounds)配置驱动;手动模式是autoFulfill: false加按调用的allowInputRequired: true请求选项与withInputRequired()schema 包装器。2025 时代行为不变——传统 wire 没有input_required词汇。

七、列表自动聚合、懒加载与运行时支持

7.1 列表方法自动聚合分页

Client.listTools()/listPrompts()/listResources()/listResourceTemplates()在无cursor调用时自动聚合每一页并返回完整结果(nextCursor: undefined),与 C#、Java 和 mcp.d SDK 对齐。传显式{ cursor }字符串则单页抓取。聚合结果写入可插拔ResponseCacheStoreClientOptions.listMaxPages(默认 64)封顶自动聚合遍历,超限抛SdkError(ListPaginationExceeded),部分聚合结果永不入缓存。

7.2 懒加载与边缘运行时

  • Ajv 引擎惰性构造:创建Client/Server不再在启动时支付 ajv + ajv-formats 实例化成本;
  • Wire schema 惰性构建:各修订 schema 集由模块级记忆化工厂构建,导入包不再预先支付两份冻结 wire-schema 图;
  • preloadSchemas():显式 opt-in 急切构建 wire schema,同步且幂等;Cloudflare Workers 构建自动调用(isolate 平台按请求 CPU 计费,模块求值不收费),Node 与浏览器构建保持惰性,服务器包为此获得独立浏览器 shim。

7.3 模块系统与打包

  • 每个包同时产出 ESM 与 CommonJS(tsdownformat: ['esm', 'cjs']),exportsmap 增加require条件,require('@modelcontextprotocol/…')在 CJS 消费者中可用;
  • stdio 传输移至./stdio子路径导出(StdioClientTransportgetDefaultEnvironmentDEFAULT_INHERITED_ENV_VARSStdioServerParameters),根入口不再拉入node:child_processnode:streamcross-spawn,修复浏览器与 Cloudflare Workers 的打包;
  • 验证器提供方类从根类型声明撤下,仅通过显式子路径提供:@modelcontextprotocol/client/validators/ajvvalidators/cf-worker;默认验证器通过运行时 shim 按环境自动选择(Node 用 AJV、浏览器/workerd 用@cfworker/json-schema),根入口 chunk 不携带验证器依赖。

7.4 方言支持与 schema 生态

默认验证器现在尊重声明的 2019-09 与 draft-07/06 方言而非拒绝:"$schema": "http://json-schema.org/draft-07/schema#"(zod-to-json-schema 默认输出)按 draft-07 语义校验,2019-09 盖章按 2019-09 语义。无$schema的 schema 仍按 2020-12 校验,未知方言产生类型化错误(列出受支持方言:2020-12、2019-09、draft-07、draft-06)。

八、OAuth 与认证安全增强

  • RFC 9207 / RFC 8414 §3.3 issuer 校验discoverAuthorizationServerMetadata()拒绝 issuer 与发现 URL 不匹配的元数据(可通过skipIssuerValidation/AuthOptions.skipIssuerMetadataValidation关闭——会削弱安全);auth()exchangeAuthorization()fetchToken()transport.finishAuth(code, iss?)在赎回 code 前校验回调iss
  • 按授权服务器隔离凭据(SEP-2352):auth()在传给saveTokens()/saveClientInformation()的每个值上盖章issuer;读取时盖章指向其他授权服务器的凭据视为undefined,一个 AS 签发的client_id/refresh_token绝不会发给另一个 AS;
  • 非 https token 端点防护:token 交换、刷新与 Cross-App Access 路径对非https:端点抛InsecureTokenEndpointErrorlocalhost/127.0.0.1/::1豁免);
  • scope 步进升级(SEP-2350):403 insufficient_scope时用先前请求与挑战 scope 的并集重新授权(onInsufficientScope: 'reauthorize' | 'throw',默认'reauthorize'maxStepUpRetries默认 1),'throw'InsufficientScopeError
  • AuthProvider:可组合的 bearer-token 认证接口{ token(): Promise<string | undefined>; onUnauthorized?(ctx): Promise<void> };传输的authProvider选项现在接受AuthProvider | OAuthClientProvider,OAuth 提供方经adaptOAuthProvider()自动适配。简单 bearer token 只需一行对象字面量:{ authProvider: { token: async () => myKey } }
  • DPoP(RFC 9449 / SEP-1932)DpopSession与密钥对原语、generateDpopKeyPairaccessTokenHash等导出,可将 sender-constrained token 接入完整 OAuth+DPoP 流。

九、其他值得注意的变更

  • CallToolResult.content解析容错恢复:v1 的解析容错被恢复——入站传统时代tools/call结果缺content默认[]而非校验失败;2026 时代 wire schema 保持严格。服务端作者端时代无关:无content的结果在时代校验前规范化为content: []
  • 自定义方法(3 参setRequestHandler(method, schemas, handler))与request(req, resultSchema)重载,支持厂商前缀方法;结果 schema 校验失败以SdkError(InvalidResult)拒绝而非裸ZodError
  • 标准 Schema 支持:工具与提示注册接受任何实现 Standard Schema 的库(Zod v4、Valibot、ArkType 等),RegisteredTool.inputSchema/outputSchemaRegisteredPrompt.argsSchema使用StandardSchemaWithJSON;原始 JSON Schema 用fromJsonSchema适配器接入;zodpeerDependencies移入直接依赖,自动安装;
  • subscriptions/listen与优雅关闭:现代连接上Client.listen(filter)打开订阅流,McpSubscription.closed解析'local' | 'graceful' | 'remote'三态;ClientOptions.listChanged在现代连接自动打开 listen 流;
  • 取消语义:2026-07-28 Streamable HTTP 连接上取消请求改为关闭该请求的 SSE 响应流(规范取消信号),2025 时代与任意时代的 stdio 仍发notifications/cancelled
  • 能力清单列表方法尊重协商:服务器未声明能力时listTools()/listPrompts()/listResources()/listResourceTemplates()返回空列表而非发请求(enforceStrictCapabilities: true仍抛错);
  • stdio 传输maxBufferSize(默认 10 MB,导出STDIO_DEFAULT_MAX_BUFFER_SIZE):单条消息将推超上限时触发onerror并关闭,而非无界增长;
  • WebSocket 传输移除:WebSocket 不是规范定义传输,改用 stdio 或 Streamable HTTP;Transport接口仍导出供自定义实现(见 custom-transports 文档)。

十、升级与迁移路径

从 v1(@modelcontextprotocol/sdk单包)迁移,官方提供两条明确路径:

  1. v1 → v2 升级指南:涵盖包拆分、导入路径变化、Protocol基类与mergeCapabilities从包根导出(v1 的shared/protocol.js导入由 codemod 自动重写)、stdio 子路径、错误类与类型变化;
  2. 2026-07-28 修订采用指南:涵盖时代 wire codec 拆分、resultType建模变化、_meta信封键上提、协议错误码重编号等。

仓库内的packages/codemod提供 v1→v2 的自动化迁移工具。升级时请特别关注本文列出的破坏性变更:ResponseCacheStorevalue改为stringDiscoverResult不再声明serverInfoOAuthClientFlowError家族的错误类型变化、以及错误码重编号。测试覆盖方面,客户端包的完整测试矩阵(认证、DPoP、版本协商、缓存、SSE/Streamable HTTP/stdio 传输、输入必需引擎等)位于 packages/client/test/client,是理解各行为边界的权威参考。

【免费下载链接】typescript-sdkThe official TypeScript SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/GitHub_Trending/ty/typescript-sdk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询