#2479·mcp-use

客户端: StdioConnector 在失败的握手后重试时,在前驱进程退出之前就可以创建子进程

作者: rohith500创建于 2026年9月8日更新于 2026年9月8日
标签buginvalidserverclientprotocolmcp-use/mcp-use

重现

正如 @Elioooon 在 https://GitHub.com/mcp-use/mcp-use/pull/2458#issuecomment-5587664551 中所述: 当初始化失败时,Client._legacyHandshake() 会立即调用 this.close() 而不等待,而 StdioClientTransport.close() 会在不等待子进程退出的情况下将其进程句柄置为空:

javascript
import assert from "node:assert/strict";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";

// Child server that fails initialize
const transport = new StdioClientTransport({
  command: process.execPath,
  args: ["-e", `/* keep open until SIGUSR2 */`],
});

await assert.rejects(client.connect(transport), /initialization failure/);
await client.close();

// Child process is still alive when client.close() resolves:
process.kill(transportPid, 0); // succeeds (ESRCH is not thrown)

生产影响

  1. 独占锁竞争:如果一个 MCP 服务器在启动时获取了一个独占锁(例如嵌入式 SQLite / DuckDB 数据库锁,.mcp/ 中的文件锁或 leveldb),那么立即重试可能会失败,因为退出的前驱进程还没有完成刷新和释放操作系统锁。
  2. 端口/套接字冲突:如果本地 stdio 服务器为辅助服务绑定到固定的循环回环端口或 IPC 套接字,那么快速重启可能会与 EADDRINUSE 相冲突。
  3. 资源峰值:在崩溃-重启循环期间,多个同时运行的相同服务器的并发实例会产生临时的 CPU/内存峰值。

内容来源: mcp-use/mcp-use