zai-org / zai-org/feedback

[bug] node_repl MCP 间歇性协商失败:5s discover 探针窗口 < Windows 进程冷启动时间

Open
#291 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

priority: P2 status: 开发中
Dominant language
No language data
Stars
22
Forks
1
PR merge metrics
No merged PRs in 30d

Description

[bug] node_repl MCP 间歇性协商失败:5s discover 探针窗口 < Windows 进程冷启动时间

环境

  • ZCode 桌面端 3.7.7(Windows 11 26200,Win64)
  • CLI 内核 0.16.3(resources/glm/zcode.cjs
  • browser-use 插件 0.2.1(内置包与缓存副本字节级一致,均内含 modern SDK,FIRST_MODERN_PROTOCOL_VERSION = "2026-07-28"
  • Windows Defender 实时防护开启(默认)

现象

配置 ≥2~3 个全局自配 stdio MCP 服务器后,浏览器工具(node_repl)在会话启动时间歇性注册失败,报错:

Version negotiation failed: the server did not offer pinned protocol version 2026-07-28
via server/discover (no fallback in pin mode)

mcp__node_repl__* 工具不出现,内置浏览器不可用。自配 MCP 本身全部正常连接(它们走 auto 模式可降级)。

复现概率(实测,每轮全新启动):

  • 0/1 个自配服务器:5/5 成功
  • 2 个:1 成 1 败
  • 3 个:2/2 失败
  • 3 个 + 人为高压 MCP(启动期 CPU 忙 2s×4 进程):稳定复现失败

根因(反编译 + 失败现场插桩双重确认)

服务器端无辜:对 browser-use-plugin/dist/mcp/server.js 插桩捕获失败现场,discover 探针进程在自身就绪后 0.9~1.3 秒内即写出正确应答 {"supportedVersions":["2026-07-28"],...}(与成功轮逐字节同构)。

失败现场时间线(插桩 + CLI 日志对齐,连接 #1):

时刻 事件
14:20:12.488 客户端 connect.started,spawn 探针进程,exchange 计时器启动
14:20:17.500 5s 探针窗口到期
14:20:17.378 探针进程才完成 BOOT(冷启动 4.9s
14:20:18.276 服务器写出正确应答(迟到 0.78s,被丢弃)
14:20:22.359 客户端上报失败(durationMs=9716 = 5s 窗口 + 4.7s 清理)

连接 #2 更极端:窗口 19.5s 关闭时探针进程 19.9s 才就绪(冷启动 5.4s)。

代码链zcode.cjs):

  1. resolveBuiltInNodeReplMcpServers 硬编码 node_repl 注册为 protocolVersion: "2026-07-28"(pin 模式)+ timeoutMs: 600000
  2. Ovi():探针超时 = Math.min(Rvi=5000, timeoutMs/2) —— 全局 5 秒硬上限Rvi=5e3 不可配置;600s 超时的注册也被压到 5s
  3. ProbeWindowy_i/Qsn):exchange()setTimeout 先于 send 注册——计时从 spawn 后瞬间起算,不含子进程冷启动时间;disposableProbe 为独立 spawn 的一次性进程(需加载 7.3MB server.js)
  4. probe.maxRetries ?? 0 —— 超时零重试
  5. l_i 分类器:case "timeout": stdio → {kind:"legacy"} —— 超时被静默归类为 legacy
  6. pin 模式对 legacy 无回退,抛出上述误导性文案("did not offer version",实际是窗口内未收到应答)

为何 Windows 高发:Defender 对每个新 node 进程实时扫描 7.3MB 单文件 + 多 MCP 并发 spawn 排队,实测冷启动 4.95.4s(同一文件在无竞争手动 spawn 下全链路仅 1.32.3s)。5s 窗口在进程就绪前已耗尽,服务器应答再快也无法挽救

修复建议(按优先级)

  1. 探针计时起点改为子进程就绪后(stdout 可读/首字节),而非 send 前——这是根本修复,5s 窗口本意是测服务器应答速度,不应被进程冷启动侵占
  2. 或 pin 模式给予 ≥1 次重试(maxRetries 当前硬编码 0)
  3. 或 node_repl 这类高 timeoutMs 注册豁免 Rvi 上限(600s/2 = 300s 才是注册配置的本意)
  4. 报错文案区分 "probe timed out" 与 "server answered without the pinned version"——两者现共用同一句话,严重误导排查方向(服务器端怎么查都无辜)

证据

失败现场插桩日志(BOOT 时刻 / stdout 应答原文 / EXIT)已存档,可提供。CLI 日志时间戳与上述时间线逐条对齐。


连带建议:同一注册中 timeoutMs: 600000(表达"值得等 10 分钟")与 discover 探针 5s 硬上限(不给冷启动留 1 秒)自相矛盾,建议内部评审该组合。

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing resolveBuiltInNodeReplMcpServers, Ovi(), ProbeWindow, and the l_i classifier in resources/glm/zcode.cjs; use browser-use-plugin/dist/mcp/server.js only to verify the probe response timing. Reproduce on Windows with multiple stdio MCP servers, then confirm the chosen timeout behavior and error classification distinguish a probe timeout from a missing pinned version.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js
Domain
cli, devtools, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.