MCP TypeScript SDK 请求体大小限制全面解析:Streamable HTTP 全入口 4 MiB 上限与 413 防护机制
2026/9/14 11:25:59 网站建设 项目流程

MCP TypeScript SDK 请求体大小限制全面解析:Streamable HTTP 全入口 4 MiB 上限与 413 防护机制

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

导读

本文基于@modelcontextprotocol/typescript-sdk仓库中.changeset/request-body-size-limit.md变更记录,系统讲解 SDK 为 Streamable HTTP 服务端引入的请求体大小限制机制:从WebStandardStreamableHTTPServerTransportcreateMcpHandlertoNodeHandlercreateMcpHonoApp等所有 SDK 自有的 body 读取路径,如今统一在解析任何内容之前以默认4 MiB上限拦截超大请求并回答413 Payload Too Large,同时配套引入 JSON-RPC 批量消息数量上限(100 条)与 Host/Origin 校验前置的硬化改造。读完本文,你将掌握每个入口的maxRequestBodySize配置方法、readRequestBody有界读取器的实现原理、parsedBody逃逸通道的边界,以及 Node 适配器双层上限的调优要点。

一、背景:为什么需要统一的请求体大小限制

MCP(Model Context Protocol)服务端通过 Streamable HTTP 接收客户端 POST 上来的 JSON-RPC 消息。在过去,SDK 的各个 HTTP 入口对请求体大小缺乏一致的约束:

  • 一个超大的 POST body 会被完整读入内存后再尝试 JSON 解析,攻击者或故障客户端可以借此耗尽服务器内存(DoS 风险);
  • 不同入口(web-standard 传输层、createMcpHandler入口、Node/Hono 适配器)各自为政,行为不统一,运维难以预期;
  • 旧版 SSE 传输早已存在读取上限,而 Streamable HTTP 路径却没有对齐。

本次变更的目标十分明确:让所有 SDK 自有的 body 读取都在"解析之前"就受限于统一大小,超限直接回答413 Payload Too Large,不浪费任何解析与调度资源。核心实现位于 packages/server/src/server/requestBody.ts,常量与工具函数则从@modelcontextprotocol/server包公开导出(见 packages/server/src/index.ts)。

二、统一上限:DEFAULT_MAX_REQUEST_BODY_SIZE= 4 MiB

在 requestBody.ts 中定义了两个核心常量:

/** Default upper bound, in bytes, on a request body read by the HTTP entry points (4 MiB). */ export const DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024; /** Upper bound on the number of messages accepted in one JSON-RPC batch array. */ export const MAX_BATCH_SIZE = 100;
  • DEFAULT_MAX_REQUEST_BODY_SIZE4 * 1024 * 1024字节,即4 MiB。这个值并非凭空而来——它正是旧版 SSE 传输早已使用的限制,本次变更让 Streamable HTTP 各入口与其对齐。Express 适配器与 stdio 传输此前也已各自对读取设限,因此全 SDK 的 HTTP 读取路径现在处于同一防护水位。
  • MAX_BATCH_SIZE:单次 JSON-RPC 批量(batch)数组最多接受100 条消息,超过则整体拒绝,详见下文第六节。

上限可通过新的maxRequestBodySize选项(单位:字节,默认即DEFAULT_MAX_REQUEST_BODY_SIZE)配置,覆盖以下全部入口:

入口选项挂载点源码位置
WebStandardStreamableHTTPServerTransport(含基于它构建的 Node transport)WebStandardStreamableHTTPServerTransportOptions.maxRequestBodySizestreamableHttp.ts
createMcpHandler(转发到其无状态 legacy 分支)CreateMcpHandlerOptions.maxRequestBodySizecreateMcpHandler.ts
isLegacyRequest/legacyStatelessFallback相同的选项,保证判定与处理用同一把尺子createMcpHandler.ts
createMcpHonoApp(JSON 预解析)CreateMcpHonoAppOptions.maxRequestBodySizehono.ts
toNodeHandler/toWebRequest(Node 适配器)ToNodeHandlerOptions/ToWebRequestOptionstoNodeHandler.ts

