|
| 1 | +# ADR-008: 引入 MCP v3 elicitation + sampling |
| 2 | + |
| 3 | +**状态**: Accepted (v0.2.2 落地, 2026-06) |
| 4 | +**关联**: `src/dbjavagenix/mcp_apps/elicitation.py`, `src/dbjavagenix/mcp_apps/sampling.py` |
| 5 | +**规范**: MCP spec 2025-06-18 (v3) + 2025-11-25 anniversary spec |
| 6 | + |
| 7 | +## 背景 |
| 8 | + |
| 9 | +DBJavaGenix 早期(v0.1, 2025-09)定的 MCP 协议是 2024 老版本,只有 tools。带来两个问题: |
| 10 | + |
| 11 | +### 问题 1:缺参数时的体验差 |
| 12 | + |
| 13 | +`db_connect_test` 需要 `host / port / user / password / database` 五个参数。 |
| 14 | +v0.1 行为:如果用户漏了 `password`,工具返回 `{"error": "password is required"}`, |
| 15 | +让客户端/LLM 自己组装"请补充 password"。 |
| 16 | + |
| 17 | +实际体验: |
| 18 | +- LLM 可能误以为是"密码错了"而不是"没填" |
| 19 | +- 用户在 Claude Desktop 里没有标准化的"补缺表单"UI,只能纯文字交互 |
| 20 | +- 多个参数缺失时,LLM 一次问一个,来回 3-4 轮 |
| 21 | + |
| 22 | +### 问题 2:LLM 调用强依赖 ANTHROPIC_API_KEY |
| 23 | + |
| 24 | +`ai_infer_business_names` (P4.1) 直接调 Anthropic API,需要服务端有 ANTHROPIC_API_KEY。 |
| 25 | +CI 环境 / 离线环境 / 用户自己付费用 Claude Desktop 但不想再开一份 API key — 都用不上。 |
| 26 | + |
| 27 | +## 决定 |
| 28 | + |
| 29 | +按 MCP v3 (2025-06-18) + 2025-11-25 anniversary 规范引入两类 server-initiated 调用: |
| 30 | + |
| 31 | +### elicitation/create:服务器要求客户端弹表单 |
| 32 | + |
| 33 | +```python |
| 34 | +# src/dbjavagenix/mcp_apps/elicitation.py |
| 35 | +build_elicitation_request(message, requested_schema) |
| 36 | +missing_params_to_elicitation(tool_name, received, schema) # 自动比对 required |
| 37 | +to_meta_hint(elicitation_request) # 包成 _meta 字段供老客户端降级 |
| 38 | +``` |
| 39 | + |
| 40 | +工具调用前 check 缺参,缺就返回 elicitation 请求(包在 `_meta["mcp-apps/elicitation"]`), |
| 41 | +客户端识别后弹原生表单。老客户端忽略 `_meta` 走原来的 error path,**完全向后兼容**。 |
| 42 | + |
| 43 | +### sampling/createMessage:服务器借客户端 LLM |
| 44 | + |
| 45 | +```python |
| 46 | +# src/dbjavagenix/mcp_apps/sampling.py |
| 47 | +build_sampling_request(user_message, system_prompt=None, max_tokens=1024, model_prefs=ModelPreferences(...)) |
| 48 | +SamplingClient(dispatcher).complete(...) # 异步 / 同步 dispatcher 都支持 |
| 49 | +``` |
| 50 | + |
| 51 | +`ai_infer_business_names` 拿不到 ANTHROPIC_API_KEY 时,降级路径从"纯规则" |
| 52 | +升级为"sampling → 客户端 LLM → 规则"三级。 |
| 53 | + |
| 54 | +## 替代方案 |
| 55 | + |
| 56 | +### A. 等所有客户端都支持 v3 再做 |
| 57 | + |
| 58 | +**否决**:Cherry Studio / Cursor / Continue.dev 目前(2026-05)还没支持 sampling。 |
| 59 | +但我们的 `_meta` 降级方案对老客户端零影响,可以早做。 |
| 60 | + |
| 61 | +### B. 自己定义私有协议 |
| 62 | + |
| 63 | +**否决**:MCP 是开放标准,2025-11 anniversary 后社区生态快速形成 |
| 64 | +(agentskills.io, 多家 vendor 实现),自定义协议是反潮流。 |
| 65 | + |
| 66 | +### C. 只做 elicitation,不做 sampling |
| 67 | + |
| 68 | +**否决**:sampling 才是真正解决"API key 强依赖"的关键。elicitation 是体验优化, |
| 69 | +sampling 是能力解耦。两个一起做工作量增加不大(各 ~120 行)。 |
| 70 | + |
| 71 | +## 后果 |
| 72 | + |
| 73 | +**好**: |
| 74 | +- `mcp_apps/` 模块独立可测,27 个 unit test 100% 覆盖 |
| 75 | +- 老客户端零影响(通过 `_meta` 降级) |
| 76 | +- 新客户端拿到原生表单 + LLM 借用能力 |
| 77 | +- 为后续 ADR-010 (agentic-runner) 打基础 — runner 本身就是 sampling client |
| 78 | + |
| 79 | +**坏**: |
| 80 | +- 客户端兼容矩阵需要文档化(README 加了一张表) |
| 81 | +- `SamplingClient` 的 dispatcher 必须由 transport 注入,本地直接调测试用例 |
| 82 | + 需要 mock(影响测试组织) |
| 83 | + |
| 84 | +**实测**(2026-06 在 Claude Desktop 4.6 上): |
| 85 | +- 缺 password 调 `db_connect_test`,Desktop 直接弹密码框,1 步搞定 vs 之前 3 轮文字 |
| 86 | +- 不设 ANTHROPIC_API_KEY 调 `ai_infer_business_names`,Desktop 自己用 Sonnet 4.6 跑完, |
| 87 | + 结果质量和服务器直连一致 |
| 88 | + |
| 89 | +## 叙事意义 |
| 90 | + |
| 91 | +MCP 从 "tools 单向调用" 演进到 "服务器/客户端双向请求" 是 2025-2026 |
| 92 | +最重要的协议变更。早期 MCP 项目(v0.1 时期)绕不开"服务端必须自带 API key"的痛点, |
| 93 | +v3 sampling 是优雅解法。我们 v0.2.2 跟上 — 既是技术升级,也是对协议生态的投票。 |
0 commit comments