@modelcontextprotocol/[email protected] 破坏性变更:强制单请求构建
@modelcontextprotocol/[email protected]
推荐理由
v2.0.1 改变了 MCP Fastify 传输层的生命周期模型,复用实例会导致服务中断。使用此 SDK 的开发者必须立即检查代码,确保在 Handler 内为每个请求新建 Server 和 Transport,否则线上服务将在第二个请求时崩溃。
Patch Changes
补丁更改
- #2918 84804c2 Thanks @claude! - A Server or McpServer now serves one connection at a time, and a Streamable HTTP server transport without sessions (sessionIdGenerator: undefined) serves one request. An app that uses one server object, or one stateless transport, for every HTTP request fails on the second request after this upgrade. Build the server and the transport per request instead.
- What keeps working without a change:
- createMcpHandler(buildServer) and serveStdio(buildServer), where buildServer returns a new server on every call.
- A handler that builds a new server and a new stateless transport for each request.
- One server and one transport per session (a transport with a sessionIdGenerator).
- Connecting a server again after close().
- Client.
- What fails now, how it shows, and what to change:
- One server object with a new stateless transport per request (const server = new McpServer(...) outside the handler, await server.connect(transport) inside it): the second HTTP request the process receives fails, and so does every later one. connect() rejects with an SdkError of code ALREADY_CONNECTED. If the handler closes the transport when the response ends, requests that arrive one after the other still work and a request that overlaps another one fails. Change: move new McpServer(...) and its registrations into the handler.
- One stateless transport for every request (a transport built once with sessionIdGenerator: undefined): the second HTTP request fails. WebStandardStreamableHTTPServerTransport.handleRequest() rejects with Stateless transport cannot be reused across requests. Create a new transport per request., and NodeStreamableHTTPServerTransport.handleRequest() answers 500. Change: build the server and the transport inside the handler and connect them there.
- createMcpHandler(() => server) with a server built once: a request that arrives after the previous response has been read to its end still works. A request that arrives while another one is being served is answered 500 with the JSON-RPC error -32603 (Internal server error); the reason is reported only through the onerror option. Change: pass a function that builds the server, as in createMcpHandler(buildServer).
- One server object for every session: the initialize request of the second session fails with ALREADY_CONNECTED. Change: build a server per session.
- What the caller sees when connect() or handleRequest() rejects depends on the host. Express 5, Fastify and Hono answer 500. A plain node:http listener without its own error handling gets an unhandled rejection, which ends the process.
- The README examples of @modelcontextprotocol/express, @modelcontextprotocol/fastify, @modelcontextprotocol/hono and @modelcontextprotocol/node, and the handler examples in the JSDoc of WebStandardStreamableHTTPServerTransport and NodeStreamableHTTPServerTransport, now build a server and a transport per request.
- #2908 633dd3e Thanks @claude! - The license field of the package manifests is now Apache-2.0; the LICENSE file shipped in each package carries the full terms, including the MIT text for earlier contributions. No code change.
- Updated dependencies [40f8f4e, 6d8dbc6, 5238fba, 2fc49ea, 4d94e7b, 84804c2, e55f9ac, 633dd3e, 2237555]:
- @modelcontextprotocol/[email protected]
- #2918 84804c2 感谢 @claude!- Server 或 McpServer 现在一次仅服务一个连接,且没有会话的 Streamable HTTP 服务器传输(sessionIdGenerator: undefined)一次仅处理一个请求。如果在每次 HTTP 请求中都使用同一个服务器对象或无状态传输的应用程序,在此升级后的第二个请求将会失败。改为为每个请求构建服务器和传输层。
- 无需更改即可继续正常工作的内容:
- createMcpHandler(buildServer) 和 serveStdio(buildServer),其中 buildServer 在每次调用时返回一个新的服务器。
- 为每个请求构建新服务器和新无状态传输的处理程序。
- 每个会话一个服务器和一个传输层(带有 sessionIdGenerator 的传输层)。
- 在 close() 之后重新连接服务器。
- 客户端。
- 当前会失败的情况、表现方式以及修改方法:
- 每个请求使用一个新无状态传输层的单个服务器对象(const server = new McpServer(...) 在处理程序外部定义,await server.connect(transport) 在其内部执行):进程收到的第二个 HTTP 请求将失败,后续所有请求也会失败。connect() 将以代码为 ALREADY_CONNECTED 的 SdkError 拒绝。如果处理程序在响应结束时关闭传输层,连续到达的请求仍能正常工作,但与其他请求重叠的请求则会失败。修改方法:将 new McpServer(...) 及其注册逻辑移入处理程序中。
- 每个请求使用一个无状态传输层(通过 sessionIdGenerator: undefined 一次性构建的传输层):第二个 HTTP 请求将失败。WebStandardStreamableHTTPServerTransport.handleRequest() 将抛出错误“Stateless transport cannot be reused across requests. Create a new transport per request.”,而 NodeStreamableHTTPServerTransport.handleRequest() 将返回 500 状态码。修改方法:在处理程序内部构建服务器和传输层,并在其中进行连接。
- createMcpHandler(() => server) 中服务器被一次性构建:在当前响应完全读取完毕后到达的请求仍可正常工作。而在另一个请求正在被服务期间到达的请求将返回 500 状态码及 JSON-RPC 错误 -32603(内部服务器错误);该原因仅通过 onerror 选项报告。修改方法:传入一个用于构建服务器的函数,如 createMcpHandler(buildServer)。
- 每个会话使用一个服务器对象:第二个会话的 initialize 请求将因 ALREADY_CONNECTED 而失败。修改方法:为每个会话构建一个服务器。
- 当 connect() 或 handleRequest() 被拒绝时,调用方看到的内容取决于主机。Express 5、Fastify 和 Hono 返回 500。没有自带错误处理的普通 node:http 监听器会收到未处理的拒绝(unhandled rejection),从而导致进程终止。
- @modelcontextprotocol/express、@modelcontextprotocol/fastify、@modelcontextprotocol/hono 和 @modelcontextprotocol/node 的 README 示例,以及 WebStandardStreamableHTTPServerTransport 和 NodeStreamableHTTPServerTransport JSDoc 中的处理程序示例,现在为每个请求构建一个服务器和一个传输层。
- #2908 633dd3e 感谢 @claude!- 包清单的许可证字段现已改为 Apache-2.0;每个包附带的 LICENSE 文件包含完整条款,包括早期贡献的 MIT 文本。无代码变更。
- 更新依赖项 [40f8f4e, 6d8dbc6, 5238fba, 2fc49ea, 4d94e7b, 84804c2, e55f9ac, 633dd3e, 2237555]:
- @modelcontextprotocol/[email protected]
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力