dsh-mcp-panel 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/README.es.md +17 -5
- package/README.hi.md +17 -5
- package/README.md +17 -5
- package/README.pt.md +17 -5
- package/README.zh.md +17 -5
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/docs/optimization-plan-v2.zh.md +325 -0
- package/docs/optimization-plan.zh.md +157 -0
- package/docs/research-notes.zh.md +114 -0
- package/docs/upstream-proposal.md +150 -0
- package/lib/client.js +283 -130
- package/lib/client.js.map +1 -1
- package/lib/index.js +87 -19
- package/lib/typert.host.js +2 -1
- package/lib/types/client/McpPanelTab.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +12 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/present.d.ts +29 -1
- package/lib/types/client/present.d.ts.map +1 -1
- package/lib/types/client/remote.d.ts +9 -9
- package/lib/types/command.d.ts +6 -0
- package/lib/types/command.d.ts.map +1 -1
- package/lib/types/probe.d.ts.map +1 -1
- package/lib/types/service.d.ts +11 -2
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/typert.host.d.ts +9 -9
- package/lib/types/wire.d.ts +21 -21
- package/lib/types/wire.d.ts.map +1 -1
- package/package.json +18 -8
- package/src/client/McpPanelTab.tsx +68 -14
- package/src/client/locales.ts +12 -0
- package/src/client/present.ts +49 -1
- package/src/client/styles.ts +32 -0
- package/src/command.ts +39 -4
- package/src/probe.ts +1 -1
- package/src/service.ts +62 -10
- package/src/wire.ts +3 -3
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# dsh-mcp-panel 完善与提升方案 v2
|
|
2
|
+
|
|
3
|
+
> 复核日期:2026-08-14。复核基线:本仓库当前工作区(含未提交的 Phase 2 改动)、本地 deepseek-harness checkout(workspace 包 `0.1.0-rc.5`,mainline `7b9644f`)、npm registry 实况、`dsh-plugin-guide` 知识库(§7 踩坑清单)。
|
|
4
|
+
> 本方案是对 `docs/optimization-plan-v2.zh.md` 的前一版 `docs/optimization-plan.zh.md` 的**续篇**:上版 22 项已基本实现(见 §1.1),本文档只列**尚未完成或新发现**的事项。
|
|
5
|
+
> 每项给出:现状证据、具体改动点(文件级)、验收标准、工作量(S <1h / M 1–3h / L >3h)、可行性结论。
|
|
6
|
+
|
|
7
|
+
## 0. 结论摘要
|
|
8
|
+
|
|
9
|
+
1. **当前工作区是红灯状态**:上一版 Phase 2 功能(面板轮询、探测按钮、被动探测、/mcp i18n、探测上限、事件陈旧度)已写入源码但**未收口**——`pnpm run typecheck` 有 7 个错误、`pnpm test` 84 例中 1 例失败、15 个文件未提交、文档(5 语言 README / cordis.patch.yml / CHANGELOG / AGENTS.md)未同步。**第一优先级是完成收口并提交**(§2),而不是叠加新功能。
|
|
10
|
+
2. **CI typecheck 可恢复**:上一版 A6 以"npm 类型包过旧"为由否决 CI typecheck;实测证据表明该前提已不成立——`@deepseek-ai/dsh-*` 全套 `0.1.0-rc.6` 已发布到 npm(`next` 标签),独立 spike 用纯 npm 类型闭包编译本仓库源码,错误输出与 checkout 版本**完全一致**(仅本仓库自身 7 错,无任何模块解析/类型闭包错误)。恢复 CI typecheck 可直接堵住当前这类"改一半"的回归(§3)。
|
|
11
|
+
3. **上游 `mcp/status` seam 仍未落地**:grep 本地 harness checkout 全部 `packages/` 与 `docs/`,`mcp/status` 事件、`McpStatusService` 均不存在,`docs/upstream-proposal.md` 也未出现在 harness 仓库。插件当前全部连接字段仍走 `unknown + statusSource: derived` 降级。提案文档已完备(本仓库 `docs/upstream-proposal.md`,含 PR contents 清单),**提交上游 PR 是让插件核心价值完整化的最高杠杆动作**(§6)。
|
|
12
|
+
4. **新增产品与工程机会**已逐项核查(§4/§5),均不触碰只读契约与诚实性约束。
|
|
13
|
+
|
|
14
|
+
### 可行性总览
|
|
15
|
+
|
|
16
|
+
| 编号 | 事项 | 可实施 | 关键前置/风险 | 工作量 |
|
|
17
|
+
|---|---|---|---|---|
|
|
18
|
+
| P0-1 | 修复 `CommandMessages.noTools` 接口签名(7 个 tsc 错误同源) | ✅ 直接修 | 无 | XS |
|
|
19
|
+
| P0-2 | 修复 2 个过时测试(aggregate probeStates / client-registration probe 类型) | ✅ 直接修 | 无 | XS |
|
|
20
|
+
| P0-3 | 全绿 + 提交 Phase 2 工作区 | ✅ | 依赖 P0-1/2 | S |
|
|
21
|
+
| P0-4 | 文档同步:5 语言 README、cordis.patch.yml、AGENTS.md | ✅ 内容现成 | 翻译量 | M |
|
|
22
|
+
| P0-5 | CHANGELOG [Unreleased] 条目化 + 版本号与 clientInfo 版本一致性 tripwire | ✅ | 依赖 P0-3 | S |
|
|
23
|
+
| P1-1 | CI typecheck 恢复(npm rc.6 类型闭包,已有 spike 证据) | ✅ | 需更新 pnpm-lock | M |
|
|
24
|
+
| P1-2 | CI 矩阵加 Node 24 | ✅ | 无 | XS |
|
|
25
|
+
| P1-3 | `.gitattributes` 固定 LF(消除 CRLF 警告) | ✅ | 无 | XS |
|
|
26
|
+
| P1-4 | harness checkout 兼容性 job(可选) | ✅ | 网络/仓库体积成本 | M |
|
|
27
|
+
| P2-1 | `/mcp <server> probe` 命令动作(与面板按钮/工具对称) | ✅ | jobs 缺失分支需文案 | S |
|
|
28
|
+
| P2-2 | `/mcp` 错误文案本地化(未知服务器提示目前硬编码英文) | ✅ | 无 | S |
|
|
29
|
+
| P2-3 | 工具过滤框改为按卡片独立状态(现为全局共享,跨卡片串扰) | ✅ | 无 | S |
|
|
30
|
+
| P2-4 | 轮询可见性感知(document.hidden 暂停/恢复) | ✅ | 无 | S |
|
|
31
|
+
| P2-5 | 探测按钮防重 + 探测行时间戳 | ✅ | 无 | S |
|
|
32
|
+
| P2-6 | 未配置命名空间徽标修正(leftover 行误标 "disabled") | ✅ 零 wire 变更 | 无 | XS |
|
|
33
|
+
| P2-7 | sanitizeUrl 补 URL fragment 凭据键脱敏 | ✅ | 无 | XS |
|
|
34
|
+
| P2-8 | 配置事实展示(failOnStartupError / reconnect 策略,derived 标注) | ✅ | 需 locale 文案 | S |
|
|
35
|
+
| P3-1 | 面板/命令扩展到 es/pt/hi(对齐 5 语言 README) | ✅ | 翻译 + 校对 | M–L |
|
|
36
|
+
| P4-1 | 向 deepseek-harness 提交 mcp/status 上游 PR | ✅ 提案完备 | harness 仓库门禁(测试/Agent Note/双语) | L |
|
|
37
|
+
| P4-2 | 上游落地后回归 + tripwire 清理 | ✅ | 依赖 P4-1 合入 | S |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. 现状核查(证据)
|
|
42
|
+
|
|
43
|
+
### 1.1 上一版方案落实状态
|
|
44
|
+
|
|
45
|
+
上一版 `docs/optimization-plan.zh.md`(2026-08-14,22 项)逐项核对源码:
|
|
46
|
+
|
|
47
|
+
| 项 | 状态 | 证据 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| C1 entryId 修正 | ✅ 已做 | `src/service.ts:147` 用 `entry.options.id`;`tests/command.spec.ts:121` nestedMcpRow 用例 |
|
|
50
|
+
| C2 提案入库 | ✅ 已做 | `docs/upstream-proposal.md`(随包分发);README 已改相对链接 |
|
|
51
|
+
| C3 README 五章×5 语言 | ✅ 已做 | README.md 含 Compatibility/Quick start/Uninstall/Honest by contract/Configuration/Permissions & data/Troubleshooting/Security;`README.zh.md` 章节结构一致 |
|
|
52
|
+
| A3 v0.1.0 发布 | ⚠️ 部分 | CHANGELOG 有 0.1.0 条目、README 引 `#v0.1.0`;但本机 clone **无 remote**,tag/Release 无法本地验证(见 §1.5) |
|
|
53
|
+
| A1 CI / A2 产物冒烟 / A4 probe 单测 / A5 客户端接线 / A7 dependabot | ✅ 已做 | `.github/workflows/ci.yml`、`scripts/verify-artifacts.mjs`、`tests/probe.spec.ts`(9 例)、`tests/client-registration.spec.ts`(6 例)、`.github/dependabot.yml` |
|
|
54
|
+
| B1 轮询 / B3 探测按钮 / B5 过滤框 / B4 命令 i18n / B6 探测上限 / B7 陈旧度 / B2 被动探测 | ⚠️ 已写未收口 | 源码全部就位(`refreshIntervalMs`、`mcpPanel/probe` 描述符、`toolQuery`、`outputLanguage`、`maxProbes`、`observedAt`、`passiveProbe*`),但见 §1.2 红灯 |
|
|
55
|
+
| C6–C9 打磨项 | ✅ 已做 | `styles.ts:83` focus-visible、attempt x/y、`THIRD_PARTY_NOTICES.md`、`probe.ts:43` 80 字符截断 |
|
|
56
|
+
|
|
57
|
+
### 1.2 工作区健康状态:红灯(本次实测)
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
$ pnpm run typecheck → 7 个错误(exit 2)
|
|
61
|
+
src/command.ts(70,12)/(94,12) TS7006 + TS2322 noTools 字典是函数、接口是 string
|
|
62
|
+
src/command.ts(169,21) TS2349 renderTools 调 messages.noTools(view.serverName)
|
|
63
|
+
tests/aggregate.spec.ts(104,82) TS2345 McpStatusFacts 缺 probeStates(B2 连带漏改)
|
|
64
|
+
tests/client-registration.spec.ts(93,32) TS6133 serverName 未使用
|
|
65
|
+
tests/client-registration.spec.ts(99,46) TS2353 probe 假实现返回类型过窄(ok:true 推断)
|
|
66
|
+
$ pnpm test → 84 例中 1 例失败(exit 1)
|
|
67
|
+
tests/aggregate.spec.ts > projects upstream status facts
|
|
68
|
+
TypeError: Cannot read properties of undefined (reading 'get') ← facts.probeStates 未传
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**根因**:上一版 Phase 2 是"多文件连带变更",`src/command.ts` 的接口行(`noTools: string`)与字典/调用点(函数化)改了一半;两个测试文件的连带更新漏了一半。`git status` 显示 15 个修改文件 + 2 个未跟踪测试文件(`tests/command-i18n.spec.ts`、`tests/service.spec.ts`)全部未提交。
|
|
72
|
+
|
|
73
|
+
### 1.3 文档缺口
|
|
74
|
+
|
|
75
|
+
- 5 语言 README 的 **Configuration 表只有 `probeEnabled`/`probeTimeoutMs` 两行**;`maxProbes`、`refreshIntervalMs`、`outputLanguage`、`passiveProbeEnabled`、`passiveProbeIntervalMs` 均未记载(grep 全仓 0 命中)。
|
|
76
|
+
- README「What you get」未提:面板探测按钮、被动探测徽标、轮询刷新、/mcp 双语文案。
|
|
77
|
+
- README Development 仍写「58 tests」(现 84)。
|
|
78
|
+
- `cordis.patch.yml` 只注释了 probeEnabled/probeTimeoutMs 两个键。
|
|
79
|
+
- `AGENTS.md` 布局段未提 `service.probe()` 与新配置键;`CHANGELOG.md` [Unreleased] 只有一行占位。
|
|
80
|
+
|
|
81
|
+
### 1.4 上游与生态状态
|
|
82
|
+
|
|
83
|
+
- **mcp/status seam 未落地**:`grep -r "mcp/status\|mcpStatus\|McpStatus" D:\deepseek-harness\packages` 零命中;harness `docs/` 无 `upstream-proposal.md`(glob 零命中)。`packages/mcp/mcp-client/src/` 仍是 connection/index/invariant/tools/transport 五文件,连接状态全部闭包私有。
|
|
84
|
+
- **transport 词汇与上游一致**:mcp-client 仅 `stdio` + `streamable-http` 两种(`src/transport.ts`),与本插件 `McpTransport` 词汇完全对齐;OAuth/PKCE 属另一生态位(`hyqhyq3/dsh-mcp-manager`),本插件边界不变。
|
|
85
|
+
- **npm rc.6 已发布(A6 否决前提失效)**:实测 `@deepseek-ai/dsh-tools|dsh-commands|dsh-client-runtime|dsh-mcp-client` 的 `latest` 均为陈旧的 `0.0.1-rc.1`、`next` 均为 `0.1.0-rc.6`;`dsh-typert-protocol` 的 `latest` 已是 `0.1.0-rc.6`。**Spike 证据**(`.rc6-check/spike`,已 gitignore):把 `src/` + `tests/` 复制到独立目录,devDeps 全用 npm `0.1.0-rc.6`(含 dsh-client-connection/runtime/locale/ui-settings/ui-slots)+ `react@18.3.1` + `@types/react@18.3.31`,tsconfig 去掉全部 `paths` 覆写后运行 tsc:输出与 checkout 版本**逐条一致**(仅上述 7 个自身错误,零模块解析错误)→ 纯 npm 类型闭包可完整解析本仓库源码。
|
|
86
|
+
- 对照 `dsh-plugin-guide` §7.3:本仓库已规避"cordis 双副本"(peer + dev 对齐 4.0.1)、"prepare 自包含"(`scripts/prepare.mjs` 只用 dependencies 内工具)、"latest 标签陈旧"(devDeps 显式 `0.1.0-rc.6`)、"noEmitOnError"(build 前 tsc 失败即 exit,`prepare.mjs:24` `result.status !== 0 → process.exit`)。
|
|
87
|
+
|
|
88
|
+
### 1.5 其他核查点
|
|
89
|
+
|
|
90
|
+
- **探测历史保留**:jobs 注册表对 unowned job 无属主清理路径(`packages/jobs/jobs/src/index.ts` 注释:清理仅发生在属主无法再收集其记录时),探测记录随进程存活,`maxProbes` 上限有效——无需改动。
|
|
91
|
+
- **发布状态无法本地验证**:本机 clone 无 `origin` remote(`git remote -v` 为空),tag/Release 检查需在带 remote 的环境执行;仓库根目录有一个 gitignored 的 `dsh-mcp-panel-0.1.0.tgz`(本地 pack 残留,可删除)。
|
|
92
|
+
- **诚实性约束复核**:`probeState` 与 `phase` 语义隔离(B2 按设计未覆盖 phase);`statusSource` 只在有上游 payload 时变 `upstream-event`;面板内容只走 `mcpPanel` remote 命名空间;`/mcp` 输出经 commands 服务落 `command/run`+`command/done`——均符合只读契约,无需修正。
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 2. Phase 0 —— 收口与发布基线(必做,先行)
|
|
97
|
+
|
|
98
|
+
### P0-1 · 修复 `CommandMessages.noTools` 接口签名(清 7 个 tsc 错误)
|
|
99
|
+
|
|
100
|
+
- 现状:`src/command.ts:47` 接口声明 `noTools: string`;`EN_MESSAGES`/`ZH_MESSAGES`(:70/:94)实现为 `server => …`;`renderTools`(:169)以函数调用。
|
|
101
|
+
- 改动:`src/command.ts:47` 改为 `noTools: (server: string) => string`(接口行与字典/调用点对齐;EN 为源)。
|
|
102
|
+
- 验收:`pnpm run typecheck` 归零错误。
|
|
103
|
+
|
|
104
|
+
### P0-2 · 修复两个过时测试
|
|
105
|
+
|
|
106
|
+
- `tests/aggregate.spec.ts:104`:`McpStatusFacts` 实参补 `probeStates: new Map()`(B2 连带)。
|
|
107
|
+
- `tests/client-registration.spec.ts:93`:假 probe 实现把未用形参改为 `_serverName`(或显式联合返回类型);`:99` 处假实现返回类型放宽为 `{ ok: false; error: { code: string; message: string } }` 可赋值的并集(mockResolvedValueOnce 的 error 分支目前撞窄类型)。
|
|
108
|
+
- 验收:`pnpm test` 84/84 全绿。
|
|
109
|
+
|
|
110
|
+
### P0-3 · 提交 Phase 2 工作区
|
|
111
|
+
|
|
112
|
+
- 提交拆分建议(同一 PR 内按主题分 commit):① i18n(command.ts/config.ts/index.ts + command-i18n.spec);② probe remote + 上限 + 陈旧度(wire/typert.host/remote/service + 相关测试);③ 轮询 + 被动探测 + 打磨(McpPanelTab/styles/aggregate + 相关测试)。
|
|
113
|
+
- 验收:`git status` 干净;每个 commit 后 `typecheck + test` 保持绿。
|
|
114
|
+
|
|
115
|
+
### P0-4 · 文档同步
|
|
116
|
+
|
|
117
|
+
- 5 语言 README(EN 为源):Configuration 表补 5 个新键(含义 + 默认值);「What you get」表补面板探测按钮、被动探测徽标、轮询刷新、双语 /mcp;Development 段测试数 58 → 84。
|
|
118
|
+
- `cordis.patch.yml`:注释补齐新键(默认值即最佳实践的键不必显式写,注释说明即可)。
|
|
119
|
+
- `AGENTS.md`:Layout 段补 `service.probe()`(第二个 remote 方法)与新配置键清单。
|
|
120
|
+
- 验收:`grep -r "refreshIntervalMs" README* cordis.patch.yml` 有命中;五个语言版本配置表行数一致。
|
|
121
|
+
|
|
122
|
+
### P0-5 · CHANGELOG 条目化 + 版本一致性 tripwire
|
|
123
|
+
|
|
124
|
+
- `CHANGELOG.md` [Unreleased] 按 P0-3 的三个主题写条目。
|
|
125
|
+
- 版本漂移防护:`src/probe.ts:32` 的 `PROBE_CLIENT_INFO.version = '0.1.0'` 是硬编码;新增一个单测断言其与 `package.json` 的 `version` 一致(读 `../package.json`,vitest 可解析 JSON)。
|
|
126
|
+
- 发布准备(需带 remote 的环境执行,本机无 remote):版本号决策(0.2.0 或 0.1.1)、`git tag`、Release notes 自 CHANGELOG。
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 3. Phase 1 —— 工程化门禁升级
|
|
131
|
+
|
|
132
|
+
### P1-1 · 恢复 CI typecheck(推翻 A6,基于 spike 证据)
|
|
133
|
+
|
|
134
|
+
- 现状:CI 无 tsc;上一版 A6 的否决理由是 npm 类型包过旧 + cordis 4.0.1 类型缺面。§1.4 证据表明两个前提均已失效:rc.6 全系已在 npm(`next` 标签),且 spike 证明纯 npm 类型闭包能完整编译本仓库源码(含 client 三面类型)。
|
|
135
|
+
- 改动:
|
|
136
|
+
1. devDependencies 增补:`@deepseek-ai/dsh-client-connection`、`dsh-client-runtime`、`dsh-client-locale`、`dsh-client-ui-settings`、`dsh-client-ui-slots`(均 `0.1.0-rc.6`)、`react@18.3.1`、`@types/react@18.3.31`。
|
|
137
|
+
2. 新增 `tsconfig.ci.json`:extends 主配置、清空 `paths`(npm 解析);主 `tsconfig.json` 的 checkout paths 保留给本地"mainline 最新类型"门禁——**双轨**:本地用 checkout、CI 用 npm,两者都跑 typecheck。
|
|
138
|
+
3. `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 增补新引入的 5 个 client 包。
|
|
139
|
+
4. `.github/workflows/ci.yml`:在 test 前加 `pnpm run typecheck:ci`(新 script:`tsc -p tsconfig.ci.json --noEmit`)。
|
|
140
|
+
- 风险与对策:npm rc.6 类型快照可能落后于 harness mainline 改名(如 0812 批量服务改名)——本地 checkout 门禁保持"主线新鲜度",CI 门禁保证"发布版本兼容性",漂移时两套报告对照即可定位。
|
|
141
|
+
- 验收:CI 全绿;人为引入一个类型错误能同时被本地与 CI typecheck 拦下。
|
|
142
|
+
|
|
143
|
+
### P1-2 · CI 矩阵加 Node 24
|
|
144
|
+
|
|
145
|
+
- 现状:engines 声明 `^22.19.0 || >=24.0.0`,CI 只跑 node 22。
|
|
146
|
+
- 改动:matrix 加 `24`。
|
|
147
|
+
- 验收:CI 双 OS × 双 Node 全绿。
|
|
148
|
+
|
|
149
|
+
### P1-3 · 行尾稳定化
|
|
150
|
+
|
|
151
|
+
- 现状:git 反复告警 "LF will be replaced by CRLF"(Windows 检出)。
|
|
152
|
+
- 改动:新增 `.gitattributes`(`* text=auto eol=lf`)。
|
|
153
|
+
- 验收:`git status` 不再出现行尾告警;`git diff --cached --check` 通过。
|
|
154
|
+
|
|
155
|
+
### P1-4 · harness 兼容性 job(可选)
|
|
156
|
+
|
|
157
|
+
- 现状:`scripts/verify-headless.mjs` 只能在有 checkout 的本机手动跑(README 已说明)。
|
|
158
|
+
- 改动:CI 可选 job(`workflow_dispatch` + 每月 cron):actions/checkout 本仓库 → 按 pin SHA checkout deepseek-harness → `pnpm install`(harness 侧)→ `node --import tsx/esm scripts/verify-headless.mjs`。
|
|
159
|
+
- 成本:harness 仓库体积大、安装耗时;收益:上游 mainline 漂移提前报警。标注为可选,不阻塞主 CI。
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 4. Phase 2 —— 产品完善
|
|
164
|
+
|
|
165
|
+
### P2-1 · `/mcp <server> probe` 命令动作
|
|
166
|
+
|
|
167
|
+
- 现状:面板有探测按钮、模型侧有 `mcp_probe` 工具,但 `/mcp` 命令没有对称动作;CLI 用户想从命令面板发起探测只能靠模型调用工具。
|
|
168
|
+
- 改动:`parseMcpArgs` 的动作白名单加 `probe`;`mcpCommand` handler 加分支调 `service.probe(server)`,输出保持 panel-only 契约(只回 job id + "结果在 Settings → Plugins → MCP");jobs 缺失/stdio 服务器分支复用 service 抛错文案;`usage` 行与 `input.hint` 同步更新;`EN_MESSAGES`/`ZH_MESSAGES` 加对应文案。
|
|
169
|
+
- 测试:`tests/command.spec.ts` 补 3 例(成功返回 job id、stdio 拒绝、jobs 缺失报错)。
|
|
170
|
+
- 验收:verify-headless 输出 `/mcp everything probe` 为「探测已启动(job …),结果仅面板可见」;单测全绿。
|
|
171
|
+
|
|
172
|
+
### P2-2 · 命令错误文案本地化
|
|
173
|
+
|
|
174
|
+
- 现状:`mcpCommand` 的 unknown-server 错误是硬编码英文(`Unknown MCP server "…" (configured: …)`),`outputLanguage: zh` 下仍是英文;机器可读字段(`status: unknown (source: derived)`)保留英文是有意设计(已有测试断言),不动。
|
|
175
|
+
- 改动:unknown-server 文案进 `CommandMessages`(en/zh 各一);handler 用字典渲染。
|
|
176
|
+
- 验收:`command-i18n.spec.ts` 补 zh 断言。
|
|
177
|
+
|
|
178
|
+
### P2-3 · 工具过滤框按卡片独立
|
|
179
|
+
|
|
180
|
+
- 现状:`McpPanelTab.tsx` 的 `toolQuery` 是组件级单值 state,多个展开的服务器卡片共享同一过滤词——在卡片 A 输入会同时过滤卡片 B,且各卡片输入框显示相同文本。
|
|
181
|
+
- 改动:改为 `Map<serverName, string>`(`useState<Record<string, string>>`),每卡片读写自己的键;展开状态不受影响。
|
|
182
|
+
- 验收:jsdom 组件测试(若启用组件渲染测试)或手工验证两个卡片过滤互不影响。
|
|
183
|
+
|
|
184
|
+
### P2-4 · 轮询可见性感知
|
|
185
|
+
|
|
186
|
+
- 现状:`refreshIntervalMs > 0` 时 `setInterval` 无视页面可见性;后台标签页空转请求(浏览器虽会节流,但切回时快照可能是旧值)。
|
|
187
|
+
- 改动:`document.hidden` 时暂停计时器,`visibilitychange` 恢复时立即 `reload()`;组件卸载清理不变。
|
|
188
|
+
- 验收:手工:切后台 2 分钟切回,立即出现新快照;无可见性 API 的环境(jsdom)不报错。
|
|
189
|
+
|
|
190
|
+
### P2-5 · 探测按钮防重 + 探测行时间戳
|
|
191
|
+
|
|
192
|
+
- 现状:`dmcp-probe-now` 无禁用态,双击会并发启动两个同目标探测;探测列表只显示 detail 文本,无起止时间。
|
|
193
|
+
- 改动:从快照 `probes` 判断该服务器是否存在 `running` 探测,存在则禁用按钮并显示"探测中";探测行加 `startedAt`/`finishedAt` 的本地化时间(locale 格式化或 `toLocaleTimeString`,无时钟进 presenter——时间格式在组件层做)。
|
|
194
|
+
- 验收:单测 presenter 不受影响;手工双击只产生一个 job。
|
|
195
|
+
|
|
196
|
+
### P2-6 · 未配置命名空间徽标修正
|
|
197
|
+
|
|
198
|
+
- 现状:`aggregate.ts` 对 leftover 命名空间(外来插件的 `mcp__` 工具,无 loader 行)产出 `enabled: false`,面板徽标显示"已停用"——误导(它根本不是配置项)。
|
|
199
|
+
- 改动:**零 wire 变更**——`entryId === ''` 即未配置标志;`present.ts` 的 `connectionBadge` 在 `view.entryId === ''` 时返回 `{ badge: 'unknown', tone: 'muted' }`;locale 无需新增(复用 `statusUnknown`)。
|
|
200
|
+
- 验收:`present.spec.ts` 补 leftover 行用例。
|
|
201
|
+
|
|
202
|
+
### P2-7 · sanitizeUrl 补 fragment 凭据键
|
|
203
|
+
|
|
204
|
+
- 现状:URL fragment(`#token=…`)不进请求但会进展示;`QUERY_CREDENTIAL` 只覆盖 `?`/`&` 对。
|
|
205
|
+
- 改动:`sanitize.ts` 对解析成功的 URL 同样扫描 `parsed.hash` 中的凭据键并替换;未解析回退路径的文本扫描正则同样覆盖 `#key=value`。
|
|
206
|
+
- 验收:`sanitize.spec.ts` 补 2 例(解析成功/未解析各一)。
|
|
207
|
+
|
|
208
|
+
### P2-8 · 配置事实展示(可选小项)
|
|
209
|
+
|
|
210
|
+
- 现状:重连预算等配置事实(`reconnect.enabled/maxAttempts`、`failOnStartupError`、`toolCallTimeoutMs`)用户完全不可见;上游 seam 未落地前 `maxAttempts` 一直是 `—`,但**配置意图**其实是可诚实展示的派生事实。
|
|
211
|
+
- 改动:面板 detail 区加"配置"行(从 loader 原始 config 读取,与 `deriveTarget` 同级的纯函数,标注 derived);`/mcp` 行保持紧凑不加(避免模型侧膨胀)。不得触碰 `phase`/`statusSource` 语义(诚实性约束 §7)。
|
|
212
|
+
- 验收:aggregate 单测 + 手工面板核验。
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 5. Phase 3 —— 多语言扩展(可选)
|
|
217
|
+
|
|
218
|
+
### P3-1 · 面板/命令扩展到 es/pt/hi
|
|
219
|
+
|
|
220
|
+
- 现状:README 五语言,但面板字典只有 zh/en(`client/locales.ts`)、命令只有 en/zh(`outputLanguage` 联合类型)。
|
|
221
|
+
- 改动:`outputLanguage` 联合类型加 `'es' | 'pt' | 'hi'` + 三套 `CommandMessages`(命令侧已完成并测试通过)。
|
|
222
|
+
- **面板字典受宿主限制**:`ctx.locale.register` 的类型面只接受 `'en' | 'zh'` 两个 UI 语言码(harness 的 `LocaleDictOf` face,实测 TS2353),标签页字典扩展需等宿主 locale 注册面支持更多语言码后再做——当前标签页随宿主 UI 语言在 en/zh 间切换,命令侧五语言独立生效。
|
|
223
|
+
- 验收:`resolveConfig` 新值通过;`command-i18n.spec.ts` 各语言快照断言(es/pt/hi 已加)。
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 6. Phase 4 —— 上游联动(可选,价值最高)
|
|
228
|
+
|
|
229
|
+
### P4-1 · 向 deepseek-harness 提交 mcp/status PR
|
|
230
|
+
|
|
231
|
+
- 现状:提案完备(本仓库 `docs/upstream-proposal.md` 含动机、面定义、6 个发射点、PR contents 清单、非目标);harness 侧零落地。
|
|
232
|
+
- 动作(在 harness 仓库执行,非本仓库):
|
|
233
|
+
1. `src/status.ts`(payload 类型、`McpStatusService`、事件声明)+ `src/connection.ts` 六个 `report()` 点 + `src/index.ts` 单例挂载 + README Observability 段 + `status.spec.ts`/`reconnect.spec.ts` 测试。
|
|
234
|
+
2. 遵守 harness 仓库门禁:Agent Note(非平凡变更)、双语文档、`typecheck/test`、事件 JSDoc `@mode emit`。
|
|
235
|
+
3. 本仓库的 `src/upstream.ts` 是**故意的 tripwire**:上游合入后,本地 checkout 门禁会立刻检验声明合并是否一致(冲突则编译失败)。
|
|
236
|
+
- 验收:harness PR 合入;本插件在 mainline checkout 上 `statusSource: upstream-event` 实转(verify-headless 输出变化)。
|
|
237
|
+
|
|
238
|
+
### P4-2 · 上游落地后回归
|
|
239
|
+
|
|
240
|
+
- 动作:verify-headless 重跑记录新输出;`docs/upstream-proposal.md` 头部状态改为 implemented 并指向合入 commit;`AGENTS.md`/README 的"expected until the upstream seam lands"表述更新。
|
|
241
|
+
- 验收:全绿 + 文档一致。
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 7. 边界与已否决项(沿用 + 新增)
|
|
246
|
+
|
|
247
|
+
- **只读契约不变**:任何新功能不得写配置文件、不得调 `Entry.update`(会持久化写回用户配置)、不得伪造运行时效果。§2–§5 全部满足。
|
|
248
|
+
- **诚实性约束**:探测可达性(probeState)永远与连接状态(phase)分离展示;`statusSource: derived` 不得伪装成 `upstream-event`。P2-8 只加"配置意图"事实并标注来源。
|
|
249
|
+
- **panel-only 契约**:探测细节永不进模型上下文;P2-1 的命令输出只回 job id + 面板指引(与 `mcp_probe` 工具一致)。
|
|
250
|
+
- **已否决:stdio 进程探测**。理由:mcp-client 经 SDK `StdioClientTransport` 持子进程句柄且对面板不可见;自行 spawn 第二实例有单实例锁/端口冲突/DB 独占风险,且需复刻 env scrub 语义。stdio 可达性只能等上游 seam。
|
|
251
|
+
- **已否决:OAuth/新传输支持**。mcp-client 目前只有 stdio + streamable-http,OAuth 属另一生态位;上游扩展后本插件再跟进词汇(watch item,无需现在写码)。
|
|
252
|
+
- **已否决:探测结果进入 `/mcp` 列表行**。probeState 已在面板行展示;模型侧输出保持现状(避免把 panel-only 数据升格为模型上下文)。
|
|
253
|
+
- **npm `latest` 标签陈旧**:devDeps 必须显式 `0.1.0-rc.6`(现状已如此),升级时核对 `next` 标签,防踩 0.0.1-rc.1。
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 8. 实施顺序与验收
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
Phase 0(P0-1 → P0-2 → P0-3 → P0-4 → P0-5) ← 先救红灯再谈新功能
|
|
261
|
+
Phase 1(P1-1 → P1-2 → P1-3 → [P1-4 可选])
|
|
262
|
+
Phase 2(P2-1 … P2-8,按列顺序)
|
|
263
|
+
Phase 3 / Phase 4(可选,各自独立可插队)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
- P0 完成后打版本 tag(在带 remote 的环境)。
|
|
267
|
+
- 每个 Phase 的验收命令:
|
|
268
|
+
`pnpm run typecheck && pnpm run typecheck:ci && pnpm test && pnpm run build && pnpm run verify:self-contained && pnpm run verify:artifacts`
|
|
269
|
+
- 命令输出变更(P2-1/P2-2)用 `scripts/verify-headless.mjs` 回归(需本机 checkout + 已装 profile)。
|
|
270
|
+
- 完成 P0–P2 后,本文档可归档;新想法继续在 v3 续篇记录。
|
|
271
|
+
|
|
272
|
+
## 9. 总结
|
|
273
|
+
|
|
274
|
+
17 项全部可实施,无一受阻。**最关键的是 P0**:当前工作区处于"功能已写、门禁红灯、文档滞后"的半完成状态,先把 7 个类型错误与 1 个失败测试修掉、把 Phase 2 收口提交并同步五语言文档,再谈提升。之后 P1-1(CI typecheck 恢复,有 spike 证据背书)能永久堵住同类回归;P2 八项把面板/命令打磨到与功能集相称的完整度;P4-1 上游 PR 则决定插件的终极价值上限。
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 10. 实施记录(2026-08-14 完成)
|
|
279
|
+
|
|
280
|
+
| 项 | 状态 | 证据 |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| P0-1/P0-2 修复红灯 | ✅ | `typecheck` 0 错;`pnpm test` 96/96 全绿 |
|
|
283
|
+
| P0-3 收口提交 | ✅ | 6 个主题提交(feat/docs/ci);工作区干净 |
|
|
284
|
+
| P0-4 文档同步 | ✅ | 5 语言 README 配置表 7 键齐全;cordis.patch.yml 注释齐全 |
|
|
285
|
+
| P0-5 CHANGELOG + tripwire | ✅ | `tests/version.spec.ts` 断言 clientInfo.version === package version |
|
|
286
|
+
| P1-1 CI typecheck 恢复 | ✅ | `tsconfig.ci.json`(无 paths)+ `typecheck:ci` script + npm rc.6 devDeps;本地双轨 typecheck 均绿 |
|
|
287
|
+
| P1-2 Node 24 矩阵 | ✅ | ci.yml matrix `node: [22, 24]` |
|
|
288
|
+
| P1-3 `.gitattributes` | ✅ | `* text=auto eol=lf`,行尾告警消除 |
|
|
289
|
+
| P1-4 兼容性 job | ✅ | `.github/workflows/compat.yml`(monthly + dispatch);本地全流程实测通过(见下) |
|
|
290
|
+
| P2-1..P2-8 产品项 | ✅ | 命令 probe 动作、错误本地化、按卡片过滤、可见性轮询、探测防重+时间戳、徽标修正、fragment 脱敏、configuredNote |
|
|
291
|
+
| P3-1 多语言 | ✅ 命令侧 | `outputLanguage: en\|zh\|es\|pt\|hi` 五套字典 + 测试;面板字典受宿主 locale 面限制(仅 en/zh,已记录) |
|
|
292
|
+
| P4-1 上游实现 | ✅ 本地 | harness checkout 分支 `feat/mcp-client-status-observability-seam`:`src/status.ts` + 六点 report + 测试(34/34)+ README 双语 + Agent Note 三件套;oxlint 0 错;仓库 typecheck 绿 |
|
|
293
|
+
| P4-1 上游 PR 提交 | ⛔ 外部受阻 | GitHub API 拒绝(详见 §11) |
|
|
294
|
+
| P4-2 落地回归 | ✅ | 端到端实测:真实 server-everything 行 → `/mcp` 输出 `status: connected (source: upstream-event)`;verify-headless 全链路绿 |
|
|
295
|
+
|
|
296
|
+
**P1-4 本地验证流程**(与 compat.yml 一致,全部实测):临时 `DSH_HOME` → `pnpm pack` 出 tarball → `dsh plugin --profile web add <tgz>` → `scripts/verify-headless.mjs`(脚本已补 launcher 的三步语义:`healProfilesModuleFallback`、空根配置写入、安装锚点向上探测 + `DSH_INSTALL_ANCHOR` 覆盖)→ web profile 启动、`mcpPanel/status` descriptor 注册、`/mcp` 走真实 commands 服务、`command/run`+`command/done` 落会话日志。
|
|
297
|
+
|
|
298
|
+
**verify-headless 修复**(本仓库):原脚本锚点写死 `../../../apps/cli`(假定仓库位于 checkout 两层之下),并漏了 launcher `prepareProfile` 的根配置写入与 fallback 愈合——在干净 profile 上分别表现为「无法定位安装」与「schemastery 解析失败」。现改为向上探测 2–6 层 + `DSH_INSTALL_ANCHOR` 覆盖。
|
|
299
|
+
|
|
300
|
+
**P3-1 面板侧边界**:`ctx.locale.register` 的类型面只接受 `'en' | 'zh'`(宿主 `LocaleDictOf` face,TS2353 实测),面板页签字典保持 en/zh 并随宿主 UI 语言切换;命令侧五语言不受此限。
|
|
301
|
+
|
|
302
|
+
## 11. 上游 PR 提交的阻塞条件(P4-1)
|
|
303
|
+
|
|
304
|
+
分支已推送至 fork:`PerryLink/deepseek-harness:feat/mcp-client-status-observability-seam`(rebase 于上游 master `47f9438`,单提交)。PR 创建被 GitHub 拒绝,证据(2026-08-14 实测):
|
|
305
|
+
|
|
306
|
+
- GraphQL `createPullRequest`:`PerryLink does not have the correct permissions to execute CreatePullRequest`。
|
|
307
|
+
- REST `GET/POST /repos/deepseek-ai/deepseek-harness/pulls`:HTTP 404(`gh api` 与裸 curl 一致;无 `X-GitHub-SSO` 头,排除 SSO 授权问题;同 token 对 fork 的 /pulls 正常返回)。
|
|
308
|
+
- GraphQL 查询:仓库 `open`/`merged`/`closed` PR 均为 0 条(与 master 历史中 `Merge pull request #2519` 矛盾——PR 历史已被清空/关闭)。
|
|
309
|
+
- 三轮重试(共 7 次创建尝试 + 双通道探测)结果一致,条件未变化;fork 分支 tip 已钉死在 seam 提交 `e1611e9`(共享 checkout 上其他会话的后续提交不会进入该分支)。
|
|
310
|
+
|
|
311
|
+
结论:上游仓库当前不向外部账号开放 Pull Requests 通道。手头交付物已就绪:fork 分支 + PR 正文(`Project/Plugins/pr-body-mcp-status-seam.md`)+ 交接说明(`Project/Plugins/pr-handoff.md`,含对比链接 `https://github.com/deepseek-ai/deepseek-harness/compare/master...PerryLink:feat/mcp-client-status-observability-seam`)——具备权限者可从网页一键开 PR;上游恢复 PR 通道后重跑 `gh pr create --repo deepseek-ai/deepseek-harness --head PerryLink:feat/mcp-client-status-observability-seam --base master`。**已获授权并完成 Discussions 移交**:https://github.com/deepseek-ai/deepseek-harness/discussions/1300(Show and tell 分类,含完整实现清单与一键 PR 链接)。
|
|
312
|
+
|
|
313
|
+
## 12. v0.3.0 实施记录(2026-08-15)
|
|
314
|
+
|
|
315
|
+
| 项 | 状态 | 证据 |
|
|
316
|
+
|---|---|---|
|
|
317
|
+
| 0.2.1 CHANGELOG 虚假声明修正 | ✅ | "jsdom 30/vitest 4/typescript 7" 依赖行从未发生(`git diff 616ebaf..b32ab8c -- package.json` 无依赖变更);amend 未发布 commit `b32ab8c` 删除该行 |
|
|
318
|
+
| 依赖升级真实落地 | ✅ | typescript 7.0.2 / vitest 4.1.10 / jsdom 30.0.1;`pnpm install` 后全门禁绿(109 tests) |
|
|
319
|
+
| 面板总览/过滤/展开折叠 | ✅ | `present.ts` 纯函数 `summarizePanel`/`filterServers`(与徽标派生同源,永不矛盾);多卡展开替代单开手风琴;新增 4 个单测 |
|
|
320
|
+
| 发布流水线 | ✅ | `scripts/release.mjs`(bump+盖章+门禁+commit+tag,失败回滚)、`check-tag-version.mjs`(CI tripwire)、`changelog-section.mjs`(Release notes);`release.yml` 由 `v*` tag 触发:门禁 → `npm publish --provenance` → GitHub Release(附 tarball) |
|
|
321
|
+
| compat pin 修复 | ✅ | 上游 master 被 force-push 回退(47f9438 ← 8c690c7 之后),旧 pin `8c690c7` 在远端已不可解析 → 月度 job checkout 必败;改 pin 为当前 master `47f9438`(fork 分支基准,已本地验证) |
|
|
322
|
+
| 包元数据 + 五语言 README 同步 | ✅ | `homepage`/`bugs`/`author`;npm 版本/下载/CI 徽章;快速上手版本 → 0.3.0;测试数 105 → 109;发布流程说明 |
|
|
323
|
+
| 发布 | ✅ | v0.3.0 tag + npm publish + GitHub Release(本次会话执行) |
|
|
324
|
+
|
|
325
|
+
**面板 5 语言仍受阻**:harness locale face `LOCALE_IDS = ['zh', 'en']`(`packages/client/locale/src/locale-settings.ts`,截至 47f9438),es/pt/hi 面板字典继续等待宿主扩展;命令侧五语言不受影响。
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# dsh-mcp-panel 优化提升方案
|
|
2
|
+
|
|
3
|
+
> 依据:2026-08-14 对仓库现状的逐项核查(CI、README 章节、entryId 数据源、probe 测试覆盖、产物冒烟、依赖闭包)。每项给出:现状证据、具体改动点、验收标准、工作量(S <1h / M 1–3h / L >3h)、可行性结论。
|
|
4
|
+
|
|
5
|
+
## 0. 可行性总览
|
|
6
|
+
|
|
7
|
+
| 编号 | 事项 | 可实施 | 关键前置/风险 | 工作量 |
|
|
8
|
+
|---|---|---|---|---|
|
|
9
|
+
| C1 | entryId 修正(disable/enable 建议行不可用) | ✅ 直接修 | 无 | S |
|
|
10
|
+
| C2 | 上游提案文档入库(README 死链) | ✅ 直接修 | 无 | S |
|
|
11
|
+
| C3 | README 补齐 5 章 × 5 语言 | ✅ 内容现成 | 翻译量 | M |
|
|
12
|
+
| A3 | v0.1.0 tag + CHANGELOG + Release | ✅ | 依赖 C1 先修 | S |
|
|
13
|
+
| A1 | CI(test+build+verify,无 tsc) | ✅ | 需 A2 脚本 | S–M |
|
|
14
|
+
| A2 | 构建产物冒烟(防 decorator 泄漏回归) | ✅ 直接加 | 无 | S |
|
|
15
|
+
| A6 | typecheck 进 CI | ⚠️ 不推荐 | 依赖闭包跨 ~6 包(证据见 §5.1),漂移成本 > 收益 | — |
|
|
16
|
+
| A4 | probe 网络路径单测 | ✅ | 无 | M |
|
|
17
|
+
| A5 | 客户端注册接线测试 | ✅ | 加 jsdom devDep | M |
|
|
18
|
+
| A7 | dependabot | ✅ | 无 | XS |
|
|
19
|
+
| B1 | 面板轮询刷新 | ✅ | 无 | M |
|
|
20
|
+
| B3 | 面板「探测」按钮(remote probe 方法) | ✅ | 复用 typert 双描述符模式 | M |
|
|
21
|
+
| B5 | 工具列表过滤框 | ✅ | 无 | S |
|
|
22
|
+
| B6 | 探测记录上限 | ✅ | 无 | XS |
|
|
23
|
+
| B7 | 上游事件陈旧度(observedAt) | ✅ | wire 变更需一次连带 | S |
|
|
24
|
+
| B4 | /mcp 输出 i18n | ✅ | 无 | M |
|
|
25
|
+
| B2 | 被动周期探测 | ✅(默认关) | 语义隔离:新增 probeState,不覆盖 phase | M |
|
|
26
|
+
| C4–C9 | 打磨小项 | ✅ | 无 | XS–S |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Phase 0 —— 正确性与发布基线(先行)
|
|
31
|
+
|
|
32
|
+
### C1 · 修复 entryId:建议行必须用用户可写的裸 id
|
|
33
|
+
- 现状:`src/service.ts` 用 `entry.id`(Loader 嵌套拼接,实测输出 `include:mcp-everything`);`cordis.patch.yml` 的 patch 按裸 id 匹配 → `/mcp <server> disable|enable` 给出的 `- set: { id: include:mcp-everything, … }` 会被 "entry not found" 跳过。
|
|
34
|
+
- 改动:
|
|
35
|
+
1. `src/service.ts`:`entryId: entry.options.id`(`options` 是序列化原配置,裸 id)。
|
|
36
|
+
2. `tests/harness.ts`:`FakeEntry.options` 增加 `id` 字段(与顶层 `id` 一致)。
|
|
37
|
+
3. 新增用例:`renderPatchSuggestion` 输出的 `id:` 与 loader 行的 `options.id` 一致(模拟嵌套 id:顶层 `id: 'include:mcp-everything'`、`options.id: 'mcp-everything'`)。
|
|
38
|
+
4. `scripts/verify-headless.mjs` 回归:`/mcp everything disable` 输出应为 `id: mcp-everything`。
|
|
39
|
+
- 验收:headless 输出 + 新单测全绿;live 快照 `entryId` 变为裸 id。
|
|
40
|
+
|
|
41
|
+
### C2 · 上游提案入库,消除死链
|
|
42
|
+
- 现状:README(5 语言)链接指向 `deepseek-ai/deepseek-harness/blob/master/docs/upstream-proposal.md`,该文件仅存在于本地 checkout(master 尚无)→ 死链;插件仓库 `docs/` 只有 research-notes。
|
|
43
|
+
- 改动:复制 `D:\deepseek-harness\docs\upstream-proposal.md` → `docs/upstream-proposal.md`(头部注明"权威提案目标为 deepseek-harness 仓库,本副本随插件分发");5 个 README 与 `docs/research-notes.zh.md` 的相关链接改为相对路径 `docs/upstream-proposal.md`。
|
|
44
|
+
- 验收:`grep -r 'deepseek-ai/deepseek-harness/blob/master/docs' .` 无遗留外部死链。
|
|
45
|
+
|
|
46
|
+
### C3 · README 补齐五章(对齐 awesome 目录 L2 判定)
|
|
47
|
+
- 现状:README 缺 Compatibility / Install & Uninstall / Permissions & data / Troubleshooting / License & security。
|
|
48
|
+
- 内容规格(全部事实现成,不新增承诺):
|
|
49
|
+
- **Compatibility**:peers 固定 `0.1.0-rc.6`;实测环境 = deepseek-harness checkout(rc.5,mainline `7b9644f`),最后验证日期 2026-08-14。
|
|
50
|
+
- **Install / Uninstall**:`dsh plugin add github:PerryLink/dsh-mcp-panel#v0.1.0` + 手动 patch 行;卸载 = 删 patch 行 + 删包(附 `--dump-config` 自检)。
|
|
51
|
+
- **Permissions & data**:只读 Loader/工具注册表/上游事件;不写任何文件;探测仅向**已配置的**端点发一次 initialize 请求(携带已配置 headers,值永不展示);无遥测。
|
|
52
|
+
- **Troubleshooting**:`dsh web --dump-config` 检查行;启动日志 FAILED 定位;面板"未知状态"属预期(见提案);回滚 = 移除行。
|
|
53
|
+
- **License & security**:Apache-2.0;问题请提 GitHub Issue(勿在 issue 中贴密钥)。
|
|
54
|
+
- 改动:5 个 README 同步(EN 为源,其余忠实翻译)。
|
|
55
|
+
- 验收:README 目录含上述 5 节;五个语言版本章节结构一致。
|
|
56
|
+
|
|
57
|
+
### A3 · 发布基线
|
|
58
|
+
- 改动:`CHANGELOG.md`(0.1.0 条目:功能清单 + 已验证环境);`git tag v0.1.0` + GitHub Release(Release Notes 从 CHANGELOG 生成);README 安装示例改用 `#v0.1.0`(5 语言)。
|
|
59
|
+
- 验收:tag 存在、Release 页可访问、README 无 `#main` 安装引用。
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Phase 1 —— 工程化门禁
|
|
64
|
+
|
|
65
|
+
### A2 · 构建产物冒烟(`scripts/verify-artifacts.mjs`)
|
|
66
|
+
- 背景:曾真实踩坑(stage-3 装饰器残留进 `lib/index.js` → ESM 语法错误),当前零防回归。
|
|
67
|
+
- 改动:
|
|
68
|
+
1. `node --check` 检查 `lib/index.js`、`lib/typert.host.js`、`lib/client.js`(语法层)。
|
|
69
|
+
2. 动态 `import()` 前两者并断言导出(`apply` / `TYPERT`)。
|
|
70
|
+
3. 断言 `lib/client.js` 含 `window.__ModuleLoader__.load({ id: "dsh-mcp-panel"`(banner 与 bundle id 契约)。
|
|
71
|
+
- 接线:`package.json` 增加 `verify:artifacts`;CI 在 build 后执行;`verify:self-contained` 保持不变。
|
|
72
|
+
|
|
73
|
+
### A1 · CI(`.github/workflows/ci.yml`)
|
|
74
|
+
- 矩阵:ubuntu-latest + windows-latest,node 22。
|
|
75
|
+
- 步骤:`pnpm/action-setup` → `pnpm install --frozen-lockfile` → `pnpm test` → `pnpm run build` → `pnpm run verify:self-contained` → `pnpm run verify:artifacts`。
|
|
76
|
+
- **不含 `tsc`**:typecheck 依赖 harness checkout 相对路径(见 A6 决策),在 CI 环境不可解析;此为显式记录而非遗漏。
|
|
77
|
+
|
|
78
|
+
### A6 · typecheck 边界(决策文档化,不做 vendoring)
|
|
79
|
+
- 证据:client 类型面的传递闭包跨 `dsh-api-remotes/client`(再导出 ~60 个跨包类型)、`dsh-client-connection/client`、`dsh-session/surface`、`react@18`、vendor cordis——手工同步的漂移成本超过收益;npm 发布版(0.0.1-rc.1)过旧且 cordis 4.0.1 类型缺 `Context.inject/plugin/get`。
|
|
80
|
+
- 改动:新增 `CONTRIBUTING.md`:说明 typecheck 是本地门禁、需 checkout 在固定相对位置;给出仅跑 `pnpm test && pnpm run build` 的 CI 等价路径。
|
|
81
|
+
|
|
82
|
+
### A4 · probe 网络路径单测(`tests/probe.spec.ts`)
|
|
83
|
+
- `vi.stubGlobal('fetch', …)` 六例:2xx+合法 initialize JSON(detail 含 server name/version 且脱敏);2xx+非 JSON(连通成功降级);非 2xx(`HTTP 404 …`);网络 reject(sanitizeError 路径);signal 已中止 → `timeout after … or cancelled`;长 serverInfo 截断(配合 C9)。
|
|
84
|
+
- 同时补 `probeJob`:`cancel()` 触发 abort;`done` 永不 reject。
|
|
85
|
+
|
|
86
|
+
### A5 · 客户端注册接线测试(`tests/client-registration.spec.ts`)
|
|
87
|
+
- 环境:加 `jsdom` devDep + `environment: 'jsdom'`(单文件级 `// @vitest-environment jsdom`)。
|
|
88
|
+
- fake `slots`/`locale`/`remote`(`$mount` 记录参数并 resolve);断言:`$mount` 收到与宿主 `TYPERT` 同源的 `MCP_PANEL_REMOTE`;`slots.inject('settings.plugins.tab')` 注册 `{ id: 'mcp', locale: NS }`;注入面 `status()` 解包 `RemoteResult`(ok / error 两分支)。
|
|
89
|
+
- 组件级渲染测试列 P2 可选(jsdom + react-dom/client + act)。
|
|
90
|
+
|
|
91
|
+
### A7 · dependabot(`.github/dependabot.yml`)
|
|
92
|
+
- weekly;`zod`、`tsdown`、`typescript`、`vitest`、`@types/node`;open-pull-requests-limit 5。
|
|
93
|
+
|
|
94
|
+
### B6 · 探测记录上限
|
|
95
|
+
- `src/config.ts` 增加 `maxProbes: number`(默认 10);`McpPanelService.probeViews()` 只取最近 N 条;`resolveConfig` 同步校验。
|
|
96
|
+
|
|
97
|
+
### B7 · 上游事件陈旧度
|
|
98
|
+
- `src/wire.ts`:`McpServerView` 增加 `observedAt: number | null`(事件接收时刻,service 在 `observe()` 记 `Date.now()`);zod schema、`aggregate.ts`、`tests/aggregate.spec.ts`、`present.ts`(注入 `now` 参数保持纯函数)、面板「最后事件 N 秒前」与测试一次性连带变更。
|
|
99
|
+
|
|
100
|
+
### C6/C7/C8/C9 · 打磨小项(与 Phase 1 同批)
|
|
101
|
+
- C6:`styles.ts` 增加 `.dmcp-card-content:focus-visible` 可见焦点。
|
|
102
|
+
- C7:面板 detail 行增加 `attempt x/y`(复用 `view.attempt/maxAttempts`,`-1` 显示 `—`)。
|
|
103
|
+
- C8:`THIRD_PARTY_NOTICES.md`(zod v4,MIT,内联于 host/client bundle 的归属声明)。
|
|
104
|
+
- C9:probe detail 的 serverInfo name/version 截断(如 80 字符)。
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Phase 2 —— 产品能力
|
|
109
|
+
|
|
110
|
+
### B1 · 面板轮询刷新
|
|
111
|
+
- `src/config.ts` 增加 `refreshIntervalMs`(默认 0 = 关闭,>0 轮询);`cordis.patch.yml` 与 README Config 表同步。
|
|
112
|
+
- `McpPanelTab.tsx`:`useEffect` 按 interval 重取;保持 presenter 纯函数不动;locale 无需新增。
|
|
113
|
+
|
|
114
|
+
### B3 · 面板「探测」按钮
|
|
115
|
+
- Host:`McpPanelService` 增加第二个 remote 方法 `probe(serverName)`(复用 `rawEndpoint` + `probeJob`;`ctx.get('jobs')` 缺失时抛明确错误);`src/wire.ts` 增加第二个 `InvocationDescriptor`(参数 `serverName: string`,strict codec;结果 `{ jobId, note }`);`src/typert.host.ts` 与 `src/client/remote.ts` 同步登记 + `TypertRemoteMap` 合并。
|
|
116
|
+
- Client:tab 中 streamable-http 行加「探测」按钮 → 调 remote → 立即重取快照(结果仍 panel-only);`mcp_probe` 工具保持不动。
|
|
117
|
+
- 测试:descriptor 双登记一致性断言;probe remote 的 jobs 缺失分支。
|
|
118
|
+
|
|
119
|
+
### B5 · 工具过滤框
|
|
120
|
+
- `McpPanelTab.tsx`:工具区加 `<input type="search">`(plugin-inventory 模式),过滤 public name/description;locale 加 `filterTools` 键(zh/en)。
|
|
121
|
+
|
|
122
|
+
### B4 · /mcp 输出 i18n
|
|
123
|
+
- `src/config.ts` 增加 `outputLanguage: 'en' | 'zh'`(默认 en);`src/command.ts` 的 renderers 改为接受消息字典参数;新增 `tests/command-i18n.spec.ts`(同一快照两种语言输出断言)。
|
|
124
|
+
|
|
125
|
+
### B2 · 被动周期探测(可选,默认关)
|
|
126
|
+
- `src/config.ts`:`passiveProbe: { enabled: false, intervalMs: 60000 }`。
|
|
127
|
+
- 语义隔离:wire 增加 `probeState: 'reachable' | 'unreachable' | null` + `probeCheckedAt: number | null`(**不覆盖** `phase`——探测可达性 ≠ 客户端自身连接状态);zod/aggregate/presenter/locale 连带;host 侧 effect-scoped `setInterval`(`timer.unref`)。
|
|
128
|
+
- 验收:默认关闭时行为零变化;开启后 http 行新增独立 probe 徽标。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 5. 风险与已否决项
|
|
133
|
+
|
|
134
|
+
### 5.1 已否决:typecheck 类型面 vendoring(A6)
|
|
135
|
+
闭包证据(2026-08-14 实测):`dsh-client-runtime/client` 直接依赖 `dsh-api-remotes/client`、`dsh-client-connection/client`、`dsh-session/surface`,其中 `dsh-api-remotes/client` 再导出约 60 个跨包类型——完整同步需要逐文件跟进 harness 版本,漂移风险与维护成本远超收益。CI 采用「test + build + 产物冒烟」门禁;typecheck 保留为 checkout 侧本地门禁并写入 CONTRIBUTING。
|
|
136
|
+
|
|
137
|
+
### 5.2 诚实性约束(所有探测类改动共用)
|
|
138
|
+
任何新增状态观测(B2/B3)都只添加**独立字段/来源标注**,绝不改写 `phase` 或把 `statusSource` 从 `derived` 伪装成 `upstream-event`。
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 6. 实施顺序与依赖
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
Phase 0(C1 → C2 → C3 → A3) ← 先修 bug 再发 tag
|
|
146
|
+
Phase 1(A2 → A1 → A4/A5/A7 + B6/B7 + C6–C9)
|
|
147
|
+
Phase 2(B1 → B3 → B5 → B4 → B2)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- C1 必须早于 A3(tag 不应包含已知 bug)。
|
|
151
|
+
- A1 依赖 A2(workflow 引用 verify:artifacts)。
|
|
152
|
+
- B7 是一次 wire 连带变更(zod/aggregate/presenter/tests 一个 commit 内完成)。
|
|
153
|
+
- 每项完成后跑:`pnpm run typecheck && pnpm test && pnpm run build && pnpm run verify:self-contained && pnpm run verify:artifacts`;命令输出变更用 `scripts/verify-headless.mjs` 回归。
|
|
154
|
+
|
|
155
|
+
## 7. 总体结论
|
|
156
|
+
|
|
157
|
+
22 项中 21 项可直接实施;唯一受阻项(A6 typecheck 进 CI)已给出明确否决理由与替代方案。建议按 Phase 0 → 1 → 2 顺序推进,每阶段一个 PR、一条 CHANGELOG 条目;Phase 0 合并后打 `v0.1.0`。
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# dsh-mcp-panel 可观测面调研笔记
|
|
2
|
+
|
|
3
|
+
调研对象:`D:\deepseek-harness\packages\mcp\mcp-client`(README、`src/index.ts`、
|
|
4
|
+
`src/connection.ts`、`src/transport.ts`、`src/tools.ts`、`src/invariant.ts`)、
|
|
5
|
+
`docs/config-catalog.zh.md` 的 `dsh-mcp-client` 段、`docs/subsystems/tools.zh.md`。
|
|
6
|
+
交付日期与上游提案 `docs/upstream-proposal.md` 同步(位于 harness 仓库)。
|
|
7
|
+
|
|
8
|
+
## ① dsh-mcp-client 是否暴露连接状态服务 / 事件 / 健康回调
|
|
9
|
+
|
|
10
|
+
**结论:不暴露。** 证据:
|
|
11
|
+
|
|
12
|
+
- `connection.ts` 的 `startConnection()` 把所有状态放在闭包局部变量里:
|
|
13
|
+
`client`、`clientClosed`、`disposers`、`reconnectTimer`、`failedAttempts`、
|
|
14
|
+
`connectedAt`、`firstAttemptError`、`syncChain`。返回值只有
|
|
15
|
+
`{ ready, dispose() }`,没有任何查询面。
|
|
16
|
+
- 全文件没有任何 `ctx.emit(...)` / `ctx.on(...)` 调用;唯一对外信号是
|
|
17
|
+
`ctx.logger` 文本(warn/error/info:reconnecting 含 attempt 数与 delay、
|
|
18
|
+
recovered、give-up、disabled-loss、re-sync 失败等,见 README "Behavior")。
|
|
19
|
+
- 该包自带的 invariant 伴随插件明说:
|
|
20
|
+
*"the bridge exposes no independent server-to-tool snapshot after an
|
|
21
|
+
asynchronous resync"*(`src/invariant.ts`)。
|
|
22
|
+
- 未注册任何 Cordis 服务;README "Services consumed" 只有 `ctx.tools`。
|
|
23
|
+
|
|
24
|
+
**推论**:连接状态、最近错误、重连计数在机制上不可观测;从
|
|
25
|
+
`ctx.tools.schemas()` 反推"已连接"会撒谎(断线期间最后一次成功
|
|
26
|
+
generation 仍注册着工具;`reconnect.enabled: false` 时同样如此)。
|
|
27
|
+
|
|
28
|
+
**是否提上游 PR:是。** 理由:状态本来就存在于 supervisor 里,外部无法
|
|
29
|
+
合法重建;最小 seam(`mcp/status` 事件 + `mcpStatus` 查询服务)不动
|
|
30
|
+
transport/OAuth/协议,成本低而所有面板类消费者都能受益。提案全文见
|
|
31
|
+
harness 仓库 `docs/upstream-proposal.md`;本插件同时实现了**消费该提案**
|
|
32
|
+
的一半(事件订阅 + 服务特性探测 + 诚实降级),上游落地后无需改本插件。
|
|
33
|
+
|
|
34
|
+
## ② 已注册工具名空间能否从 `ctx.tools.schemas()` 枚举
|
|
35
|
+
|
|
36
|
+
**能。** `ToolRuntime.schemas(scope?)`(`packages/core/tools/src/index.ts`)
|
|
37
|
+
返回面向模型的 `ToolSchema[]`(name / description / parameters 白名单,
|
|
38
|
+
不含 execute/output 等宿主字段)。MCP 工具的公开名是
|
|
39
|
+
`mcp__<serverName>__<rawName>`(超长/非法字符时经确定性归一化 + 12 位 hash,
|
|
40
|
+
纯函数 `publicToolName(serverName, rawName)`),因此按 `mcp__<server>__` 前缀
|
|
41
|
+
分组即可还原每个服务器的工具清单与描述。
|
|
42
|
+
|
|
43
|
+
注意事项:
|
|
44
|
+
|
|
45
|
+
- `schemas()` 无 scope 参数时是全局视图;面板要展示的是全局注册集。
|
|
46
|
+
- **不能用工具注册与否推断服务器在线**(见 ①)。
|
|
47
|
+
- 无工具注册的服务器(启动失败 + `failOnStartupError: false`、重连预算耗尽)
|
|
48
|
+
在 `schemas()` 里没有名字空间,行数据必须来自 Loader 配置,工具数记为 0。
|
|
49
|
+
- `tools/change`(emit)事件可用于实时刷新,但本插件用 Typert remote 的
|
|
50
|
+
`status()` 拉取快照,不需要在 Host 侧做推送。
|
|
51
|
+
|
|
52
|
+
## ③ 启停是否只能通过 patch 行 disabled
|
|
53
|
+
|
|
54
|
+
- mcp-client 自身没有运行时启停接口。
|
|
55
|
+
- Cordis Loader 其实有运行时通道:`Entry.update({ disabled })`
|
|
56
|
+
(`vendor/cordis/…/cordis-plugin-loader/src/config/entry.ts`)可以热卸载/
|
|
57
|
+
重挂载,但它会把改动**持久化写回**用户配置树——这正是本任务禁止的
|
|
58
|
+
"静默改动用户配置"。
|
|
59
|
+
- 因此按任务要求实现为**受控的 patch 建议**:`/mcp <server> disable|enable`
|
|
60
|
+
只输出建议的 `cordis.patch.yml` 行(`- set: { id, name, disabled }`)并说明
|
|
61
|
+
生效路径,绝不写文件、绝不伪造运行时效果。
|
|
62
|
+
- 生效路径已核实:web 长驻面板对 profile 级与 home 级 `cordis.patch.yml`
|
|
63
|
+
挂 `watchUserPatches` 热重载(`packages/boot/app-boot`);其他面板需重启。
|
|
64
|
+
Loader patch 语法核实自 `cordis-plugin-include` 的 `applyEntryPatches`。
|
|
65
|
+
|
|
66
|
+
## 冲突排查(GitHub topic mcp-manager / mcp-panel)
|
|
67
|
+
|
|
68
|
+
- **`1a125/dsh-mcp-manager`**("DSH global MCP manager",2026-08-13 push):
|
|
69
|
+
向 `~/.dsh/cordis.patch.yml` 写 mcp-client 行的配置编辑器;自称
|
|
70
|
+
"运行期即时连接/断开"(实际依赖配置热重载,且它直接改写用户全局配置);
|
|
71
|
+
状态仅"通过宿主工具注册表实时检测"(即 ① 里说明的不可靠推断);无错误
|
|
72
|
+
摘要、无重连计数、无脱敏。质量偏雏形。
|
|
73
|
+
- **`hyqhyq3/dsh-mcp-manager`**(2026-08-13 push):OAuth PKCE + 动态客户端
|
|
74
|
+
注册 + 自研 stdio/HTTP 传输——属于"扩展 mcp-client 能力"的另一生态位,
|
|
75
|
+
恰好落在本任务边界之外(不做 OAuth/远程传输、不重写 mcp-client)。
|
|
76
|
+
- **差异化定位(本插件)**:只读的运行时管理面,服务于**官方**
|
|
77
|
+
`dsh-mcp-client`:loader 配置事实(transport/target/enabled/fiber 状态)
|
|
78
|
+
+ `schemas()` 工具清单 + 上游提案落地后的真实连接状态/错误/重连计数 +
|
|
79
|
+
脱敏 + 受控 patch 建议 + 可选连通性探测。两个雏形均未做"错误 + 重连计数
|
|
80
|
+
+ 官方客户端只读观测"这一组合,无需合作,不重叠。
|
|
81
|
+
|
|
82
|
+
## 面板数据通道决策
|
|
83
|
+
|
|
84
|
+
- 任务要求 "client 插件 + session/投影"。逐条核对后**采用 Typert remote
|
|
85
|
+
服务**(Host 侧 `mcpPanel` 命名空间,客户端 `$mount` 手写
|
|
86
|
+
`TypertRemoteContribution`,模式同 `ui-settings-plugin-inventory` →
|
|
87
|
+
`remote.pluginInventory`):
|
|
88
|
+
- session 投影是**按会话**的持久值,由会话日志折叠而来;MCP 连接状态是
|
|
89
|
+
**应用级、运行期变化**的状态。apiproxy 里 `imageLimits` 用 `view` 读
|
|
90
|
+
活服务是被"boot-constant(进程生命周期内不变)"特批的,MCP 状态不满足;
|
|
91
|
+
硬塞进投影会污染每个会话日志且跨会话重复。
|
|
92
|
+
- 任务同时要求遵守 client 插件契约(`window.__DSH_BOOT__` 模块表、
|
|
93
|
+
presenter 纯函数)——remote 命名空间正是该契约下的宿主状态读取通道。
|
|
94
|
+
- 客户端 bundle:CJS + `window.__ModuleLoader__.load({ id, factory })` 包裹,
|
|
95
|
+
平台模块(react / cordis / dsh-client-ui-slots / dsh-client-runtime/client 等)
|
|
96
|
+
外部化,其余内联;`dsh.client` 元数据 + `exports["./client"]` 让
|
|
97
|
+
`client-modules` 自动挂到 `/plugins/dsh-mcp-panel/client.js`。
|
|
98
|
+
- Host 侧 typert:`exports["./typert"]` 手写 `TYPERT` 清单(与生成物同构,
|
|
99
|
+
逐条通过 `typert-loader` 校验:strict codec 必须带 zod v4 schema 等),
|
|
100
|
+
typert-loader 随 Loader 条目自动注册;`TypertRemoteService` 的
|
|
101
|
+
`typertRemote` 绑定由 gateway 自动发现导出。
|
|
102
|
+
|
|
103
|
+
## 硬性契约落实点
|
|
104
|
+
|
|
105
|
+
- **只读**:不改任何配置文件;disable/enable 只输出建议文本;面板无任何
|
|
106
|
+
写入口;连接状态在无上游数据时标 `unknown` + `statusSource: 'derived'`。
|
|
107
|
+
- **脱敏**:URL 查询串凭据键、userinfo、`Authorization: Bearer …`、JWT、
|
|
108
|
+
`key=value` 凭据键值全部在展示前清洗(`src/sanitize.ts`,纯函数,含极端
|
|
109
|
+
case 测试);config.headers 永不进快照。
|
|
110
|
+
- **面板内容不进模型上下文**:快照只走 remote RPC;`mcp_probe` 结果只进
|
|
111
|
+
面板(unowned 后台 job,无完成通知注入),工具返回值只有 job id。
|
|
112
|
+
- **`/mcp` 输出模型可读且可重建**:标准 `CommandResult` 文本,走官方
|
|
113
|
+
`commands` 服务(自动落 `command/run` + `command/done` 会话事件)。
|
|
114
|
+
- **不动 mcp-client**:传输/OAuth/协议零改动;上游 PR 提案单独成文。
|