选项值的校验由resolveMaxRequestBodySize完成(requestBody.ts):省略时回落默认值;传入的值必须为正的有限数值,否则在配置期即抛出RangeErrormaxRequestBodySize must be a positive number of bytes),杜绝运行时才暴露的配置错误。

三、核心实现:readRequestBody有界读取器

maxRequestBodySize之所以能在"解析之前"生效,靠的是导出的有界读取器readRequestBody(requestBody.ts):

export async function readRequestBody( request: Request, maxBytes: number = DEFAULT_MAX_REQUEST_BODY_SIZE ): Promise<{ tooLarge: true } | { tooLarge: false; text: string }> { if (Number(request.headers.get('content-length')) > maxBytes) { return { tooLarge: true }; } if (request.body === null) { return { tooLarge: false, text: '' }; } const reader = request.body.getReader(); const decoder = new TextDecoder(); let received = 0; let text = ''; try { for (;;) { const { done, value } = await reader.read(); if (done) break; received += value.byteLength; if (received > maxBytes) { return { tooLarge: true }; } text += decoder.decode(value, { stream: true }); } } finally { reader.releaseLock(); } return { tooLarge: false, text: text + decoder.decode() }; }

其设计要点有三:

  1. Content-Length预检查(零读取拒绝):若请求头声明的Content-Length已经超过上限,一个字节都不读,直接返回tooLarge: true。这是最高效的拦截路径。
  2. 流式累积检查(边读边断):对于没有可靠Content-Length(或使用 chunked transfer)的请求,通过ReadableStreamgetReader()逐块读取,累计字节数一旦超过上限立即返回tooLarge,不会等到把整个 body 读完。流式读取失败(网络中断等)会原样向上传播。
  3. 释放锁与 UTF-8 安全解码finally中保证reader.releaseLock();文本用TextDecoder流式解码并在末尾 flush,避免多字节字符在块边界被截断损坏。

该函数作为readRequestBody@modelcontextprotocol/server导出(index.ts),其定位是"像isJsonContentType一样"的基础构件,供需要自行预解析 body 的适配器作者复用,从而让第三方适配器也能获得与 SDK 一致的有界读取语义。

超限后的统一响应由requestBodyTooLargeMessage生成(requestBody.ts):

Payload Too Large: Request body must not exceed {maxBytes} bytes

对应响应为 HTTP413,JSON-RPC error code-32000,且在 body 被解析、任何服务器实例被创建之前就已返回。

四、各入口的行为与配置详解

4.1WebStandardStreamableHTTPServerTransport(及 Node transport)

这是核心的 Web 标准 Streamable HTTP 传输层,可在任意支持 Web 标准的运行时运行(Node.js 18+、Cloudflare Workers、Deno、Bun 等)。Node 环境下的NodeStreamableHTTPServerTransport正是对其的封装(其 JSDoc 中明确说明 "wraps this transport"),因此同样继承了上限。

在构造函数中(streamableHttp.ts),maxRequestBodySizeresolveMaxRequestBodySize解析后存入实例。handlePostRequest的处理流程(streamableHttp.ts)为:

  1. 校验Accept头与Content-Type(415 检查);
  2. 若调用方未提供parsedBody,则调用readRequestBody(req, this._maxRequestBodySize)
  3. tooLarge时通过createJsonErrorResponse返回413,并触发onerror报告;
  4. 通过后才JSON.parse,然后做 batch 数量检查与JSONRPCMessageSchema校验。

有状态/无状态会话语义、SSE 流式响应等既有行为均不受影响——限制只在"读 body"这一步之前生效。

4.2createMcpHandler/isLegacyRequest/legacyStatelessFallback

