From 3bd77d950e5e88456350dec4e29e3cbe1b74a1e9 Mon Sep 17 00:00:00 2001 From: LittleGuo Date: Wed, 14 Oct 2026 06:21:46 +0800 Subject: [PATCH] =?UTF-8?q?fix(frame):=20=E6=9C=AA=E7=9F=A5=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E9=94=99=E8=AF=AF=E7=A0=81=20-32601=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20-32602=EF=BC=8C=E7=AC=A6=E5=90=88=20MCP=20=E8=A7=84?= =?UTF-8?q?=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: "。 - 无需改 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 正常 --- ai.memory/project_stdio_bridge.md | 11 +++++++---- src/cad_mcp_frame/mcp_ConfigParser.cpp | 5 +++-- verify_mcp_frame.py | 6 +++--- verify_stdio_bridge.py | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ai.memory/project_stdio_bridge.md b/ai.memory/project_stdio_bridge.md index d77ad2a..a134ecc 100644 --- a/ai.memory/project_stdio_bridge.md +++ b/ai.memory/project_stdio_bridge.md @@ -24,7 +24,7 @@ type: project | 生命周期 | **没有** `initialize` / `initialized` / `ping` → **必须由桥合成** | | 通知 | 无 `id` 即不回包 | | `tools/list` | 26 个工具,`name`/`description`/`inputSchema(type=object)` 已是标准 MCP 形状 → **可原样转发** | -| 未知工具 | 回**顶层** `error:-32601`(不是 `result.isError`)→ 桥忠实透传 | +| 未知工具 | 回**顶层** `error:-32602`(`"Unknown tool: "`)→ 桥忠实透传 | ## 关键设计决策 @@ -37,9 +37,12 @@ type: project 3. **stdout 纯净性**:只用 `sys.stdout.buffer.write(... + b"\n")` + `flush()`(加锁)。 用 `buffer` 而非文本流,**规避 Windows 的 `\n`→`\r\n` 翻译**;所有日志只走 stderr。 4. **CAD 未启动**:桥照常启动,`initialize`/`ping` 正常,`tools/list` 回 `-32603`(可重试)。 -5. **不擅自改写语义**:基座对未知工具回顶层 `-32601`("Method not found")语义上偏 JSON-RPC - 方法层,严格说应是 `-32602` 或 `result.isError`。桥**不改写**;要修应改基座 - `mcp_server.cpp: HandleToolsCall` 的码值分类。 +5. **不擅自改写语义**:桥只做忠实透传,错误码一律由基座决定。 + (**2026-10-14 修正**:基座原先对未知工具回 `-32601`,语义偏 JSON-RPC 方法层; + 按 MCP 规范「未知工具属于**协议错误**,示例码 `-32602`」已改为 `-32602` + + `"Unknown tool: "`。改动点:`mcp_configParser.cpp: mcp_Tools::Call` + —— 只需改码值,`mcp_server.cpp: HandleToolsCall` 的协议错误白名单 + `{-32700,-32600,-32601,-32602}` 已含 `-32602`,分类逻辑无需动。) ## 验证方法(重要,含踩坑) diff --git a/src/cad_mcp_frame/mcp_ConfigParser.cpp b/src/cad_mcp_frame/mcp_ConfigParser.cpp index d95b9a3..a681ba4 100644 --- a/src/cad_mcp_frame/mcp_ConfigParser.cpp +++ b/src/cad_mcp_frame/mcp_ConfigParser.cpp @@ -255,8 +255,9 @@ nlohmann::json mcp_Tools::Call(const std::string &name, const nlohmann::json &ar return it->second->Execute(args); } - // 找不到工具时,返回标准的 JSON-RPC 错误对象 - return mcp_Tool_Utility::make_error(-32601, L"Specified tool not found: " + mcp_utils::utf8ToWString(name)); + // 找不到工具时,返回标准的 JSON-RPC 协议错误。 + // 依 MCP 规范,未知工具属于「协议错误」,示例码为 -32602 (Invalid params)。 + return mcp_Tool_Utility::make_error(-32602, L"Unknown tool: " + mcp_utils::utf8ToWString(name)); } void mcp_Tools::print() diff --git a/verify_mcp_frame.py b/verify_mcp_frame.py index 0e921f1..da1f7bc 100644 --- a/verify_mcp_frame.py +++ b/verify_mcp_frame.py @@ -49,7 +49,7 @@ def test_string_id(): def test_large_request(): - """构造 >2KB 的 arguments;旧基座会截断成 -32700,新基座应正常返回 -32601。""" + """构造 >2KB 的 arguments;旧基座会截断成 -32700,新基座应正常返回 -32602。""" big = {"blob": "x" * 5000} resp = json.loads( _call( @@ -62,8 +62,8 @@ def test_large_request(): ).decode() ) code = (resp.get("error") or {}).get("code") - assert code == -32601, f"期望 -32601(工具不存在),实际 {resp}" - print("[PASS] >2KB 请求未被截断(返回 -32601 而非 -32700)") + assert code == -32602, f"期望 -32602(未知工具,协议错误),实际 {resp}" + print("[PASS] >2KB 请求未被截断(返回 -32602 而非 -32700)") def test_notification_no_reply(): diff --git a/verify_stdio_bridge.py b/verify_stdio_bridge.py index 9806f2b..805f248 100644 --- a/verify_stdio_bridge.py +++ b/verify_stdio_bridge.py @@ -193,7 +193,7 @@ def main(): "params": {"name": "__no_such_tool__", "arguments": {}}}) r = bridge.recv() code = ((r or {}).get("error") or {}).get("code") - check("透传顶层 error.code == -32601", code == -32601, f"got {r!r}") + check("透传顶层 error.code == -32602(未知工具)", code == -32602, f"got {r!r}") check("error 响应的 id 正确", (r or {}).get("id") == 7, f"got {(r or {}).get('id')!r}") # ---- 8. 未知方法 ----