Files
cad-agent/ai.memory/project_stdio_bridge.md
T
gjm 3bd77d950e fix(frame): 未知工具错误码 -32601 改为 -32602,符合 MCP 规范
MCP 规范《Tools / Error Handling》把「未知工具」归为**协议错误**,
示例即为 {"code": -32602, "message": "Unknown tool: invalid_tool_name"}。
基座原先回 -32601(Method not found),语义偏 JSON-RPC 方法层。

- mcp_configParser.cpp: mcp_Tools::Call 改 -32601 → -32602,
  消息改为 "Unknown tool: <name>"。
- 无需改 mcp_server.cpp 的 HandleToolsCall:其协议错误白名单
  {-32700,-32600,-32601,-32602} 已含 -32602,分类逻辑不变。
- 同步更新 verify_mcp_frame.py 与 verify_stdio_bridge.py 的断言
  (未知「方法」仍是 -32601,语义正确,不动)。

验证(重建基座 + 重启 CAD 后实测):
- 直连 TCP:{"error":{"code":-32602,"message":"Unknown tool: __no_such_tool__"}}
- verify_mcp_frame.py 4/4 PASS;verify_stdio_bridge.py 24/24 PASS
- 回归:verify_annotate.py 9/9、dwg_review --check 正常
- MCP Inspector 冒烟:tools/list 26 个工具、tools/call get_drawing_info 正常
2026-10-14 06:21:46 +08:00

66 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: stdio↔TCP 桥(MCP 标准化)
description: mcp_bridge 包——把标准 MCP 客户端的 stdio 桥接到 CAD 基座裸 TCP;含基座协议缺口、设计决策与用 MCP Inspector 验证的方法
type: project
---
# stdio ↔ TCP 桥(`mcp_bridge/`)
**状态:已实现并验证通过**(2026-10-14)。这是「MCP 标准化」路线的第二步
(第一步是基座 B 最小集,见 `project_review_agent_impl.md`)。
## 为什么需要它(非显而易见)
基座是**裸 TCP 自定义传输**,标准 MCP 客户端(Claude Desktop / CodeBuddy / MCP Inspector)
只实现 **stdio** 与 **Streamable HTTP**,因此**无法直连基座**。桥在客户端与基座之间做协议适配,
**CAD 侧零改动**。
## 基座的协议缺口(实测,决定桥要补什么)
| 项 | 事实 |
| --- | --- |
| 连接模型 | 一个连接**只处理一帧**,响应后立即 `closesocket`(`TaskData` 析构)→ 桥每次请求新建连接 |
| 支持方法 | **只有** `tools/list` / `tools/call`;其余回 `-32601` |
| 生命周期 | **没有** `initialize` / `initialized` / `ping` → **必须由桥合成** |
| 通知 | 无 `id` 即不回包 |
| `tools/list` | 26 个工具,`name`/`description`/`inputSchema(type=object)` 已是标准 MCP 形状 → **可原样转发** |
| 未知工具 | 回**顶层** `error:-32602`(`"Unknown tool: <name>"`)→ 桥忠实透传 |
## 关键设计决策
1. **位置**:顶层包 `mcp_bridge/`;TCP 复用 `dwg_review.mcp_client` 的 `raw_rpc()`
(新增:`_rpc(normalize=False)` 返回基座**原始响应 dict**,不做 error/isError 归一化,
这样桥才能把 `result` 与顶层 `error` 原样透传)。桥调用时 `retries=0`:
"发完即超时"语义有歧义,重试可能**重复执行写类工具**。
2. **并发**:**读线程 + 单工作线程队列**。`initialize`/`ping` 在读线程即时应答,
不会被慢工具阻塞;`tools/list`/`tools/call` 由单工作线程**串行**转发,保证调用顺序。
3. **stdout 纯净性**:只用 `sys.stdout.buffer.write(... + b"\n")` + `flush()`(加锁)。
用 `buffer` 而非文本流,**规避 Windows 的 `\n`→`\r\n` 翻译**;所有日志只走 stderr。
4. **CAD 未启动**:桥照常启动,`initialize`/`ping` 正常,`tools/list` 回 `-32603`(可重试)。
5. **不擅自改写语义**:桥只做忠实透传,错误码一律由基座决定。
(**2026-10-14 修正**:基座原先对未知工具回 `-32601`,语义偏 JSON-RPC 方法层;
按 MCP 规范「未知工具属于**协议错误**,示例码 `-32602`」已改为 `-32602` +
`"Unknown tool: <name>"`。改动点:`mcp_configParser.cpp: mcp_Tools::Call`
—— 只需改码值,`mcp_server.cpp: HandleToolsCall` 的协议错误白名单
`{-32700,-32600,-32601,-32602}` 已含 `-32602`,分类逻辑无需动。)
## 验证方法(重要,含踩坑)
- `verify_stdio_bridge.py`(仓库根):子进程拉起桥,24 项断言全绿(版本协商、通知不回包、
ping、`tools/list` 与基座**等价**、`tools/call`、错误透传、`-32700`、string id、
stdout 纯净性、死端口降级)。
- **用真实标准客户端(MCP Inspector)验证**——这是"标准化"的终极证明:
```bash
npx --yes @modelcontextprotocol/inspector --cli \
--config <配置文件> --server cad-autocad --method tools/list --strict
```
- 实测:`tools/list` 拿到 26 个工具;`--strict` 退出码 **0**(无 schema 可移植性问题);
`--method tools/call --tool-name get_drawing_info` 返回标准 `content` + `isError:false`。
- **踩坑**:Inspector CLI 在 Windows 上**参数解析很别扭**——用 `-- python -m mcp_bridge`
会把 `--method` 一起当成 target 传给 python(argparse 报错 → "Connection closed");
不用 `--` 又报 "Target is required"。
**可靠做法:写一个 `{"mcpServers":{...}}` 配置文件,用 `--config` + `--server`**。
这也顺带验证了用户实际要粘贴的配置格式。
- `python -m mcp_bridge --print-config` 可直接生成该配置片段(`PYTHONPATH` 用绝对路径,
避免依赖客户端的工作目录)。