dsh-project-mcp-manager 0.1.1 → 0.4.2

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 CHANGED
@@ -1,134 +1,151 @@
1
- # dsh-project-mcp-manager
2
-
3
- English | [中文](docs/README.zh.md)
4
-
5
- A project-level MCP auto-loading plugin for DSH: write MCP server configs in
6
- `<projectRoot>/.dsh/mcp.yml` and they are mounted automatically (via the
7
- official `@deepseek-ai/dsh-mcp-client`) whenever a dsh session opens in that
8
- project. Changes to the file hot-reload into the running dsh process, and tool
9
- visibility is scoped per session cwd. No UI — core functionality only.
10
-
11
- ## Installation (mount into a profile)
12
-
13
- The plugin is mounted through a **bundle patch**: once the package is added to
14
- `dsh.profile.bundles`, dsh synthesizes each bundle's patch (the
15
- `cordis.patch.yml` pointed to by `dsh.bundle.patch`) into plugin lines at
16
- startup, in order.
17
-
18
- **Prerequisite: install dsh itself** (for users who don't have dsh yet):
19
-
20
- ```powershell
21
- npm install -g @deepseek-ai/dsh # official npm package
22
- npm install -g deepseek-ai/dsh # or install from the GitHub source
23
- ```
24
-
25
- **Option 1: the dsh plugin command (recommended)** — `dsh plugin` forwards
26
- pnpm inside the profile directory and handles installing/upgrading
27
- dependencies:
28
-
29
- ```powershell
30
- # Install the latest version (web profile shown as an example;
31
- # substitute the name of any other profile, e.g. headless)
32
- dsh plugin --profile web add dsh-project-mcp-manager@latest
33
-
34
- # Install a specific version (check available versions with
35
- # npm view dsh-project-mcp-manager versions)
36
- dsh plugin --profile web add dsh-project-mcp-manager@0.1.0
37
- ```
38
-
39
- **Option 2: install directly with pnpm** (equivalent to option 1):
40
-
41
- ```powershell
42
- # dshHome defaults to %USERPROFILE%\.dsh (uses $DSH_HOME if set)
43
- cd $env:USERPROFILE\.dsh\profiles\web
44
- pnpm add dsh-project-mcp-manager@latest
45
- ```
46
-
47
- **Option 3: local development install** (a junction that live-syncs your
48
- source, so code changes take effect immediately):
49
-
50
- ```powershell
51
- cd $env:USERPROFILE\.dsh\profiles\web
52
- pnpm add link:<path-to-your-dsh-mcp-project-source> # e.g. D:\dev\dsh-mcp-project
53
- ```
54
-
55
- **Upgrading / pinning versions**: re-run the `add` command from option 1 with
56
- the desired version suffix — `@latest` upgrades to the newest release, `@0.1.0`
57
- pins to a specific version.
58
-
59
- ## Build & test
60
-
61
- ```powershell
62
- npm install
63
- npm run build # tsc → lib/
64
- node test/test-model.mjs
65
- node test/test-registry.mjs
66
- ```
67
-
68
- ## Configuration format
69
-
70
- `<projectRoot>/.dsh/mcp.yml` uses the same managed-block format as the profile
71
- `cordis.patch.yml` (a YAML `insert` list between begin/end markers), with one
72
- MCP server per line:
73
-
74
- ```yaml
75
- # >>> dsh-project-mcp-manager:mcp:begin
76
- - insert:
77
- - id: panel-mcp-gitlab
78
- name: '@deepseek-ai/dsh-mcp-client'
79
- config:
80
- serverName: gitlab
81
- transport: stdio
82
- command: npx
83
- args: ['-y', '@modelcontextprotocol/server-gitlab']
84
- cwd: . # resolved relative to the project root
85
- toolCallTimeoutMs: 60000
86
- failOnStartupError: false
87
- reconnect:
88
- enabled: true
89
- initialDelayMs: 500
90
- maxDelayMs: 30000
91
- maxAttempts: 10
92
- # <<< dsh-project-mcp-manager:mcp:end
93
- ```
94
-
95
- `transport` supports `stdio` (command/args/env/cwd) and `streamable-http`
96
- (url/headers). Add `disabled: true` to a line to deactivate it. Content outside
97
- the markers is preserved byte-for-byte.
98
-
99
- ## How it works
100
-
101
- - **Project discovery**: the `session.header.cwd` of an active agent session,
102
- plus the dsh process start directory → walk up to the nearest ancestor
103
- containing `.git` as the project root (falls back to the directory itself
104
- when there is no `.git`).
105
- - **Mounting**: each `(project, serverName)` pair mounts one
106
- `@deepseek-ai/dsh-mcp-client` instance (`ctx.plugin`) on the host ctx and
107
- registers it into the global tool layer. Multiple sessions inside the same
108
- project share a single connection.
109
- - **Hot reload**: chokidar watches each project root (depth 2, ignoring
110
- node_modules/.git/.hg/.svn); changes to `.dsh/mcp.yml` trigger a full
111
- reconciliation after a 150 ms debounce: added lines are mounted, removed
112
- lines are unmounted, and config changes are remounted.
113
- - **Effective names**: when the original `serverName` is unique across the
114
- whole catalog (global lines + all project lines) it keeps its name; on a
115
- conflict both sides are renamed to
116
- `p<first 6 chars of sha256(projectRoot)>_<original name>` (truncated to 32
117
- characters, deterministic and independent of mount order) to avoid the
118
- serverName reservation conflicts that `dsh-mcp-client` makes per process
119
- root. Global lines (profile `cordis.patch.yml` / mcp-client lines already
120
- mounted at the bundle level) participate in occupancy determination but are
121
- never renamed.
122
- - **Session visibility**: when an agent is created, its session cwd resolves
123
- to a project, and `tools.restrict({ deny })` is applied to that agent to deny
124
- every project server except those of the session's own project; a session
125
- without a cwd falls back to the owner project (subagents), then to the
126
- project containing the dsh process cwd. Released when the session is
127
- destroyed.
128
-
129
- ## Security boundary
130
-
131
- `stdio` lines in `.dsh/mcp.yml` spawn their `command` inside the dsh host
132
- process — project files are **executable code carriers**, so only add them in
133
- projects you trust. Lines that fail to mount or are invalid are skipped with a
134
- warning and do not affect other servers.
1
+ # dsh-project-mcp-manager
2
+
3
+ English | [中文](docs/README.zh.md)
4
+
5
+ A project-level MCP auto-loading plugin for DSH: write MCP server configs in
6
+ `<projectRoot>/.dsh/mcp.yml` and they are mounted automatically (via the
7
+ official `@deepseek-ai/dsh-mcp-client`) whenever a dsh session opens in that
8
+ project. Changes to the file hot-reload into the running dsh process, and tool
9
+ visibility is scoped per session cwd. No UI — core functionality only.
10
+
11
+ ## Documentation
12
+
13
+ Feature documentation lives in `docs/`, English and Chinese side by side:
14
+
15
+ - [Configuration format](docs/guide/format.md) — native YAML managed
16
+ block, JSON dialect, divergences from the cordis dialect.
17
+ - [Configuration sources and layers](docs/guide/layers.md) — the
18
+ six-layer source model, shadow priority, global vs project mounting, and the
19
+ read-only legacy Claude Code layer.
20
+ - [`${VAR}` expansion](docs/guide/env-expansion.md) — mount-time interpolation and
21
+ its diagnostics.
22
+ - [CLI `dsh-mcp`](docs/guide/cli.md) — scopes, write formats, ownership contract.
23
+
24
+ Design and release records (Chinese): [dsh 0.1.2-rc.1 adaptation](docs/design/adaptation-dsh-0.1.2-rc1.md) ·
25
+ [JSON config layer proposal](docs/design/proposal-json-mcp-config.md) ·
26
+ [v0.4.2 release notes](docs/releases/v0.4.2.md) ·
27
+ [v0.4.1 release notes](docs/releases/v0.4.1.md) ·
28
+ [v0.4.0 release notes](docs/releases/v0.4.0.md) ·
29
+ [v0.3.1 release notes](docs/releases/v0.3.1.md).
30
+
31
+ Code review records (Chinese): [TypeScript changes since v0.3.1](docs/code-review/ts-review-since-v0.3.1.zh.md).
32
+
33
+ ## Installation (mount into a profile)
34
+
35
+ The plugin is mounted through a **bundle patch**: once the package is added to
36
+ `dsh.profile.bundles`, dsh synthesizes each bundle's patch (the
37
+ `cordis.patch.yml` pointed to by `dsh.bundle.patch`) into plugin lines at
38
+ startup, in order.
39
+
40
+ **Prerequisite: install dsh itself** (for users who don't have dsh yet):
41
+
42
+ ```powershell
43
+ npm install -g @deepseek-ai/dsh # official npm package
44
+ npm install -g deepseek-ai/dsh # or install from the GitHub source
45
+ ```
46
+
47
+ **Option 1: the dsh plugin command (recommended)** — `dsh plugin` forwards
48
+ pnpm inside the profile directory and handles installing/upgrading
49
+ dependencies:
50
+
51
+ ```powershell
52
+ # Install the latest version (web profile shown as an example;
53
+ # substitute the name of any other profile, e.g. headless)
54
+ dsh plugin --profile web add dsh-project-mcp-manager@latest
55
+
56
+ # Install a specific version (check available versions with
57
+ # npm view dsh-project-mcp-manager versions)
58
+ dsh plugin --profile web add dsh-project-mcp-manager@0.2.0
59
+ ```
60
+
61
+ **Option 2: install directly with pnpm** (equivalent to option 1):
62
+
63
+ ```powershell
64
+ # dshHome defaults to %USERPROFILE%\.dsh (uses $DSH_HOME if set)
65
+ cd $env:USERPROFILE\.dsh\profiles\web
66
+ pnpm add dsh-project-mcp-manager@latest
67
+ ```
68
+
69
+ **Option 3: local development install** (a junction that live-syncs your
70
+ source, so code changes take effect immediately):
71
+
72
+ ```powershell
73
+ cd $env:USERPROFILE\.dsh\profiles\web
74
+ pnpm add link:<path-to-your-dsh-mcp-project-source> # e.g. D:\dev\dsh-mcp-project
75
+ ```
76
+
77
+ > **dsh ≥ 0.1.2 note**: whether the plugin loads depends on the profile's
78
+ > `dsh.profile.bundles` list, and a plain `pnpm add link:` does **not** add the
79
+ > package to it. Options 1 and 2 reconcile it automatically; if you ran pnpm by
80
+ > hand, run any `dsh plugin --profile web list` once (or check
81
+ > `dsh --profile web --dump-config` for a `dsh-project-mcp-manager` row) to
82
+ > trigger the bundle reconcile.
83
+
84
+ **Upgrading / pinning versions**: re-run the `add` command from option 1 with
85
+ the desired version suffix — `@latest` upgrades to the newest release, `@0.2.0`
86
+ pins to a specific version.
87
+
88
+ ## Build & test
89
+
90
+ ```powershell
91
+ pnpm install
92
+ pnpm run build # tsc → lib/
93
+ pnpm test # node test/test-model.mjs / test-mcp-file / test-json-file / test-json-write / test-registry / test-cli
94
+ ```
95
+
96
+ ## How it works
97
+
98
+ - **Project discovery**: the `session.header.cwd` of an active agent session,
99
+ plus the dsh process start directory → walk up to the nearest ancestor
100
+ containing `.git` as the project root (falls back to the directory itself
101
+ when there is no `.git`).
102
+ - **Mounting**: each `(project, serverName)` pair in the project layers mounts
103
+ one `@deepseek-ai/dsh-mcp-client` instance (`ctx.plugin`) on the host ctx and
104
+ registers it into the global tool layer; multiple sessions inside the same
105
+ project share a single connection. **Every user-layer row mounts exactly one
106
+ instance** (global, independent of the number of projects) — see
107
+ [configuration sources and layers](docs/guide/layers.md).
108
+ - **Hot reload**: chokidar watches each project root (depth 2, ignoring
109
+ node_modules/.git/.hg/.svn), but only edits to the **exact** config files of
110
+ known project roots — `<projectRoot>/.dsh/mcp.yml`,
111
+ `<projectRoot>/.dsh/mcp.json` and `<projectRoot>/.mcp.json` — trigger a full
112
+ reconciliation after a 150 ms debounce: added rows are mounted, removed rows
113
+ are unmounted, and config changes are remounted. A second watcher covers the
114
+ user layer as three **exact file paths** — `~/.dsh/mcp.yml`,
115
+ `~/.dsh/mcp.json` and `~/.dsh/profiles/<active profile>/mcp.json` (chokidar
116
+ v5 can deliver an event for a watched missing file when it is created, as
117
+ long as its parent directory exists) — never the home directory at large.
118
+ - **Profile name resolution**: derived from the loader root include's
119
+ `config.path` (`~/.dsh/profiles/<name>/cordis.yml`) or `ctx.baseUrl`, and
120
+ overridable with `DSH_MCP_PROFILE=<name>`; when it cannot be resolved the
121
+ profile layer is not read (the other layers still are).
122
+ - **Effective names**: when the original `serverName` is unique across the
123
+ whole catalog (host global rows + all project rows) it keeps its name; on a
124
+ conflict **project rows** are renamed to `p<first 6 hex chars of
125
+ sha256(project root)>_<original name>` (truncated to 32 characters,
126
+ deterministic and independent of mount order) to avoid the serverName
127
+ reservation conflicts that `dsh-mcp-client` makes per process root. Global
128
+ rows (profile patch lines and user-layer rows) participate in occupancy
129
+ determination but are never renamed. Model-visible tool names are built from
130
+ the **effective** server name and the MCP tool's own name
131
+ (`mcp__<effectiveServerName>__<toolName>`), which may differ from the
132
+ `serverName` written in the file.
133
+ - **Session visibility**: when an agent is created, its session cwd resolves to
134
+ a project, and `tools.restrict({ deny })` is applied to that agent to deny
135
+ every project server except those of the session's own project, plus the
136
+ global servers suppressed by the project's own rows; a session without a cwd
137
+ falls back to the owner project (subagents), then to the project containing
138
+ the dsh process cwd. Released when the session is destroyed.
139
+
140
+ ## Security boundary
141
+
142
+ `stdio` lines in `.dsh/mcp.yml`, `.dsh/mcp.json` and `.mcp.json` spawn their
143
+ `command` inside the dsh host process — config files are **executable code
144
+ carriers**, so only add them in projects you trust. The user layers
145
+ (`~/.dsh/mcp.yml`, `~/.dsh/mcp.json`, the profile json) are executable code
146
+ carriers too, they just belong to your own machine: user-layer rows mount
147
+ **globally** (one host-level connection, visible to every project) and are no
148
+ longer fanned out per project. Lines that fail to mount or are invalid are
149
+ skipped with a warning and do not affect other servers. Claude user-state
150
+ monoliths such as `~/.claude.json` (mixing credentials with project history)
151
+ are **no longer read at all** as of v0.4.0.
package/docs/README.zh.md CHANGED
@@ -1,114 +1,123 @@
1
- # dsh-project-mcp-manager
2
-
3
- [English](../README.md) | 中文
4
-
5
- 项目级 MCP 自动加载插件:在项目根 `<projectRoot>/.dsh/mcp.yml` 写入 MCP
6
- 服务器配置,在该项目开启 dsh 会话时自动装载(经官方
7
- `@deepseek-ai/dsh-mcp-client`),文件改动热重载到运行中的 dsh 进程,并按
8
- 会话 cwd 控制工具可见性。无 UI,仅具备核心功能。
9
-
10
- ## 安装(挂载到 profile)
11
-
12
- 插件通过 **bundle patch** 挂载:把包加入 `dsh.profile.bundles` 后,dsh 启动时
13
- 按顺序合成每个 bundle 的 patch(`dsh.bundle.patch` 指向的 `cordis.patch.yml`)
14
- 作为插件行。
15
-
16
- **前置:安装 dsh 本体**(尚未安装 dsh 的用户):
17
-
18
- ```powershell
19
- npm install -g @deepseek-ai/dsh # npm 官方包
20
- npm install -g deepseek-ai/dsh # 或从 GitHub 源码安装
21
- ```
22
-
23
- **方式一:dsh 插件命令(推荐)**——`dsh plugin` 在 profile 目录内转发 pnpm,
24
- 负责安装/升级依赖:
25
-
26
- ```powershell
27
- # 安装最新版(web profile 示例;headless 等其他 profile 替换名字即可)
28
- dsh plugin --profile web add dsh-project-mcp-manager@latest
29
-
30
- # 安装指定版本(版本号可先 npm view dsh-project-mcp-manager versions 查看)
31
- dsh plugin --profile web add dsh-project-mcp-manager@0.1.0
32
- ```
33
-
34
- **方式二:直接 pnpm 安装**(与方式一等价):
35
-
36
- ```powershell
37
- # dshHome 默认为 %USERPROFILE%\.dsh(设置了 DSH_HOME 则用其值)
38
- cd $env:USERPROFILE\.dsh\profiles\web
39
- pnpm add dsh-project-mcp-manager@latest
40
- ```
41
-
42
- **方式三:本地开发安装**(junction 实时同步源码,改代码即生效):
43
-
44
- ```powershell
45
- cd $env:USERPROFILE\.dsh\profiles\web
46
- pnpm add link:<你的 dsh-mcp-project 源码目录> # 例如 D:\dev\dsh-mcp-project
47
- ```
48
-
49
- **升级/锁定版本**:重跑方式一的 `add` 命令并带上目标版本后缀——`@latest`
50
- 升级到最新,`@0.1.0` 锁定到指定版本。
51
-
52
- ## 构建与测试
53
-
54
- ```powershell
55
- npm install
56
- npm run build # tsc → lib/
57
- node test/test-model.mjs
58
- node test/test-registry.mjs
59
- ```
60
-
61
- ## 配置格式
62
-
63
- `<projectRoot>/.dsh/mcp.yml`,格式与 profile `cordis.patch.yml` 的受管块
64
- 一致(begin/end 标记之间的 YAML insert 列表),每行一个 MCP 服务器:
65
-
66
- ```yaml
67
- # >>> dsh-project-mcp-manager:mcp:begin
68
- - insert:
69
- - id: panel-mcp-gitlab
70
- name: '@deepseek-ai/dsh-mcp-client'
71
- config:
72
- serverName: gitlab
73
- transport: stdio
74
- command: npx
75
- args: ['-y', '@modelcontextprotocol/server-gitlab']
76
- cwd: . # 相对项目根解析
77
- toolCallTimeoutMs: 60000
78
- failOnStartupError: false
79
- reconnect:
80
- enabled: true
81
- initialDelayMs: 500
82
- maxDelayMs: 30000
83
- maxAttempts: 10
84
- # <<< dsh-project-mcp-manager:mcp:end
85
- ```
86
-
87
- `transport` 支持 `stdio`(command/args/env/cwd)与 `streamable-http`
88
- (url/headers)。行加 `disabled: true` 即停用。标记之外的内容逐字节保留。
89
-
90
- ## 工作原理
91
-
92
- - **项目发现**:在线 agent 会话的 `session.header.cwd` + dsh 进程启动目录 →
93
- 向上找最近的含 `.git` 的祖先目录作为项目根(无 `.git` 时退回目录本身)。
94
- - **装载**:每个 `(项目, serverName)` 在宿主 ctx 上装载一个
95
- `@deepseek-ai/dsh-mcp-client` 实例(`ctx.plugin`),注册进全局工具层。
96
- 同一项目内多会话共享同一连接。
97
- - **热重载**:chokidar 监听各项目根(depth 2,忽略 node_modules/.git/.hg/
98
- .svn),`.dsh/mcp.yml` 的增删改经 150ms 防抖触发全量对账:新增行装载、
99
- 删除行卸载、配置变化重装。
100
- - **生效名**:原始 `serverName` 在整个目录(全局行 + 全部项目行)中唯一时
101
- 保持原名;冲突时双方都改为 `p<sha256(项目根)前6位>_<原名>`(截断 32 字符,
102
- 确定性、与装载顺序无关),避免 `dsh-mcp-client` 按进程根的 serverName
103
- 预留冲突。全局行(profile `cordis.patch.yml` / bundle 层已装载的
104
- mcp-client 行)参与占用判定但不改名。
105
- - **会话可见性**:agent 创建时按其会话 cwd 解析项目,对该 agent 应用
106
- `tools.restrict({ deny })`,deny 掉除本会话项目外的全部项目服务器;会话
107
- 无 cwd 时回退 owner 项目(子代理),再回退 dsh 进程 cwd 所在项目。会话
108
- 销毁时释放。
109
-
110
- ## 安全边界
111
-
112
- `.dsh/mcp.yml` 中的 `stdio` 行会在 dsh 宿主进程内 spawn 其 `command`——
113
- 项目文件是**可执行代码载体**,只应在可信项目中添加。装载失败/配置无效行
114
- 仅告警跳过,不影响其他服务器。
1
+ # dsh-project-mcp-manager
2
+
3
+ [English](../README.md) | 中文
4
+
5
+ 项目级 MCP 自动加载插件:在项目根 `<projectRoot>/.dsh/mcp.yml` 写入 MCP
6
+ 服务器配置,在该项目开启 dsh 会话时自动装载(经官方
7
+ `@deepseek-ai/dsh-mcp-client`),文件改动热重载到运行中的 dsh 进程,并按
8
+ 会话 cwd 控制工具可见性。无 UI,仅具备核心功能。
9
+
10
+ ## 文档
11
+
12
+ 功能说明已拆分到 `docs/`,中英双版并存:
13
+
14
+ - [配置格式](guide/format.zh.md)——原生 YAML 受管块、JSON 方言、与
15
+ cordis 方言的差异。
16
+ - [配置来源与分层](guide/layers.zh.md)——六层来源模型、影子优先序、
17
+ 全局装载 vs 项目装载,以及只读的遗留 Claude Code 层。
18
+ - [`${VAR}` 展开](guide/env-expansion.zh.md)——装载时插值与对应诊断。
19
+ - [CLI `dsh-mcp`](guide/cli.zh.md)——作用域、写入格式与独占契约。
20
+
21
+ 设计与发布记录(中文):[dsh 0.1.2-rc.1 适配记录](design/adaptation-dsh-0.1.2-rc1.md) ·
22
+ [JSON 配置层设计提案](design/proposal-json-mcp-config.md) ·
23
+ [v0.4.2 发布说明](releases/v0.4.2.md) ·
24
+ [v0.4.1 发布说明](releases/v0.4.1.md) ·
25
+ [v0.4.0 发布说明](releases/v0.4.0.md) ·
26
+ [v0.3.1 发布说明](releases/v0.3.1.md)。
27
+
28
+ 代码审查记录(中文):[v0.3.1 以来 TypeScript 变更审查](code-review/ts-review-since-v0.3.1.zh.md)。
29
+
30
+ ## 安装(挂载到 profile)
31
+
32
+ 插件通过 **bundle patch** 挂载:把包加入 `dsh.profile.bundles` 后,dsh 启动时
33
+ 按顺序合成每个 bundle 的 patch(`dsh.bundle.patch` 指向的 `cordis.patch.yml`)
34
+ 作为插件行。
35
+
36
+ **前置:安装 dsh 本体**(尚未安装 dsh 的用户):
37
+
38
+ ```powershell
39
+ npm install -g @deepseek-ai/dsh # npm 官方包
40
+ npm install -g deepseek-ai/dsh # 或从 GitHub 源码安装
41
+ ```
42
+
43
+ **方式一:dsh 插件命令(推荐)**——`dsh plugin` 在 profile 目录内转发 pnpm,
44
+ 负责安装/升级依赖:
45
+
46
+ ```powershell
47
+ # 安装最新版(web profile 示例;headless 等其他 profile 替换名字即可)
48
+ dsh plugin --profile web add dsh-project-mcp-manager@latest
49
+
50
+ # 安装指定版本(版本号可先 npm view dsh-project-mcp-manager versions 查看)
51
+ dsh plugin --profile web add dsh-project-mcp-manager@0.2.0
52
+ ```
53
+
54
+ **方式二:直接 pnpm 安装**(与方式一等价):
55
+
56
+ ```powershell
57
+ # dshHome 默认为 %USERPROFILE%\.dsh(设置了 DSH_HOME 则用其值)
58
+ cd $env:USERPROFILE\.dsh\profiles\web
59
+ pnpm add dsh-project-mcp-manager@latest
60
+ ```
61
+
62
+ **方式三:本地开发安装**(junction 实时同步源码,改代码即生效):
63
+
64
+ ```powershell
65
+ cd $env:USERPROFILE\.dsh\profiles\web
66
+ pnpm add link:<你的 dsh-mcp-project 源码目录> # 例如 D:\dev\dsh-mcp-project
67
+ ```
68
+
69
+ > **dsh ≥ 0.1.2 注意**:插件是否生效取决于 profile 的 `dsh.profile.bundles`,
70
+ > 而单纯 `pnpm add link:` **不会**把包写进 bundles。方式一/方式二会自动补齐;
71
+ > 若你手写了 pnpm 命令,请再跑一次任意 `dsh plugin --profile web list`(或
72
+ > `--dump-config` 检查合成结果里有没有 `dsh-project-mcp-manager` 行)触发
73
+ > bundle reconcile。
74
+
75
+ **升级/锁定版本**:重跑方式一的 `add` 命令并带上目标版本后缀——`@latest`
76
+ 升级到最新,`@0.2.0` 锁定到指定版本。
77
+
78
+ ## 构建与测试
79
+
80
+ ```powershell
81
+ pnpm install
82
+ pnpm run build # tsc → lib/
83
+ pnpm test # node 直跑 test/ 下六个 .mjs(model / mcp-file / json-file / json-write / registry / cli)
84
+ ```
85
+
86
+ ## 工作原理
87
+
88
+ - **项目发现**:在线 agent 会话的 `session.header.cwd` + dsh 进程启动目录 →
89
+ 向上找最近的含 `.git` 的祖先目录作为项目根(无 `.git` 时退回目录本身)。
90
+ - **装载**:项目层每个 `(项目, serverName)` 在宿主 ctx 上装载一个
91
+ `@deepseek-ai/dsh-mcp-client` 实例(`ctx.plugin`),注册进全局工具层,同一
92
+ 项目内多会话共享同一连接;**用户层每行只装载一个实例**(全局,与项目数无关)
93
+ ——详见[配置来源与分层](guide/layers.zh.md)。
94
+ - **热重载**:chokidar 监听各项目根(depth 2,忽略 node_modules/.git/.hg/
95
+ .svn),但只有**已知项目根的精确配置文件**(`<projectRoot>/.dsh/mcp.yml`、
96
+ `<projectRoot>/.dsh/mcp.json` 与 `<projectRoot>/.mcp.json`)的改动经 150ms
97
+ 防抖触发全量对账:新增行装载、删除行卸载、配置变化重装。另有独立 watcher 以
98
+ **精确文件路径**监听用户层:`~/.dsh/mcp.yml`、`~/.dsh/mcp.json` 与
99
+ `~/.dsh/profiles/<当前 profile>/mcp.json`(chokidar v5 对被监听的缺失文件能在
100
+ 其创建时补发事件,前提是父目录已存在)——不监听家目录整体。
101
+ - **profile 名解析**:从 loader 根 include 的 `config.path`
102
+ (`~/.dsh/profiles/<name>/cordis.yml`)或 `ctx.baseUrl` 推导,可用
103
+ `DSH_MCP_PROFILE=<name>` 覆盖;解析不出时不读 profile 层(其余层照常)。
104
+ - **生效名**:原始 `serverName` 在整个目录(宿主全局行 + 全部项目行)中唯一时
105
+ 保持原名;冲突时**项目行**改为 `p<sha256(项目根)前6位>_<原名>`(截断 32 字符,
106
+ 确定性、与装载顺序无关),避免 `dsh-mcp-client` 按进程根的 serverName
107
+ 预留冲突。全局行(profile patch 行与用户层行)参与占用判定但不改名。模型可见
108
+ 的工具名由生效服务器名与 MCP 工具自身的名字拼成 `mcp__<生效名>__<工具名>`,
109
+ 与文件里写的 `serverName` 可能不同。
110
+ - **会话可见性**:agent 创建时按其会话 cwd 解析项目,对该 agent 应用
111
+ `tools.restrict({ deny })`,deny 掉除本会话项目外的全部项目服务器,以及本项目
112
+ 自身行压制过的全局服务器;会话无 cwd 时回退 owner 项目(子代理),再回退 dsh
113
+ 进程 cwd 所在项目。会话销毁时释放。
114
+
115
+ ## 安全边界
116
+
117
+ `.dsh/mcp.yml`、`.dsh/mcp.json` 与 `.mcp.json` 中的 `stdio` 行会在 dsh
118
+ 宿主进程内 spawn 其 `command`——配置文件是**可执行代码载体**,只应在可信项目
119
+ 中添加。用户层(`~/.dsh/mcp.yml`、`~/.dsh/mcp.json`、profile json)同样是可执行
120
+ 代码载体,只是它们属于你自己的机器:用户层行会以**全局**方式装载(宿主级一条
121
+ 连接,所有项目可见),不再按项目 fan-out。装载失败/配置无效行仅告警跳过,不影响
122
+ 其他服务器。`~/.claude.json` 这类 Claude 用户态单体文件(混存凭据与项目历史)
123
+ 自 v0.4.0 起**完全不再读取**。