dsh-project-mcp-manager 0.4.3 → 0.7.1
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/README.md +79 -15
- package/docs/README.zh.md +63 -12
- package/docs/code-review/review-feat-adapt-dsh-0.2.0-rc.2.zh.md +190 -0
- package/docs/code-review/ts-review-7e0088d-to-804662f.zh.md +277 -0
- package/docs/code-review/ts-review-v0.4.3-to-v0.6.0.zh.md +366 -0
- package/docs/design/adaptation-dsh-0.1.2-rc1.md +1 -1
- package/docs/design/adaptation-dsh-0.1.5-rc1.md +1 -1
- package/docs/design/adaptation-dsh-0.1.5-rc2.md +59 -0
- package/docs/design/adaptation-dsh-0.1.6-alpha.2.md +262 -0
- package/docs/design/adaptation-dsh-0.2.0-rc.2.md +89 -0
- package/docs/design/proposal-runtime-robustness-and-json-interop.md +380 -0
- package/docs/guide/cli.md +33 -2
- package/docs/guide/cli.zh.md +24 -2
- package/docs/guide/env-expansion.md +10 -1
- package/docs/guide/env-expansion.zh.md +7 -1
- package/docs/guide/format.md +56 -4
- package/docs/guide/format.zh.md +47 -4
- package/docs/guide/layers.md +46 -5
- package/docs/guide/layers.zh.md +32 -3
- package/docs/releases/v0.6.0.md +120 -0
- package/docs/releases/v0.7.0.md +41 -0
- package/docs/releases/v0.7.1.md +49 -0
- package/lib/cli.js +424 -35
- package/lib/dsh-paths.js +5 -1
- package/lib/index.js +5 -3
- package/lib/json-file.js +146 -45
- package/lib/json-write.js +50 -29
- package/lib/model.js +280 -20
- package/lib/registry.js +912 -272
- package/lib/service.js +10 -0
- package/lib/status.js +53 -6
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -3,10 +3,33 @@
|
|
|
3
3
|
English | [中文](docs/README.zh.md)
|
|
4
4
|
|
|
5
5
|
A project-level MCP auto-loading plugin for DSH: write MCP server configs in
|
|
6
|
-
`<projectRoot>/.dsh/mcp.yml` and they are mounted
|
|
7
|
-
official `@deepseek-ai/dsh-mcp-client`) whenever a dsh
|
|
8
|
-
project. Changes to the file hot-reload into the running
|
|
9
|
-
visibility is scoped per session cwd. No UI — core
|
|
6
|
+
`<projectRoot>/.dsh/mcp.yml` or `.dsh/mcp.json` and they are mounted
|
|
7
|
+
automatically (via the official `@deepseek-ai/dsh-mcp-client`) whenever a dsh
|
|
8
|
+
session opens in that project. Changes to the file hot-reload into the running
|
|
9
|
+
dsh process, and tool visibility is scoped per session cwd. No UI — core
|
|
10
|
+
functionality only.
|
|
11
|
+
|
|
12
|
+
**Capability boundary** (dsh ≥ 0.2.0-rc.2): the official client owns the
|
|
13
|
+
protocol, reconnect, tool names, resources, and server instructions. Shipped
|
|
14
|
+
profiles already mount shared MCP resource tools. Official configuration is
|
|
15
|
+
a profile-layer Cordis patch (with that layer's own HMR) plus
|
|
16
|
+
`plugin_manager`. This plugin still owns what the host does not:
|
|
17
|
+
|
|
18
|
+
1. Project-level discovery of `<projectRoot>/.dsh/mcp.yml`, `.dsh/mcp.json`,
|
|
19
|
+
and the read-only legacy `.mcp.json`.
|
|
20
|
+
2. Tool visibility isolated by session cwd.
|
|
21
|
+
3. The MCP file format and the `dsh-mcp` CLI.
|
|
22
|
+
|
|
23
|
+
Project-file hot reload is this plugin's file watcher. It does not replace
|
|
24
|
+
official profile HMR. **Transport types are decided by the official client.**
|
|
25
|
+
v0.7.x targets the dsh `0.2.0` line starting at 0.2.0-rc.2. Hosts still on
|
|
26
|
+
dsh 0.1.5 should stay on plugin v0.6.0.
|
|
27
|
+
|
|
28
|
+
If this plugin is useful, a GitHub
|
|
29
|
+
[star](https://github.com/wldxiaobai/dsh-project-mcp-manager) is appreciated.
|
|
30
|
+
Bugs, host mismatches, or ideas belong in
|
|
31
|
+
[Issues](https://github.com/wldxiaobai/dsh-project-mcp-manager/issues) — even a
|
|
32
|
+
short report helps.
|
|
10
33
|
|
|
11
34
|
## Documentation
|
|
12
35
|
|
|
@@ -21,16 +44,26 @@ Feature documentation lives in `docs/`, English and Chinese side by side:
|
|
|
21
44
|
its diagnostics.
|
|
22
45
|
- [CLI `dsh-mcp`](docs/guide/cli.md) — scopes, write formats, ownership contract.
|
|
23
46
|
|
|
24
|
-
Design and release records (Chinese): [dsh 0.
|
|
47
|
+
Design and release records (Chinese): [dsh 0.2.0-rc.2 adaptation](docs/design/adaptation-dsh-0.2.0-rc.2.md) ·
|
|
48
|
+
[dsh 0.1.6-alpha.2 adaptation plan](docs/design/adaptation-dsh-0.1.6-alpha.2.md) ·
|
|
49
|
+
[dsh 0.1.5-rc.2 adaptation](docs/design/adaptation-dsh-0.1.5-rc2.md) ·
|
|
50
|
+
[dsh 0.1.5-rc.1 adaptation](docs/design/adaptation-dsh-0.1.5-rc1.md) ·
|
|
25
51
|
[dsh 0.1.2-rc.1 adaptation](docs/design/adaptation-dsh-0.1.2-rc1.md) ·
|
|
26
52
|
[JSON config layer proposal](docs/design/proposal-json-mcp-config.md) ·
|
|
53
|
+
[Runtime robustness & JSON interop proposal](docs/design/proposal-runtime-robustness-and-json-interop.md) ·
|
|
54
|
+
[v0.7.1 release notes](docs/releases/v0.7.1.md) ·
|
|
55
|
+
[v0.7.0 release notes](docs/releases/v0.7.0.md) ·
|
|
56
|
+
[v0.6.0 release notes](docs/releases/v0.6.0.md) ·
|
|
27
57
|
[v0.4.3 release notes](docs/releases/v0.4.3.md) ·
|
|
28
58
|
[v0.4.2 release notes](docs/releases/v0.4.2.md) ·
|
|
29
59
|
[v0.4.1 release notes](docs/releases/v0.4.1.md) ·
|
|
30
60
|
[v0.4.0 release notes](docs/releases/v0.4.0.md) ·
|
|
31
61
|
[v0.3.1 release notes](docs/releases/v0.3.1.md).
|
|
32
62
|
|
|
33
|
-
Code review records (Chinese): [TypeScript changes since v0.3.1](docs/code-review/ts-review-since-v0.3.1.zh.md)
|
|
63
|
+
Code review records (Chinese): [TypeScript changes since v0.3.1](docs/code-review/ts-review-since-v0.3.1.zh.md) ·
|
|
64
|
+
[v0.4.3 to v0.6.0](docs/code-review/ts-review-v0.4.3-to-v0.6.0.zh.md) ·
|
|
65
|
+
[7e0088d to 804662f (fix follow-up)](docs/code-review/ts-review-7e0088d-to-804662f.zh.md) ·
|
|
66
|
+
[feat/adapt-dsh-0.2.0-rc.2 (v0.7.0)](docs/code-review/review-feat-adapt-dsh-0.2.0-rc.2.zh.md).
|
|
34
67
|
|
|
35
68
|
## Installation (mount into a profile)
|
|
36
69
|
|
|
@@ -57,7 +90,7 @@ dsh plugin --profile web add dsh-project-mcp-manager@latest
|
|
|
57
90
|
|
|
58
91
|
# Install a specific version (check available versions with
|
|
59
92
|
# npm view dsh-project-mcp-manager versions)
|
|
60
|
-
dsh plugin --profile web add dsh-project-mcp-manager@0.
|
|
93
|
+
dsh plugin --profile web add dsh-project-mcp-manager@0.7.1
|
|
61
94
|
```
|
|
62
95
|
|
|
63
96
|
**Option 2: install directly with pnpm** (equivalent to option 1):
|
|
@@ -84,8 +117,9 @@ pnpm add link:<path-to-your-dsh-mcp-project-source> # e.g. D:\dev\dsh-mcp-proj
|
|
|
84
117
|
> trigger the bundle reconcile.
|
|
85
118
|
|
|
86
119
|
**Upgrading / pinning versions**: re-run the `add` command from option 1 with
|
|
87
|
-
the desired version suffix — `@latest` upgrades to the newest release, `@0.
|
|
88
|
-
pins to a specific version.
|
|
120
|
+
the desired version suffix — `@latest` upgrades to the newest release, `@0.7.1`
|
|
121
|
+
pins to a specific version. v0.7.x needs dsh 0.2.0-rc.2 (the `0.2.0` line).
|
|
122
|
+
dsh 0.1.5 keeps working with plugin `@0.6.0`.
|
|
89
123
|
|
|
90
124
|
## Build & test
|
|
91
125
|
|
|
@@ -104,8 +138,12 @@ pnpm test # node test/test-model.mjs / test-mcp-file / test-json-file /
|
|
|
104
138
|
- **Mounting**: each `(project, serverName)` pair in the project layers mounts
|
|
105
139
|
one `@deepseek-ai/dsh-mcp-client` instance (`ctx.plugin`) on the host ctx and
|
|
106
140
|
registers it into the global tool layer; multiple sessions inside the same
|
|
107
|
-
project share a single connection. **
|
|
108
|
-
|
|
141
|
+
project share a single connection. **Project-layer fibers are created only
|
|
142
|
+
for projects with a live session or the process cwd**; after the last
|
|
143
|
+
session leaves (and the project is not cwd) servers unmount following a
|
|
144
|
+
5 minute grace while the catalog entry and watcher remain. **Every
|
|
145
|
+
user-layer row mounts exactly one instance** (global, independent of the
|
|
146
|
+
number of projects) — see
|
|
109
147
|
[configuration sources and layers](docs/guide/layers.md).
|
|
110
148
|
- **Hot reload**: chokidar watches each project root (depth 2, ignoring
|
|
111
149
|
node_modules/.git/.hg/.svn), but only edits to the **exact** config files of
|
|
@@ -113,10 +151,12 @@ pnpm test # node test/test-model.mjs / test-mcp-file / test-json-file /
|
|
|
113
151
|
`<projectRoot>/.dsh/mcp.json` and `<projectRoot>/.mcp.json` — trigger a full
|
|
114
152
|
reconciliation after a 150 ms debounce: added rows are mounted, removed rows
|
|
115
153
|
are unmounted, and config changes are remounted. A second watcher covers the
|
|
116
|
-
user layer as
|
|
117
|
-
`~/.dsh/mcp.json
|
|
118
|
-
|
|
119
|
-
|
|
154
|
+
user layer as **exact file paths** — `~/.dsh/mcp.yml`,
|
|
155
|
+
`~/.dsh/mcp.json`, `~/.dsh/profiles/<active profile>/mcp.json`, and
|
|
156
|
+
`$DSH_HOME/dsh-mcp.json` (the other plugin's global store; watched only so
|
|
157
|
+
creating it can be diagnosed, never mounted). chokidar v5 can deliver an
|
|
158
|
+
event for a watched missing file when it is created, as long as its parent
|
|
159
|
+
directory exists — never the home directory at large.
|
|
120
160
|
- **Profile name resolution**: derived from the loader root include's
|
|
121
161
|
`config.path` (`~/.dsh/profiles/<name>/cordis.yml`) or `ctx.baseUrl`, and
|
|
122
162
|
overridable with `DSH_MCP_PROFILE=<name>`; when it cannot be resolved the
|
|
@@ -151,3 +191,27 @@ longer fanned out per project. Lines that fail to mount or are invalid are
|
|
|
151
191
|
skipped with a warning and do not affect other servers. Claude user-state
|
|
152
192
|
monoliths such as `~/.claude.json` (mixing credentials with project history)
|
|
153
193
|
are **no longer read at all** as of v0.4.0.
|
|
194
|
+
|
|
195
|
+
## Coexistence with other MCP manager plugins
|
|
196
|
+
|
|
197
|
+
This plugin and `@wingsky-1/dsh-mcp-manager` both auto-load per-project MCP
|
|
198
|
+
servers, but they do **not** share a file format:
|
|
199
|
+
|
|
200
|
+
1. **Project files are mutually incompatible.** This plugin reads
|
|
201
|
+
`{ mcpServers: { … } }` in `<projectRoot>/.dsh/mcp.json`. The other plugin
|
|
202
|
+
stores `{ version, servers: [] }` at the same path. A missing `mcpServers`
|
|
203
|
+
key is a legal empty layer here, so the other format would otherwise look
|
|
204
|
+
like "I configured it but nothing happens". The loader now writes a
|
|
205
|
+
diagnostic naming that format and suggesting `mcpServers` or
|
|
206
|
+
`.dsh/mcp.yml`. The same hint applies to `~/.dsh/dsh-mcp.json`. If that
|
|
207
|
+
file already uses this plugin's `mcpServers` dialect, the diagnostic tells
|
|
208
|
+
you to move the object into `mcp.json` — it is still not loaded from the
|
|
209
|
+
other plugin's filename.
|
|
210
|
+
2. **The same `serverName` can be started twice** (once by each plugin).
|
|
211
|
+
stdio servers may contend for ports or exclusive resources.
|
|
212
|
+
3. **Prefer one plugin per project**, or keep this plugin on `.dsh/mcp.yml`
|
|
213
|
+
and the other on `.dsh/mcp.json`.
|
|
214
|
+
|
|
215
|
+
`globalNames()` only sees official loader patch rows, not tools registered by
|
|
216
|
+
the other plugin at runtime, so rename-to-avoid-collision does **not** cover
|
|
217
|
+
that other instance.
|
package/docs/README.zh.md
CHANGED
|
@@ -2,11 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
[English](../README.md) | 中文
|
|
4
4
|
|
|
5
|
-
项目级 MCP 自动加载插件:在项目根 `<projectRoot>/.dsh/mcp.yml`
|
|
6
|
-
服务器配置,在该项目开启 dsh 会话时自动装载(经官方
|
|
5
|
+
项目级 MCP 自动加载插件:在项目根 `<projectRoot>/.dsh/mcp.yml` 或
|
|
6
|
+
`.dsh/mcp.json` 写入 MCP 服务器配置,在该项目开启 dsh 会话时自动装载(经官方
|
|
7
7
|
`@deepseek-ai/dsh-mcp-client`),文件改动热重载到运行中的 dsh 进程,并按
|
|
8
8
|
会话 cwd 控制工具可见性。无 UI,仅具备核心功能。
|
|
9
9
|
|
|
10
|
+
**能力边界**(dsh ≥ 0.2.0-rc.2):协议、重连、工具名、resources 与服务器
|
|
11
|
+
instructions 归官方 client。发行版 profile 已经装了共享的 MCP 资源工具。官方
|
|
12
|
+
配置是 profile 层 Cordis patch(该层自有 HMR)加上 `plugin_manager`。宿主还没做、
|
|
13
|
+
仍由本插件负责的是:
|
|
14
|
+
|
|
15
|
+
1. 项目级发现:`<projectRoot>/.dsh/mcp.yml`、`.dsh/mcp.json`,以及只读的遗留
|
|
16
|
+
`.mcp.json`。
|
|
17
|
+
2. 按会话 cwd 隔离工具可见性。
|
|
18
|
+
3. MCP 专用配置格式和 `dsh-mcp` CLI。
|
|
19
|
+
|
|
20
|
+
项目文件热重载是本插件自己的文件监听,不替代官方 profile HMR。**传输类型由官方
|
|
21
|
+
client 决定。** v0.7.x 面向 dsh `0.2.0` 线(从 0.2.0-rc.2 起)。仍在 dsh 0.1.5
|
|
22
|
+
上的宿主继续用插件 v0.6.0。
|
|
23
|
+
|
|
24
|
+
若这个插件对你有帮助,欢迎给仓库点一颗
|
|
25
|
+
[star](https://github.com/wldxiaobai/dsh-project-mcp-manager)。遇到问题、宿主
|
|
26
|
+
不适配或有想法,请开
|
|
27
|
+
[Issue](https://github.com/wldxiaobai/dsh-project-mcp-manager/issues)——哪怕几句话也很有用。
|
|
28
|
+
|
|
10
29
|
## 文档
|
|
11
30
|
|
|
12
31
|
功能说明已拆分到 `docs/`,中英双版并存:
|
|
@@ -18,16 +37,26 @@
|
|
|
18
37
|
- [`${VAR}` 展开](guide/env-expansion.zh.md)——装载时插值与对应诊断。
|
|
19
38
|
- [CLI `dsh-mcp`](guide/cli.zh.md)——作用域、写入格式与独占契约。
|
|
20
39
|
|
|
21
|
-
设计与发布记录(中文):[dsh 0.
|
|
40
|
+
设计与发布记录(中文):[dsh 0.2.0-rc.2 适配记录](design/adaptation-dsh-0.2.0-rc.2.md) ·
|
|
41
|
+
[dsh 0.1.6-alpha.2 适配方案](design/adaptation-dsh-0.1.6-alpha.2.md) ·
|
|
42
|
+
[dsh 0.1.5-rc.2 适配记录](design/adaptation-dsh-0.1.5-rc2.md) ·
|
|
43
|
+
[dsh 0.1.5-rc.1 适配记录](design/adaptation-dsh-0.1.5-rc1.md) ·
|
|
22
44
|
[dsh 0.1.2-rc.1 适配记录](design/adaptation-dsh-0.1.2-rc1.md) ·
|
|
23
45
|
[JSON 配置层设计提案](design/proposal-json-mcp-config.md) ·
|
|
46
|
+
[运行时稳健性与 JSON 互通提案](design/proposal-runtime-robustness-and-json-interop.md) ·
|
|
47
|
+
[v0.7.1 发布说明](releases/v0.7.1.md) ·
|
|
48
|
+
[v0.7.0 发布说明](releases/v0.7.0.md) ·
|
|
49
|
+
[v0.6.0 发布说明](releases/v0.6.0.md) ·
|
|
24
50
|
[v0.4.3 发布说明](releases/v0.4.3.md) ·
|
|
25
51
|
[v0.4.2 发布说明](releases/v0.4.2.md) ·
|
|
26
52
|
[v0.4.1 发布说明](releases/v0.4.1.md) ·
|
|
27
53
|
[v0.4.0 发布说明](releases/v0.4.0.md) ·
|
|
28
54
|
[v0.3.1 发布说明](releases/v0.3.1.md)。
|
|
29
55
|
|
|
30
|
-
代码审查记录(中文):[v0.3.1 以来 TypeScript 变更审查](code-review/ts-review-since-v0.3.1.zh.md)
|
|
56
|
+
代码审查记录(中文):[v0.3.1 以来 TypeScript 变更审查](code-review/ts-review-since-v0.3.1.zh.md) ·
|
|
57
|
+
[v0.4.3 至 v0.6.0](code-review/ts-review-v0.4.3-to-v0.6.0.zh.md) ·
|
|
58
|
+
[7e0088d 至 804662f(审查落地复查)](code-review/ts-review-7e0088d-to-804662f.zh.md) ·
|
|
59
|
+
[feat/adapt-dsh-0.2.0-rc.2(v0.7.0)](code-review/review-feat-adapt-dsh-0.2.0-rc.2.zh.md)。
|
|
31
60
|
|
|
32
61
|
## 安装(挂载到 profile)
|
|
33
62
|
|
|
@@ -50,7 +79,7 @@ npm install -g deepseek-ai/dsh # 或从 GitHub 源码安装
|
|
|
50
79
|
dsh plugin --profile web add dsh-project-mcp-manager@latest
|
|
51
80
|
|
|
52
81
|
# 安装指定版本(版本号可先 npm view dsh-project-mcp-manager versions 查看)
|
|
53
|
-
dsh plugin --profile web add dsh-project-mcp-manager@0.
|
|
82
|
+
dsh plugin --profile web add dsh-project-mcp-manager@0.7.1
|
|
54
83
|
```
|
|
55
84
|
|
|
56
85
|
**方式二:直接 pnpm 安装**(与方式一等价):
|
|
@@ -71,11 +100,12 @@ pnpm add link:<你的 dsh-mcp-project 源码目录> # 例如 D:\dev\dsh-mcp-pr
|
|
|
71
100
|
> **dsh ≥ 0.1.2 注意**:插件是否生效取决于 profile 的 `dsh.profile.bundles`,
|
|
72
101
|
> 而单纯 `pnpm add link:` **不会**把包写进 bundles。方式一/方式二会自动补齐;
|
|
73
102
|
> 若你手写了 pnpm 命令,请再跑一次任意 `dsh plugin --profile web list`(或
|
|
74
|
-
>
|
|
75
|
-
> bundle reconcile。
|
|
103
|
+
> 用 `dsh --profile web --dump-config` 检查合成结果里有没有
|
|
104
|
+
> `dsh-project-mcp-manager` 行)触发 bundle reconcile。
|
|
76
105
|
|
|
77
106
|
**升级/锁定版本**:重跑方式一的 `add` 命令并带上目标版本后缀——`@latest`
|
|
78
|
-
升级到最新,`@0.
|
|
107
|
+
升级到最新,`@0.7.1` 锁定到指定版本。v0.7.x 需要 dsh 0.2.0-rc.2(`0.2.0` 线)。
|
|
108
|
+
dsh 0.1.5 继续用插件 `@0.6.0`。
|
|
79
109
|
|
|
80
110
|
## 构建与测试
|
|
81
111
|
|
|
@@ -91,15 +121,19 @@ pnpm test # node 直跑 test/ 下六个 .mjs(model / mcp-file / json-
|
|
|
91
121
|
向上找最近的含 `.git` 的祖先目录作为项目根(无 `.git` 时退回目录本身)。
|
|
92
122
|
- **装载**:项目层每个 `(项目, serverName)` 在宿主 ctx 上装载一个
|
|
93
123
|
`@deepseek-ai/dsh-mcp-client` 实例(`ctx.plugin`),注册进全局工具层,同一
|
|
94
|
-
|
|
124
|
+
项目内多会话共享同一连接。**项目层只给有活跃会话或进程 cwd 的项目发起装载**;
|
|
125
|
+
最后一次会话离开且该项目不是 cwd 后,宽限 5 分钟再卸载服务器,条目与文件监听
|
|
126
|
+
保留。**用户层每行只装载一个实例**(全局,与项目数无关)
|
|
95
127
|
——详见[配置来源与分层](guide/layers.zh.md)。
|
|
96
128
|
- **热重载**:chokidar 监听各项目根(depth 2,忽略 node_modules/.git/.hg/
|
|
97
129
|
.svn),但只有**已知项目根的精确配置文件**(`<projectRoot>/.dsh/mcp.yml`、
|
|
98
130
|
`<projectRoot>/.dsh/mcp.json` 与 `<projectRoot>/.mcp.json`)的改动经 150ms
|
|
99
131
|
防抖触发全量对账:新增行装载、删除行卸载、配置变化重装。另有独立 watcher 以
|
|
100
|
-
**精确文件路径**监听用户层:`~/.dsh/mcp.yml`、`~/.dsh/mcp.json
|
|
101
|
-
`~/.dsh/profiles/<当前 profile>/mcp.json
|
|
102
|
-
|
|
132
|
+
**精确文件路径**监听用户层:`~/.dsh/mcp.yml`、`~/.dsh/mcp.json`、
|
|
133
|
+
`~/.dsh/profiles/<当前 profile>/mcp.json`,以及 `$DSH_HOME/dsh-mcp.json`
|
|
134
|
+
(对方插件的全局存储;只监听以便创建时能立刻诊断,从不装载)。chokidar v5
|
|
135
|
+
对被监听的缺失文件能在其创建时补发事件,前提是父目录已存在——不监听家目录
|
|
136
|
+
整体。
|
|
103
137
|
- **profile 名解析**:从 loader 根 include 的 `config.path`
|
|
104
138
|
(`~/.dsh/profiles/<name>/cordis.yml`)或 `ctx.baseUrl` 推导,可用
|
|
105
139
|
`DSH_MCP_PROFILE=<name>` 覆盖;解析不出时不读 profile 层(其余层照常)。
|
|
@@ -123,3 +157,20 @@ pnpm test # node 直跑 test/ 下六个 .mjs(model / mcp-file / json-
|
|
|
123
157
|
连接,所有项目可见),不再按项目 fan-out。装载失败/配置无效行仅告警跳过,不影响
|
|
124
158
|
其他服务器。`~/.claude.json` 这类 Claude 用户态单体文件(混存凭据与项目历史)
|
|
125
159
|
自 v0.4.0 起**完全不再读取**。
|
|
160
|
+
|
|
161
|
+
## 与同类插件共存
|
|
162
|
+
|
|
163
|
+
本插件与 `@wingsky-1/dsh-mcp-manager` 都做按项目自带 MCP,但**文件格式互不兼容**:
|
|
164
|
+
|
|
165
|
+
1. **项目文件格式互不兼容。** 本插件读 `<projectRoot>/.dsh/mcp.json` 里的
|
|
166
|
+
`{ mcpServers: { … } }`;对方在同一路径存 `{ version, servers: [] }`。缺
|
|
167
|
+
`mcpServers` 在本插件是合法空层,对方格式会表现为「我配了但没生效」。装载器
|
|
168
|
+
现在会写一条诊断,指认该格式并建议改用 `mcpServers` 或 `.dsh/mcp.yml`。
|
|
169
|
+
全局 `~/.dsh/dsh-mcp.json` 同样提示;若该文件已经是本插件的 `mcpServers`
|
|
170
|
+
方言,诊断会建议把对象搬到 `mcp.json`——仍不会从对方文件名装载。
|
|
171
|
+
2. **同名服务器会被两个插件各启动一次**,stdio 可能互相抢端口或独占资源。
|
|
172
|
+
3. **建议同一项目只启用一个**,或让两者分居 `mcp.yml`(本插件)与
|
|
173
|
+
`.dsh/mcp.json`(对方)。
|
|
174
|
+
|
|
175
|
+
`globalNames()` 只读官方 loader patch 行,看不到对方运行时注册的工具,因此
|
|
176
|
+
「改名避让」不会覆盖对方实例。
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# 代码审查:feat/adapt-dsh-0.2.0-rc.2(v0.7.0,适配 dsh 0.2.0-rc.2)
|
|
2
|
+
|
|
3
|
+
[← 返回 README](../README.zh.md) | 相关:[上一轮审查(7e0088d 至 804662f)](ts-review-7e0088d-to-804662f.zh.md) ·
|
|
4
|
+
[dsh 0.2.0-rc.2 适配记录](../design/adaptation-dsh-0.2.0-rc.2.md) ·
|
|
5
|
+
[v0.7.0 发布说明](../releases/v0.7.0.md)
|
|
6
|
+
|
|
7
|
+
**审查日期**:2026-09-30 | **分支**:`feat/adapt-dsh-0.2.0-rc.2`(HEAD `1d586a9`)
|
|
8
|
+
**基线**:`dev`(merge-base `1af9887`)| **静态分析**:SonarCloud PR #13
|
|
9
|
+
([issues 列表](https://sonarcloud.io/project/issues?id=wldxiaobai_dsh-project-mcp-manager&pullRequest=13&issueStatuses=OPEN%2CCONFIRMED&s=IMPACT_RANK),
|
|
10
|
+
OPEN/CONFIRMED 共 24 条,技术债合计 120 min)
|
|
11
|
+
|
|
12
|
+
**范围**:4 个 commit,21 文件 `+583 / −954`。代码逻辑只动了 4 个 `src` 文件
|
|
13
|
+
(`model.ts` +23、`registry.ts` +1/−8、`json-file.ts` +2、`json-write.ts` +2/−1),
|
|
14
|
+
其余是依赖、锁文件与文档。
|
|
15
|
+
|
|
16
|
+
| commit | 内容 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `aea9aea` | `dsh-mcp-client` `^0.1.5-rc.1` → `^0.2.0-rc.2`,cordis `^4.0.2` → `^4.0.4`,重写 `minimumReleaseAgeExclude` |
|
|
19
|
+
| `1893ec5` | 删除 `agent/session-start` 监听 |
|
|
20
|
+
| `b2f945e` | 透传可选 `maxInstructionBytes`(yml / JSON / view / CLI 写 JSON) |
|
|
21
|
+
| `1d586a9` | 版本 0.7.0、CHANGELOG、README、适配记录、发布说明 |
|
|
22
|
+
|
|
23
|
+
**关于 PR #13 的口径**:GitHub 上 PR #13 是 `dev → main`(标题 "Dev",无描述),
|
|
24
|
+
6 个 commit、21 文件 `+844 / −953`。这与本地 `origin/dev..feat/adapt-dsh-0.2.0-rc.2`
|
|
25
|
+
的统计完全一致,即 PR #13 = 本分支 4 个 commit + `1af9887`(0.1.6-alpha.2 方案文档)
|
|
26
|
+
+ `e76eb1c`(README 提示)。因此 Sonar 结果可以直接对应本分支。
|
|
27
|
+
|
|
28
|
+
**方法**:通读 `dev...feat` 全部 diff 与相关上下文;逐条对照 Sonar 问题所在行;
|
|
29
|
+
本地验证见第 5 节。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 1. 结论
|
|
34
|
+
|
|
35
|
+
**可以合入,建议先处理 S1(未处理的 Promise)。** 本分支自身的改动小而准:
|
|
36
|
+
依赖范围、事件删除、新字段透传三处都和适配记录里的上游变化一一对应,测试覆盖了
|
|
37
|
+
新字段的透传与缺省省略。
|
|
38
|
+
|
|
39
|
+
Sonar 的 24 条问题**没有一条落在本分支改动的行上**:它们全在 `dev` 已有代码里
|
|
40
|
+
(创建日期 2026-08-22 至 2026-09-12,早于本分支),因为 PR 碰了 `registry.ts`
|
|
41
|
+
而被一并列出(S9381/S9382/S9383 是较新的规则编号)。其中只有 3 条 S9383 是真实的
|
|
42
|
+
可靠性问题,其余属于有意设计或风格项。
|
|
43
|
+
|
|
44
|
+
| 级别 | 数量 | 说明 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| 中 | 1 | S1:构造函数里三处 `enqueue(...)` 未处理 rejection(Sonar S9383 ×3) |
|
|
47
|
+
| 低 | 5 | 分支自身 B1–B4;Sonar S7503 / S9381 风格项 |
|
|
48
|
+
| 接受现状 | 15 | Sonar S9382「循环内 await」:串行化是装载语义的一部分 |
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Sonar 问题逐条研判
|
|
53
|
+
|
|
54
|
+
### 2.1 S9383 Promise 未处理(BUG,可靠性·中)×3 —— **应修**
|
|
55
|
+
|
|
56
|
+
位置:[registry.ts:784](../../src/registry.ts#L784)、[:792](../../src/registry.ts#L792)、[:798](../../src/registry.ts#L798)。
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
ctx.on("agent/created", ({ agent }: any) => {
|
|
60
|
+
if (agent === undefined) return;
|
|
61
|
+
this.enqueue(async () => { ... await this.reconcileAll(); }); // 返回值被丢弃
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`enqueue()` 返回的是 `run`([registry.ts:849-853](../../src/registry.ts#L849-L853)),
|
|
66
|
+
链尾 `this.chain` 吞掉了错误,但 `run` 本身仍会 reject。只要 `resolveProject` 之后的
|
|
67
|
+
`reconcileAll()` 抛出(chokidar `syncWatcher`、`scanProject` 中未捕获的 I/O 等),
|
|
68
|
+
就会产生 unhandled rejection。Node 15+ 默认对 unhandled rejection 直接终止进程;
|
|
69
|
+
宿主是否装了全局处理器本次没有核实,但插件不应依赖这一点。
|
|
70
|
+
|
|
71
|
+
同文件其他调用点(`kick`、`scheduleGraceUnmount`、`kickSweep`、`trackMount`)都已
|
|
72
|
+
`.catch(() => {})`,这三处是遗漏。本分支删除的 `agent/session-start` 监听恰好是第四处
|
|
73
|
+
同类问题,删掉后数量从 4 降到 3。
|
|
74
|
+
|
|
75
|
+
建议:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
void this.enqueue(async () => { ... }).catch((error) => {
|
|
79
|
+
this.ctx.logger.warn(`项目 MCP 对账失败:${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
比 `.catch(() => {})` 多记一条日志,方便定位。可以顺手抽一个
|
|
84
|
+
`private schedule(work)` 统一所有「后台入队」调用。
|
|
85
|
+
|
|
86
|
+
### 2.2 S7503 async 箭头函数里没有 await(CODE_SMELL,低)×2
|
|
87
|
+
|
|
88
|
+
位置:[registry.ts:2208](../../src/registry.ts#L2208)(`serverView`)、[:2240](../../src/registry.ts#L2240)(`snapshot`)。
|
|
89
|
+
|
|
90
|
+
`this.enqueue(async () => this.serverViewFromMemory(...))`:`async` 只是为了满足
|
|
91
|
+
`enqueue` 的 `() => Promise<T>` 签名。行为正确。可改为
|
|
92
|
+
`this.enqueue(() => Promise.resolve(this.serverViewFromMemory(...)))`,
|
|
93
|
+
或者放宽 `enqueue` 的签名为 `() => T | Promise<T>`。优先级低。
|
|
94
|
+
|
|
95
|
+
### 2.3 S9381 嵌套 Promise(CODE_SMELL,低)×2
|
|
96
|
+
|
|
97
|
+
位置:[registry.ts:1826](../../src/registry.ts#L1826)、[:1835](../../src/registry.ts#L1835),
|
|
98
|
+
在 `trackMount` 的 `fiber.then(onActive, onFailed)` 回调里再 `enqueue(...).catch()`。
|
|
99
|
+
|
|
100
|
+
这是有意的:fiber settle 时刻不在对账链上,诊断写必须排回链里以免 RMW 竞态(代码注释
|
|
101
|
+
已写明)。不需要改语义。想消掉告警可以抽 `private enqueueDiag(container, event)`,
|
|
102
|
+
回调里只调一个同步方法。
|
|
103
|
+
|
|
104
|
+
### 2.4 S9382 循环内 await(CODE_SMELL,可维护性·低)×15 —— **建议在 Sonar 标记 Accepted**
|
|
105
|
+
|
|
106
|
+
| 行 | 位置 | 能否并行 | 理由 |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| 800 | 构造补扫 `resolveProject` | 可以但无收益 | 一次性,会话数很小 |
|
|
109
|
+
| 903 | `knownProjects` 的 `findProjectRoot` | 可以 | 只读,`Promise.all` 安全 |
|
|
110
|
+
| 1150、1998 | `liveMountKeys` / `sweepRestrictions` 的 `resolveProject` | 可以但无收益 | 大多命中 `agentProjects` 缓存 |
|
|
111
|
+
| 1251 | 指纹 `statConfigFile` | **可以** | 纯只读 stat,每轮对账都跑,项目多时收益最明显 |
|
|
112
|
+
| 1298、1302 | `scanProject` | 不建议 | 会写诊断、改 `configReadCount`,并发后测试口径和日志顺序会变 |
|
|
113
|
+
| 1320 | 逐项目 `reconcileProject` | 不可以 | 生效名预留与装载顺序依赖串行 |
|
|
114
|
+
| 1589、1600 | 先 unmount 再 mount | **不可以** | AGENTS.md 明确要求同名先释放预留再装载 |
|
|
115
|
+
| 1714、1719 | `writeSummaries` 逐文件写 | 可以但无收益 | 各自有锁,数量小 |
|
|
116
|
+
| 1885、1934 | 健康巡检 remount | 不可以 | 逐条 unmount/mount 改动容器状态 |
|
|
117
|
+
| 2159、2174 | `waitForState` 轮询 `delay(200)` | 不适用 | 轮询本来就要逐次等待(测试辅助) |
|
|
118
|
+
| test-json-file.mjs:258 | 逐文件读 `lib/*.js` | 不适用 | 测试代码 |
|
|
119
|
+
|
|
120
|
+
只有 1251(以及可选的 903)值得改成 `Promise.all`,其余都是装载正确性依赖的串行化。
|
|
121
|
+
建议把其余 13 条在 Sonar 标为 Accepted 并附上「串行化是 reconcile 语义」的理由,
|
|
122
|
+
避免后续有人「按 Sonar 优化」时破坏 unmount → mount 顺序。
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 3. 分支自身的发现
|
|
127
|
+
|
|
128
|
+
### B1(低)`DEFAULT_MAX_INSTRUCTION_BYTES` 是死导出
|
|
129
|
+
|
|
130
|
+
[model.ts:193](../../src/model.ts#L193) 导出了 `32768`,注释说「用于文档与对照」,但
|
|
131
|
+
`src/` 与 `test/` 里没有任何引用。它和上游默认一旦漂移,也没有东西会报错。
|
|
132
|
+
建议删掉,或者在测试里用它断言 `patchRowToView` 缺省时不出现该键,让它有实际用途。
|
|
133
|
+
|
|
134
|
+
### B2(低)`agent/session-start` 删除后缺少回归测试
|
|
135
|
+
|
|
136
|
+
删除本身正确(dsh 0.2 已移除该事件,`agent/created` 带 `source`)。但
|
|
137
|
+
[test-registry.mjs](../../test/test-registry.mjs) 里没有针对 `agent/created` 的用例,
|
|
138
|
+
`resume` / `compact` / `clear` 这些原先靠 session-start 兜底的边沿只有 headless 实机验证。
|
|
139
|
+
建议补一条:fake ctx 触发 `agent/created`(带 `source: "resume"`、已有 `session.header.cwd`),
|
|
140
|
+
断言项目被挂载且 deny 被应用。
|
|
141
|
+
|
|
142
|
+
### B3(低)`maxInstructionBytes` 变化会拆连接,无测试
|
|
143
|
+
|
|
144
|
+
`canonicalConfig` 只剥 `tools`,所以改 `maxInstructionBytes` 会触发 unmount → mount。
|
|
145
|
+
这是正确行为(官方只在连接时读取),但值得一条 `planProjectChanges` 单测固化,
|
|
146
|
+
防止以后有人把它当作「非连接字段」加进剥离列表。
|
|
147
|
+
|
|
148
|
+
### B4(低)view 透传不校验
|
|
149
|
+
|
|
150
|
+
[model.ts:684](../../src/model.ts#L684) 的 `patchRowToView` 只判 `typeof === "number"`,
|
|
151
|
+
手写 yml 里的 `0` 或 `1.5` 会原样出现在 `dsh-mcp get` 里,而装载侧
|
|
152
|
+
(`inputFromPatchRow` → zod)会拒绝并记 `config-invalid`。与 `toolCallTimeoutMs`
|
|
153
|
+
等字段现有口径一致,可以接受;如果要改,应该所有数值字段一起改。
|
|
154
|
+
|
|
155
|
+
### 其他确认项(无问题)
|
|
156
|
+
|
|
157
|
+
- **依赖范围**:`^0.2.0-rc.2` 能解析到 `0.2.0` 正式版与后续 `0.2.x`,不会跨到 `0.3`,
|
|
158
|
+
符合「`0.2.0` 线」的表述。锁文件缩减 ~950 行来自 client 0.2 换掉了一批直接依赖
|
|
159
|
+
(适配记录第 2 节已说明)。
|
|
160
|
+
- **无 `peerDependencies` / `engines`**:0.1.5 宿主装上 v0.7.0 只会在 pnpm 那里看到
|
|
161
|
+
client 的 peer 警告,插件本身不拦。现在靠 README / CHANGELOG 告知「0.1.5 用 v0.6.0」。
|
|
162
|
+
可以考虑在 `activate` 时检测宿主版本并给一条明确告警,但不是本次必须。
|
|
163
|
+
- **缺省不落键**:`toOfficialConfig`、`toJsonEntry`、`patchRowToView`、`inputFromPatchRow`
|
|
164
|
+
四处口径一致,测试覆盖了「缺省不存在该键」和「设置后往返保留」。
|
|
165
|
+
- **文档**:中英文 format / env-expansion 同步;stdio 环境清洗的说明对用户有实际价值
|
|
166
|
+
(只放在环境里、没写进 `env` 的凭据在 0.2 下子进程拿不到了),建议在发布说明里
|
|
167
|
+
把这点列为「升级注意」而非普通变化。
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 4. 修复优先级建议
|
|
172
|
+
|
|
173
|
+
1. **合入前**:S1(三处 `enqueue` 加 `.catch` + 日志)。改动 3 行,关掉 3 条 Sonar BUG。
|
|
174
|
+
2. **合入前可选**:B1 删死导出;PR #13 补标题与描述(目前标题是 "Dev"、描述为空)。
|
|
175
|
+
3. **后续**:B2、B3 补测试;指纹 stat(1251)改 `Promise.all`;S7503 / S9381 小重构。
|
|
176
|
+
4. **Sonar 操作**:其余 13 条 S9382 标 Accepted 并写理由。
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 5. 本地验证
|
|
181
|
+
|
|
182
|
+
| 检查 | 结果 |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `tsc --noEmit` | 通过 |
|
|
185
|
+
| 编译到临时目录与 `lib/` 逐文件比哈希 | 11 个 `.js` 全部一致,`lib/` 即本分支源码的产物 |
|
|
186
|
+
| 六套测试(node 直接跑) | 全部通过:model 33 / mcp-file 7 / json-file 13 / json-write 6 / registry 46 / cli 21 |
|
|
187
|
+
|
|
188
|
+
未能执行的部分:`pnpm test` 因 pnpm store 锁文件无访问权限失败,`npm run build` 因
|
|
189
|
+
`lib/` 被占用无法清空(疑似宿主 junction 正在加载),所以改为上面的等价验证。
|
|
190
|
+
headless 实机结论引用自适配记录,本次没有复跑。
|