54 lines
6.3 KiB
Markdown
54 lines
6.3 KiB
Markdown
# 迭代复盘:18-QMTBridgeMCP内建
|
||
|
||
> 复盘日期:2026-09-08 | 迭代状态:**验收通过(2026-09-08)** —— 口径:服务端自动化验收全绿 + MCP 工具端到端实测 + UI 接线核验(构建产物含新控件)+ 真实调用顺带确认(老师参与验收:重启宿主 / 指令修正 / MCP 实测)
|
||
> 关联需求:R-020(已定稿,2026-09-08)| 关联计划:PLAN-018
|
||
|
||
## 结果
|
||
|
||
迭代 18 达成(R-020 全范围):**QMT Bridge MCP 能力内建**——神之一手在 apply 内动态挂载 DSH 官方 @deepseek-ai/dsh-mcp-client(serverName 固定 QMT_Bridge_MCP,url 自动派生自激活连接 baseUrl+/mcp;挂载/切换/卸载/状态全由神之一手插件内管理);**三方依赖移除**(dsh-skill-mcp-panel 卸载 + 宿主 cordis.patch.yml 受管块清空);MCP 生命周期随 QMT 连接配置增删改与激活切换联动;状态出口 ×2(设置页「QMT 连接配置」MCP 状态条 + 会话头部指示灯区新增「MCP」灯);失败不崩(failOnStartupError:false + reconnect + 挂载 catch)。REST 直连架构不变(技术约束-003)。
|
||
|
||
## 验收证据(2026-09-08 实测)
|
||
|
||
| # | 验收项 | 结果 |
|
||
|---|---|---|
|
||
| 1 | 移除宿主受管块后工具仍可用 | ✅ cordis.patch.yml 清空 []、panel 已卸载;mcp__QMT_Bridge_MCP__qmt_list_apis 实际调用成功(QMT Bridge v2.0.0 返回) |
|
||
| 2 | MCP url 随激活自动派生 | ✅ 100.110.38.78:8610/mcp(激活 baseUrl+/mcp,Q3) |
|
||
| 3 | 激活切换联动 | ✅ 切 QMT_CYY → 192.168.3.43:8610/mcp connected;切回 GEMWIN 恢复 |
|
||
| 4 | 删除激活自动切默认 + MCP 重挂 | ✅ 删死地址连接 → 自动切默认 GEMWIN → MCP 恢复 connected + 10 工具 |
|
||
| 5 | 失败不崩(Q5) | ✅ 激活死地址 → state=error(fetch failed),插件其余功能正常 |
|
||
| 6 | mcp-status(refresh=检测按钮语义) | ✅ refresh:true 即时探测 connected + 10 |
|
||
| 7 | sync-status.mcp 域(头部灯数据源) | ✅ state/url/mounted/toolCount/latencyMs/checkedAt 齐全 |
|
||
| 8 | 客户端接线 | ✅ build 产物含「MCP」指示灯 + 设置页「检测 MCP」按钮 + header.actions/settings.section 注册 |
|
||
| 9 | MCP 工具端到端实测 | ✅ qmt_kline 取 000333.SZ 近一个月日 K(30 根 OHLCV,区间 -4.27%) |
|
||
| 10 | 构建 | ✅ typecheck + pnpm build 通过(lib/mcp/QmtMcpManager.js 产出) |
|
||
|
||
## 过程事实
|
||
|
||
1. **需求讨论定稿**:老师指令(2026-09-08)→ R-020 登记 → Q1-Q6 逐项拍板(只挂激活连接 / 移除三方 / url 自动派生 / 双状态出口 / 失败重连采纳 / 不做通用 MCP 管理);
|
||
2. **老师指令修正(关键)**:AI 一度把「动态挂载官方 dsh-mcp-client」误读为要自研协议,先撤依赖后澄清——老师明确:不要的是**三方 dsh-skill-mcp-panel**,**官方 @deepseek-ai/dsh-mcp-client 可以依赖**;文档同步更正(R-020 / PLAN-018 / 迭代目标 / 索引);
|
||
3. **依赖可行性实证(PLAN-018 步骤 1)**:@deepseek-ai/dsh-mcp-client@0.1.1-rc.2(registry 有)加为 dependencies,从插件 lib 实证可 import,模块导出 {name:mcp-client, inject:[tools], apply, Config} → ctx.plugin() 动态挂载可行(mcp_router 同款路径);@modelcontextprotocol/sdk@1.30.0 供独立状态探测(dsh-mcp-client 不暴露状态 API);
|
||
4. **实现**:src/mcp/QmtMcpManager.js(挂/卸/resync/probe/getStatus/dispose);index.js 接线(start + dispose + registerApi runtime);api/qmt-connections.js(mcp-status + activate/update/remove 三热切换点 resync);api/market.js(sync-status.mcp 域);客户端 SyncIndicators(四灯 + MCP)+ SettingsSection(MCP 状态条 + 检测按钮);tsdown 增 mcp 域入口;
|
||
5. **宿主迁移(老师执行,验收方确认)**:cordis.patch.yml 受管块清空 + dsh-skill-mcp-panel 卸载 + 宿主重启 → 神之一手实例接管;
|
||
6. **验收**:服务端 8 项 + MCP 实测 + 构建全绿(上表);
|
||
7. **提交推送**:c700f2f(迭代18,16 files)+ 9382ace(迭代17 遗留文档补提交)→ origin/main。
|
||
|
||
## 经验教训(复盘沉淀)
|
||
|
||
### 1. 「去掉对三方插件的依赖」要先问清:依赖边界到底指哪一层
|
||
AI 把老师「不希望依赖三方插件来加入 MCP 能力」误推到「也不能依赖 DSH 官方 mcp-client → 要自研 MCP 协议」,先动手撤了官方依赖、准备重造轮子;老师澄清后回撤。沉淀:**需求里「不依赖 X」先精确定位 X 是谁(管理面板 vs 执行插件),并确认官方能力是否允许复用;架构岔路口先问一句,别先动手拆**。
|
||
|
||
### 2. 动态挂载子插件 = ctx.plugin(module, config),前提是模块在插件依赖树内可解析
|
||
mcp_router 实证了「import 官方模块 → ctx.plugin()」路径;one-divine-lot 照做时关键前置是把官方模块加进自己的 dependencies(而非 peer 裸声明),并用 createRequire 从插件 lib 路径实证解析成功再动代码。沉淀:**动态挂载依赖模块时,先验证「插件运行路径能解析到模块」这个前置条件(技术约束-003 语义外的新增实践)**。
|
||
|
||
### 3. 状态查询与连接管理分离:dsh-mcp-client 不暴露状态 API
|
||
官方插件只管连接与工具注册,无状态查询口 → 用 @modelcontextprotocol/sdk 独立只读探测(initialize + tools/list 拿工具数/延迟),与官方自管连接互不干扰。沉淀:**外部能力库若不开状态口,用协议级只读探测补状态出口,不试图扒内部字段**。
|
||
|
||
### 4. 验收期间临时造数要小心「先清后补」
|
||
用死地址测失败语义时两次 add 同名 TEST-DEAD 未清理(返回结构读错导致 id 取空),留下两条残留连接;发现后立即清理、用正确返回结构重测。沉淀:**验收脚本对临时数据的增删要成对且校验返回值;测试前后核对连接列表与激活态**。
|
||
|
||
## 关联
|
||
|
||
- R-020(已定稿 → 已实现 → 本迭代验收通过);宿主三方 dsh-skill-mcp-panel 已移除(其「技能/MCP」设置菜单与受管块职责移交完成)
|
||
- 新增依赖:@deepseek-ai/dsh-mcp-client@0.1.1-rc.2、@modelcontextprotocol/sdk@1.30.0
|
||
- 若需新增设计约束条目(如「插件内建 MCP 客户端注册规范」)——本迭代未新增,留待后续讨论(R-020 定稿时标注"如需…于定稿时补充"未触发)
|