跳到主内容
@wquguru
精选88MCP TypeScript SDK(GitHub Releases)AI 编程与模型

@modelcontextprotocol/[email protected]:新增OAuth scope挑战

@modelcontextprotocol/[email protected]

原文
发到 X
推荐理由

本次更新包含破坏性变更:强制要求 MCP-Protocol-Version 请求头且默认限制请求体大小。使用自定义客户端或代理的开发者必须升级 SDK 或调整代码以适配新规范,否则将遭遇连接失败或请求被拒。

Minor Changes

次要更改

  • #1624 6032170 Thanks @SamMorrowDrums! - Add request-time OAuth scope challenges for tools, resources, resource templates,
  • and prompts. Each primitive's scopeChallenge callback receives the parsed
  • request and verified authentication info, then either continues or returns the
  • exact scope set for an insufficient_scope response. requireScopes provides a
  • small helper for static all-of checks.
  • createMcpHandler and Streamable HTTP transports return HTTP 403 with an
  • insufficient_scope challenge before handler execution or SSE setup. The
  • preflight is active whenever a registered primitive carries a scopeChallenge
  • callback — there is no handler- or transport-level configuration. The
  • challenge's WWW-Authenticate header is built by the same formatter as the
  • bearer-auth 401/403 answers, and its resource_metadata parameter is derived
  • from the verified AuthInfo: requireBearerAuth / verifyBearerToken now
  • stamp their configured resourceMetadataUrl onto the AuthInfo they return
  • (new optional AuthInfo.resourceMetadataUrl field), with a fallback to the
  • well-known location for an HTTP(S) RFC 8707 resource identifier; the
  • parameter is omitted when neither is available.
  • #1624 6032170 感谢 @SamMorrowDrums!- 为工具、资源、资源模板以及提示词添加请求时的 OAuth 范围挑战。
  • 每个原语(primitive)的 scopeChallenge 回调接收已解析的请求和已验证的身份认证信息,然后继续执行或返回用于 insufficient_scope 响应的确切范围集。requireScopes 提供了一个小型辅助函数,用于静态的全量检查。
  • request and verified authentication info, then either continues or returns the
  • exact scope set for an insufficient_scope response. requireScopes provides a
  • small helper for static all-of checks.
  • createMcpHandler 和 Streamable HTTP 传输在处理器执行或 SSE 设置之前,返回带有 insufficient_scope 挑战的 HTTP 403 状态码。
  • insufficient_scope challenge before handler execution or SSE setup. The
  • 每当注册的某个原语携带 scopeChallenge 回调时,预检(preflight)即处于激活状态——无需处理器或传输级别的配置。
  • callback — there is no handler- or transport-level configuration. The
  • 挑战的 WWW-Authenticate 标头由与 bearer-auth 401/403 响应相同的格式化程序构建,其 resource_metadata 参数源自已验证的 AuthInfo:requireBearerAuth / verifyBearerToken 现在
  • 将其配置的 resourceMetadataUrl 附加到它们返回的 AuthInfo 上
  • (新增可选的 AuthInfo.resourceMetadataUrl 字段),并回退至 HTTP(S) RFC 8707 资源标识符的 well-known 位置;当两者均不可用时,省略该参数。
  • stamp their configured resourceMetadataUrl onto the AuthInfo they return
  • (new optional AuthInfo.resourceMetadataUrl field), with a fallback to the
  • well-known location for an HTTP(S) RFC 8707 resource identifier; the
  • parameter is omitted when neither is available.

Patch Changes