createMcpHandler是服务 2026-07-28 协议修订版并默认回落到 2025 时代无状态服务的 HTTP 入口。它的maxRequestBodySize同时作用于两条腿:

  • 现代分支(modern leg):请求分类步骤(classifyEntryRequest,createMcpHandler.ts)读取 body 时使用同一上限,读取结果tooLarge会返回{ step: 'body-too-large' },随后入口直接以413/-32000回答(createMcpHandler.ts),不创建任何服务器实例、不进入分类阶梯
  • 无状态 legacy 分支(legacy leg)createLegacyStatelessFallback构造的 per-request transport 会收到同样的maxRequestBodySize并透传给WebStandardStreamableHTTPServerTransport(createMcpHandler.ts),保证两个时代的服务对超大请求行为一致。

isLegacyRequest判定函数同样接受maxRequestBodySize选项(createMcpHandler.ts),并用它驱动自己的分类读取,因此判定与处理永远在同一把尺子下——从源码看,它就是createMcpHandler内部分类步骤的导出形态,二者共用classifyEntryRequest,不会出现"判定为可处理、实际却被 413 拒绝"的分歧。对于 body 超限的请求,isLegacyRequest会将其报告为非 legacy(返回false),于是它被路由到现代 handler,由现代路径回答413legacyStatelessFallback同样提供LegacyStatelessFallbackOptions.maxRequestBodySize(createMcpHandler.ts)。

4.3toNodeHandler/toWebRequestRequestBodyTooLargeError

Node 适配器toNodeHandler将 web-standard 的{ fetch, close, notify, bus }handler 适配为 Node 的(req, res, parsedBody?)形态。当没有传入预解析 body时,适配器需要自行从 Node 流读取 body 并转换为 web-standardRequest,这一步由toWebRequest完成(toNodeHandler.ts)。现在,当读取过程中 body 超过上限时:

  • toWebRequestreject 一个错误,其name'RequestBodyTooLargeError'status413(toNodeHandler.ts);
  • toNodeHandler捕获该错误后回答413,响应体为 JSON-RPC 错误-32000,并额外携带connection: close头——注释说明这是为了让 HTTP/1.1 服务器在回答后直接关闭 socket,而不是挂起一个请求流只被读了一半的连接(toNodeHandler.ts)。

手动调用toWebRequest的调用方(例如自组装isLegacyRequest路由时)需要自行处理该 rejection:要么 catch 后返回 413,要么直接传入已解析的 body(parsedBody)绕开读取。这一点在 toWebRequest 的 JSDoc 中有明确说明。

4.4createMcpHonoAppcreateMcpExpressApp:Host/Origin 校验前置

两个框架入口还伴随一项顺序性硬化:Host/Origin 校验现在先于 JSON body 解析器运行

  • HonocreateMcpHonoApp依次注册 DNS rebinding 防护(hostHeaderValidation/localhostHostValidation)→ Origin 校验 → JSON body 解析中间件(hono.ts)。body 解析中间件对Content-Typeapplication/json的请求从 clone 上调用readRequestBody(上限即maxRequestBodySize,默认 4 MiB),超限回答413,并把解析结果存入c.set('parsedBody', ...)供 MCP 适配器使用。
  • ExpresscreateMcpExpressApp同样先注册 Host/Origin 校验,再app.use(express.json(...))(express.ts),其jsonLimit选项直接透传给 Express 的express.json({ limit }),默认即 Express 内置的'100kb'

由此带来的可观察行为变化:来自不允许的 Host 或 Origin、且携带无效 JSON body 的请求,现在回答403而非400,并且其 body 根本不会被读取——校验失败即短路,解析器永远没有机会接触 payload。

五、双层上限的调优要点:Node 适配器

使用toNodeHandler时存在两层上限,需要特别注意(ToNodeHandlerOptions JSDoc 明确提示):

  1. 适配器层toNodeHandlermaxRequestBodySize):在toWebRequest从 Node 流缓冲 body 时应用;
  2. handler 层createMcpHandlermaxRequestBodySize):在 web-standardfetch内部读取时应用。

适配器的 bound 先于 handler 的 bound 生效——请求必须先通过适配器的读取,才可能到达 handler。因此若想调大上限,两层必须同时提高,否则无论 handler 层设置多大,适配器层仍会以 4 MiB(或你设置的更小值)先行拦截。

