zai-org / zai-org/feedback

[Bug] 插件模板里的MCP部分有错误

Open
#438 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

priority: P2 status: 待评估 type: Bug
Dominant language
No language data
Stars
22
Forks
1
PR merge metrics
No merged PRs in 30d

Description

提交前确认 · Pre-submission checklist
  • 我已搜索过现有 issue,确认这不是重复 / I searched existing issues and confirmed this isn't a duplicate.
  • 我已阅读 CONTRIBUTING.md / I've read CONTRIBUTING.md.
问题类别 · Category

文档 / 教程 · Docs

涉及的 Agent 框架 · Agent framework

ZCode Agent(自研)

严重程度 · Severity

阻塞使用 · Blocking (无法使用核心功能 / core function unusable)

复现频率 · Reproducibility

必现 · Always

问题描述 · Description

ZCode 官方示例插件 MCP 服务器缺陷报告

对象:example-plugin@0.2.0 mcp/hello-server.mjs客户端:ZCode CLI 0.16.5 / Windows 10.0.26200 x64 | 日期:2026-08-27 | 日志~/.zcode/cli/log/zcode-2026-08-27.jsonl(UTC 时间戳,两个独立会话均复现)| 提交方:Medical Embedded Team(自研插件采用同类手写 stdio 实现踩中同款缺陷,已自行修复并交叉实证)

TL;DR

启用 example-plugin 后,其 MCP 示例服务器 example-hello 在真实客户端 100% 握手失败:子进程正常拉起并打印就绪横幅,但 initialize 得不到任何能被客户端认可的应答,30 秒超时(Settings → MCP 显示 failed)。根因在示例服务器而非客户端——现行 MCP stdio 规范要求消息为换行分隔 UTF-8 JSON-RPC,而示例用 LSP 式 Content-Length: 帧(下称 CL 帧)作答。同机基于官方 SDK 的 mimosa 插件同一时刻连接成功,构成干净对照。

缺陷清单

编号 位置 严重度 描述
DEF-1 L40–45 writeMessage() 高·致命 所有应答写成 Content-Length: n\r\n\r\n{json};客户端只解析换行分隔 JSON 回包,无法认领任何响应 → initialize 必超时
DEF-2 L125–137 stdin 解析循环 仅当缓冲区「含 \n 且首非空白字符为 {」才走换行路径;不带尾随换行的末条消息、或以 [ 开头的 JSON-RPC batch 将永久滞留缓冲区且无任何诊断输出
DEF-3 L74 initialize 分支 `protocolVersion: params?.protocolVersion

DEF-2、DEF-3 为潜在隐患,当前恰不可见,但与 DEF-1 同属一套不合规实现。

证据

生产日志 verbatim(09:37 会话;09:51 第二会话同样字段仅 PID 不同):

{"event":"mcp.server.failed","durationMs":30195,"status":"failed","context":{"error":"MCP server plugin:example-plugin:example-hello connection timed out after 30000ms","mcpServerName":"plugin:example-plugin:example-hello","mcpTransportPid":860,"stderr":"[example-hello] stdio MCP server ready\n","transport":"stdio"}}

读法:有 transportPid 且 stderr 已见就绪横幅——进程活着,但直到超时没有任何一次往返被客户端认领。对照组(同一分钟窗口,官方 SDK 实现,连接即时完成):

mcp.server.connected  mcpServerName=plugin:mimosa:mimosa  mcpClientVersion=0.16.5
mcpProtocolEra=legacy mcpProtocolVersion=2025-11-25  connectDurationMs=988  toolCount=5

(同一日志另见 modern 协商成功记录 2026-07-28。)

隐蔽性说明:README 式手工冒烟 printf '%s\n' '{...}' | node hello-server.mjs 时,人眼确实能在 stdout 看到 JSON(紧跟在 CL 头之后输出),极易误判为工作正常;只有严格按换行分隔逐行 JSON.parse 的真实客户端才会暴露此缺陷。

复现步骤 · Steps to reproduce

最小复现

spawn 该 server,向 stdin 写一行 {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",...}}\n,2 秒后检查 stdout 是否存在以 { 开头的行。当前 0.2.0 首行输出为 Content-Length: 171\r\n\r\n{...} → FAIL,即坐实 DEF-1。

期望表现 · Expected behavior

建议修复

  1. 出站默认换行分隔 JSON;解析层记录每条请求实际使用的入站帧类型,应答按同款帧类型镜像返回——既符合现行规范,又不破坏可能存在的存量手写调用方;
  2. initialize 维护已知版本白名单(至少 ["2024-11-05","2025-03-26","2025-06-18","2025-11-25","2026-07-28"]):命中则回显,否则回落最新已知版本;
  3. 入站解析改为「探测到 CL 头模式走帧解析,否则一律按行切分」,JSON 解析失败时向 stderr 输出诊断,不再以首字符启发式 gate。

交叉实证:我方约 1450 行零依赖手写 stdio 的多探针 J-Link MCP 应用完全相同的修复后,以客户端等价方式(换行 JSON + 提议 2026-07-28)完成 initialize → tools/list → ping 全链路自检,38 个工具全部通过;既有 CL 帧测试客户端回归行为不变。方案可行性经两侧交叉验证。

实际表现 · Actual behavior

缺陷清单

编号 位置 严重度 描述
DEF-1 L40–45 writeMessage() 高·致命 所有应答写成 Content-Length: n\r\n\r\n{json};客户端只解析换行分隔 JSON 回包,无法认领任何响应 → initialize 必超时
DEF-2 L125–137 stdin 解析循环 仅当缓冲区「含 \n 且首非空白字符为 {」才走换行路径;不带尾随换行的末条消息、或以 [ 开头的 JSON-RPC batch 将永久滞留缓冲区且无任何诊断输出
DEF-3 L74 initialize 分支 `protocolVersion: params?.protocolVersion

DEF-2、DEF-3 为潜在隐患,当前恰不可见,但与 DEF-1 同属一套不合规实现。

ZCode 版本 · ZCode version

客户端:ZCode CLI 0.16.5

设备 / 系统 / 浏览器 · Device / OS / Browser

Windows 10.0.26200 x64

截图 / 录屏 / 日志 · Screenshots / Recordings / Logs

No response

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 with mcp/hello-server.mjs, especially writeMessage() at L40–45, the initialize branch at L74, and the stdin parser at L125–137. Reproduce the issue by sending the initialize JSON line described and inspect stdout and stderr. Done means newline-delimited responses work with the real client, parsing handles the listed input cases, and known or unknown protocol versions receive the expected response without breaking existing CL-frame callers.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
api, cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.