补丁更改

  • #2726 6fa4227 Thanks @LuckTerence! - SdkError and SdkHttpError accept standard ErrorOptions as an optional fourth constructor argument and forward it to Error, so a wrapped error is reachable through the standard Error.cause chain. Version-negotiation probe failures (SdkErrorCode.EraNegotiationFailed) now use it: the underlying TypeError: fetch failed and the DNS or socket error beneath it surface via error.cause, so pino, Sentry, and util.inspect render ENOTFOUND / ECONNREFUSED / ETIMEDOUT instead of stopping at the SdkError (#2657). The previous error.data.cause slot is still populated for compatibility but is deprecated and slated for removal; read error.cause instead.
  • #2654 03842cd Thanks @pshah19! - Treat request id 0 as a real id. Two guards tested a RequestId for truthiness, so the legal JSON-RPC ids 0 and '' were read as absent. Id 0 is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first sampling/createMessage, elicitation/create, or roots/list a server sends.
  • notifications/cancelled carrying id 0 was ignored, and the in-flight handler ran to completion with its AbortSignal never fired.
  • A notification sent with relatedRequestId: 0 wrongly passed the debounce gate (for methods opted into debouncedNotificationMethods). Because the pending set is keyed by method alone, a second such notification in the same tick was silently dropped rather than sent.
  • Absent is now the only value that means "no id".
  • #2668 3e90449 Thanks @KKonstantinov! - Stop sending notifications/cancelled for the initialize handshake. The spec is explicit that a client MUST NOT attempt to cancel its initialize request, but the outbound cancel path fired for any in-flight request: aborting the AbortSignal passed to connect(), or letting the handshake hit its timeout, put a forbidden cancellation on the wire naming the initialize request id.
  • The local behaviour is unchanged — the caller's promise still rejects with the same abort/timeout error, and connect() still tears the connection down. Only the wire notification is suppressed. Every other method keeps the existing cancellation path.
  • #2698 7b781ed Thanks @maxisbey! - Read Streamable HTTP request bodies with a size limit. Every SDK-owned body read —
  • WebStandardStreamableHTTPServerTransport (and the Node transport built on it),
  • createMcpHandler, toNodeHandler, and createMcpHonoApp's JSON pre-parse — now stops at
  • 4 MiB by default (the limit the legacy SSE transport already uses; the Express adapter and stdio
  • bound their reads too) and answers 413 Payload Too Large before anything is parsed.
  • toWebRequest (when it reads the Node stream itself) now rejects once the body exceeds the
  • limit with an error whose name is 'RequestBodyTooLargeError' and status is 413, and
  • toNodeHandler answers that with 413; hand-wired callers of toWebRequest should handle the
  • rejection or pass a pre-parsed body, and isLegacyRequest reports such a request as non-legacy
  • so the modern handler answers it. JSON-RPC batch arrays are limited to 100 messages; a longer
  • batch is answered 400 / -32600 and none of it is dispatched.
  • The limit is configurable with a new maxRequestBodySize option (bytes, default
  • DEFAULT_MAX_REQUEST_BODY_SIZE = 4 MiB, exported from @modelcontextprotocol/server) on
  • WebStandardStreamableHTTPServerTransportOptions, CreateMcpHandlerOptions (forwarded to its
  • stateless legacy leg; isLegacyRequest and legacyStatelessFallback take the same option),
  • CreateMcpHonoAppOptions, and ToNodeHandlerOptions / ToWebRequestOptions (the adapter's
  • bound applies before the handler's, so raise both). The bounded reader is exported as
  • readRequestBody for adapter authors. Hosts that pre-parse the body and pass it as
  • parsedBody skip the SDK's read and its size limit entirely; the batch bound applies either way.
  • createMcpHonoApp and createMcpExpressApp now run their Host/Origin validation before the
  • JSON body parser, so a request from a disallowed Host or Origin with an invalid JSON body is
  • answered 403 rather than 400, and its body is not read.
  • #2590 75dc7ea Thanks @davidpavlovschi! - Reject a modern (2026-07-28) POST that omits the required MCP-Protocol-Version header.
  • createMcpHandler accepted a request whose body carried a valid per-request _meta
  • envelope but whose MCP-Protocol-Version header was absent: the request was classified
  • modern, dispatched, and answered 200 — tool handlers ran. Only the mismatch case
  • (header present, disagreeing with the body) was rejected, so of the standard headers
  • SEP-2243 requires on a modern POST, presence was enforced for Mcp-Method (and for
  • Mcp-Name on the methods that mirror params.name / params.uri) but not for
  • MCP-Protocol-Version.
  • Such a request is now refused with 400 Bad Request and JSON-RPC -32020
  • (HeaderMismatch), matching the shape the sibling missing-header cells already emit and
  • echoing the request id — per the Streamable HTTP spec, which requires the header on every
  • POST and lists a missing required standard header as a HeaderMismatch failure. The
  • spec's allowance to treat a header-less request as 2025-03-26 is available only to a
  • server that also serves pre-2025-06-18 clients, and permits routing it to legacy
  • handling — never serving it as 2026-07-28; under legacy: 'reject' the requirement is
  • unconditional.
  • Era classification is deliberately unchanged and stays body-primary: a proxy that strips
  • the header still must not change the era, so such a request is still classified modern
  • and is refused one rung later, at standard-header-validation — the same rung that
  • already answers a missing Mcp-Method. Legacy-era traffic is untouched, notifications
  • are unaffected, body-less GET / DELETE session operations are method-routed before
  • any header validation, and stdio serving (which has no HTTP headers) is not involved.
  • Clients built with this SDK always send the header, so no first-party client is affected;
  • hand-rolled clients that omitted it must add it.
  • #2726 6fa4227 感谢 @LuckTerence!- SdkError 和 SdkHttpError 接受标准的 ErrorOptions 作为第四个可选构造函数参数,并将其转发给 Error,因此可以通过标准的 Error.cause 链访问被包装的错误。版本协商探测失败(SdkErrorCode.EraNegotiationFailed)现在使用它:底层的 TypeError: fetch failed 及其下的 DNS 或套接字错误通过 error.cause 暴露出来,因此 pino、Sentry 和 util.inspect 会渲染 ENOTFOUND / ECONNREFUSED / ETIMEDOUT,而不是停在 SdkError 处(#2657)。之前的 error.data.cause 槽位仍为兼容而填充,但已被弃用并计划移除;请改用 error.cause。
  • #2654 03842cd 感谢 @pshah19!- 将请求 ID 0 视为真实 ID。两个守卫对 RequestId 进行了真值测试,导致合法的 JSON-RPC ID 0 和 '' 被视为缺失。ID 0 并非边缘情况:出站请求计数器从零开始,因此它是每个对端分配的第一个 ID,这在服务器→客户端链路中是服务器发送的第一个 sampling/createMessage、elicitation/create 或 roots/list。
  • 携带 ID 0 的 notifications/cancelled 被忽略,且正在处理的处理器运行至完成,其 AbortSignal 从未触发。
  • 带有 relatedRequestId: 0 的通知错误地通过了防抖动门控(针对选择加入 debouncedNotificationMethods 的方法)。由于待处理集合仅以方法为键,同一时间片内的第二个此类通知被静默丢弃,而非发送。
  • Absent 现在是唯一表示“无 ID”的值。
  • #2668 3e90449 感谢 @KKonstantinov!- 停止在初始化握手期间发送 notifications/cancelled。规范明确规定客户端不得尝试取消自己的初始化请求,但出站取消路径会对任何进行中的请求触发:向 connect() 传递的 AbortSignal 中止,或让握手超时,都会在链路上放置一个命名了初始化请求 ID 的非法取消操作。
  • 本地行为保持不变——调用者的 promise 仍会以相同的中止/超时错误拒绝,且 connect() 仍会拆除连接。仅抑制链路通知。其他所有方法保留现有的取消路径。
  • #2698 7b781ed 感谢 @maxisbey!- 使用大小限制读取可流式传输 HTTP 请求体。每个由 SDK 拥有的请求体读取——
  • WebStandardStreamableHTTPServerTransport(以及基于它构建的 Node 传输层),
  • createMcpHandler、toNodeHandler 和 createMcpHonoApp 的 JSON 预解析——现在默认在
  • 4 MiB 处停止(这是旧版 SSE 传输层已使用的限制;Express 适配器和 stdio
  • 也限制了它们的读取)并在解析任何内容之前返回 413 Payload Too Large。
  • toWebRequest(当它直接读取 Node 流时)一旦请求体超过
  • 限制便会拒绝,错误名称为 'RequestBodyTooLargeError',状态码为 413,并且
  • toNodeHandler 对此返回 413;手动连接 toWebRequest 调用者应处理该
  • 拒绝或传递预解析的请求体,而 isLegacyRequest 会将此类请求报告为非旧版
  • 以便现代处理器处理它。JSON-RPC 批处理数组限制为 100 条消息;更长的
  • 批处理将收到 400 / -32600 响应,且其中没有任何内容会被分发。
  • 该限制可通过新的 maxRequestBodySize 选项进行配置(字节,默认
  • DEFAULT_MAX_REQUEST_BODY_SIZE = 4 MiB,从 @modelcontextprotocol/server 导出)在
  • WebStandardStreamableHTTPServerTransportOptions、CreateMcpHandlerOptions(转发给其
  • 无状态旧版分支;isLegacyRequest 和 legacyStatelessFallback 采用相同选项)、
  • CreateMcpHonoAppOptions 以及 ToNodeHandlerOptions / ToWebRequestOptions(适配器的
  • 限制先于处理器的限制生效,因此两者都应提高)。有界读取器作为
  • 供适配器作者使用的 readRequestBody。预解析请求体并将其作为
  • parsedBody 传递的主机完全跳过 SDK 的读取及其大小限制;无论哪种情况,批处理边界均适用。
  • createMcpHonoApp 和 createMcpExpressApp 现在在
  • JSON 请求体解析器之前运行主机/来源验证,因此来自不允许的主机或来源且 JSON 请求体无效的请求将
  • 返回 403 而非 400,并且其请求体不会被读取。
  • #2590 75dc7ea 感谢 @davidpavlovschi!- 拒绝缺少必需 MCP-Protocol-Version 标头的现代(2026-07-28)POST 请求。
  • createMcpHandler 接受了一个请求体携带有效逐请求 _meta
  • 信封但其 MCP-Protocol-Version 标头缺失的请求:该请求被分类为
  • 现代版本,已分发并返回 200 —— 工具处理器已运行。仅不匹配的情况
  • (标头存在但与请求体不一致)被拒绝,因此在标准标头中
  • SEP-2243 要求在现代 POST 上强制存在 Mcp-Method(以及对于
  • 镜像 params.name / params.uri 的方法上的 Mcp-Name),但不强制存在
  • MCP-Protocol-Version。
  • 此类请求现在以 400 Bad Request 和 JSON-RPC -32020
  • (HeaderMismatch)被拒绝,这与兄弟缺失标头单元格已经发出的形状相匹配,并
  • 回显请求 id —— 根据 Streamable HTTP 规范,该规范要求每个
  • POST 都包含此标头,并将缺失的必需标准标头列为 HeaderMismatch 失败。允许
  • 规范允许将无标头请求视为 2025-03-26 仅适用于同时服务于
  • 2025-06-18 之前客户端的服务器,并允许将其路由到遗留
  • 处理流程 —— 绝不应将其作为 2026-07-28 提供服务;在遗留模式下:'reject' 时该要求是
  • 无条件的。
  • 时代分类故意保持不变且仍以请求体为主:剥离
  • 标头的代理仍不得更改时代,因此此类请求仍被分类为现代
  • 并在稍后的标准标头验证层级被拒绝 —— 同一层级即
  • 已经解决了缺失的 Mcp-Method 问题。遗留时代的流量不受影响,通知
  • 不受影响,无主体的 GET / DELETE 会话操作在
  • 任何头部验证之前进行方法路由,而 stdio 服务(没有 HTTP 头部)不涉及其中。
  • 使用此 SDK 构建的客户端始终发送该头部,因此没有第一方客户端受到影响;
  • 手动编写的省略了该头部的客户端必须添加它。

更进一步:量化金融体系

看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力

进入量化体系 →

相似阅读

关联信息,但可能不是同一事件