六、JSON-RPC 批量消息上限:100 条,400/-32600

与 body 大小限制配套,SDK 在所有入口统一施加了 batch 数量约束:MAX_BATCH_SIZE = 100。在WebStandardStreamableHTTPServerTransport.handlePostRequest中(streamableHttp.ts):

if (Array.isArray(rawMessage) && rawMessage.length > MAX_BATCH_SIZE) { this.onerror?.(new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`)); return this.createJsonErrorResponse(400, -32_600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`); }
  • 超过 100 条消息的 batch 被整体回答400/-32600Invalid Request);
  • 其中任何一条都不会被调度执行(检查发生在消息分发之前);
  • 该约束不区分是否传入parsedBody:即使调用方预解析了 body 从而跳过 SDK 的 body 读取与大小限制,batch 数量上限依然生效——这是本文第三节"parsedBody逃逸通道"之外的唯一例外,它是无条件的。

七、parsedBody逃逸通道:何时跳过限制

HandleRequestOptions.parsedBody(streamableHttp.ts)与McpHandlerRequestOptions.parsedBody(createMcpHandler.ts)是 SDK 为"上游中间件已经解析好 body"的场景预留的通道:

  • 当调用方传入parsedBody时,SDK不再自行读取请求体,因此maxRequestBodySize的大小限制不会作用于该路径——这一般是合理的,因为 body 已经在你的 body-parser(如express.json()、Hono 内置解析)阶段被有界处理过了;
  • 但如第六节所述,batch 数量上限在任何情况下都适用

典型用法(来自toNodeHandler的 JSDoc 示例,toNodeHandler.ts):

const handler = createMcpHandler(factory); app.all('/mcp', toNodeHandler(handler)); // 或,当 body parser 已消费流时: const node = toNodeHandler(handler); app.all('/mcp', (req, res) => void node(req, res, req.body));

八、测试与验证路径

本次变更在仓库中配有完整测试,可据此验证各入口行为:

  • packages/server/test/server/streamableHttp.test.ts:验证 transport 层 413 响应、batch 限制与parsedBody通道;
  • packages/server/test/server/createMcpHandler.test.ts:验证入口层 body 超限返回 413、isLegacyRequest分类行为;
  • packages/middleware/node/test/toNodeHandler.test.ts:验证RequestBodyTooLargeErrortoNodeHandler的 413 回答;
  • packages/middleware/hono/test/hono.test.ts:验证 Hono 应用 JSON 预解析的 413 与 Host/Origin 前置。

相关使用文档可进一步参考 docs/serving/http.md、docs/serving/express.md、docs/serving/hono.md 与 docs/serving/web-standard.md。

九、迁移与配置速查

  • 默认值maxRequestBodySize缺省即 4 MiB(DEFAULT_MAX_REQUEST_BODY_SIZE),与旧版 SSE 传输的历史限制对齐;该常量从@modelcontextprotocol/server导出。
  • 单位与校验:一律为字节数;非正数或非有限值在配置期抛RangeError
  • Node 双层toNodeHandlercreateMcpHandler的上限都要配置,适配器层先生效。
  • parsedBody:传入后 SDK 不读 body、不套大小限制;batch 的 100 条上限依旧。
  • Host/Origin 前置:Hono 与 Express 应用中,不允许的 Host/Origin 请求在 body 解析前即被 403 拒绝。
  • 行为一致性createMcpHandlerisLegacyRequestlegacyStatelessFallback共享同一分类代码路径与同一上限,判定与处理不会互相矛盾。

总体而言,这次变更把"请求体大小防护"从各传输层零散的隐式约束,收敛为 SDK 所有 HTTP 入口默认统一、可在配置期显式调整、并在解析前短路拒绝的一等公民能力,配合 batch 数量上限与 Host/Origin 前置校验,显著降低了 MCP 服务端暴露在公网时遭受超大 payload 攻击与解析资源浪费的风险面。

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

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

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

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

立即咨询