dsh-project-mcp-manager 0.6.0 → 0.7.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.
@@ -0,0 +1,89 @@
1
+ # 适配记录:dsh v0.2.0-rc.2
2
+
3
+ [← 返回 README](../README.zh.md) | 相关:[dsh 0.1.6-alpha.2 适配方案](adaptation-dsh-0.1.6-alpha.2.md) ·
4
+ [dsh 0.1.5-rc.2 适配记录](adaptation-dsh-0.1.5-rc2.md) ·
5
+ [v0.7.0 发布说明](../releases/v0.7.0.md)
6
+
7
+ **记录日期**:2026-09-30 | **插件**:`dsh-project-mcp-manager` v0.7.0
8
+ **宿主**:本机 `dsh` 0.2.0-rc.2(`dsh --version`)。对照源码
9
+ `D:\Projects\Agent\deepseek-harness`,标签 `dsh-v0.1.5-rc.2` → `dsh-v0.2.0-rc.2`
10
+ (发布包 `@deepseek-ai/dsh-mcp-client@0.2.0-rc.2`、cordis 4.0.4、
11
+ cordis-plugin-include 1.0.9、cordis-plugin-loader 1.0.5)。
12
+ 实机只用 **headless** profile(`~/.dsh/profiles/headless` 以 junction 链到本仓库)。
13
+
14
+ **结论**:官方没有接过本插件的项目级发现、按会话 cwd 隔离和 `dsh-mcp` CLI。
15
+ v0.6.0 的依赖范围解析不到 0.2.0 的 client,且 `agent/session-start` 已删除、
16
+ `maxInstructionBytes` 会被静默丢掉。v0.7.0 按这三处对齐后,headless 实机可以
17
+ 装载探针并调用 `mcp__compatprobe__probe`。
18
+
19
+ ---
20
+
21
+ ## 1. 官方已经覆盖、本插件不再独占的能力
22
+
23
+ 对照 `packages/mcp` 与 profile 装配(0.1.5-rc.2 之后、0.2.0-rc.2 之前落地,
24
+ 0.2.0-rc.2 相对 0.1.6-alpha.2 的 client 源码几乎只剩类型收紧):
25
+
26
+ | 能力 | 官方现状 | 本插件 |
27
+ |---|---|---|
28
+ | 全局 / 每 profile 声明 MCP | `$DSH_HOME` 与 profile 的 Cordis patch,一行一个 `dsh-mcp-client` | 用户层 yml/json 仍是更短的写法,挂载后仍是官方 client |
29
+ | profile 配置热重载 | 官方 HMR,作用在 profile 配置层 | 不替代。项目文件仍由本插件 chokidar 监听 |
30
+ | 运行时加服务器 | `plugin_manager` 与 configuration-only bundle | 不替代 |
31
+ | 协议、分页、重连、instructions | client 内建;SDK `@modelcontextprotocol/client@2.0.0` | 不碰协议 |
32
+ | MCP resources 与服务器提示节 | `mcp-resources` 进 base bundle;client 经 `registerServerContext` 登记 | 升级到同一份 client 后,本插件挂上的服务器自动出现在资源工具里 |
33
+ | 工具名 | 干净时仍是 `mcp__<serverName>__<rawName>`;不合函数名契约时追加 12 hex | deny 展开仍用已注册的精确名 |
34
+
35
+ 源码与文档里没有 `<projectRoot>/.dsh/mcp.yml`、`.dsh/mcp.json` 或项目级
36
+ `.mcp.json` 的装载器。`docs/user/guide/mcp-memory.md` 仍是 profile overlay。
37
+ 会话 cwd 只用于文件工具和工作区指令,没有「cwd → 该项目 MCP 工具可见性」的映射。
38
+
39
+ 本插件继续留下的三件事:
40
+
41
+ 1. 项目级配置发现(含只读遗留 `.mcp.json`)与六层影子合并。
42
+ 2. 按会话 cwd 的 `tools.restrict({ deny })`。
43
+ 3. MCP 专用格式与 `dsh-mcp` CLI。
44
+
45
+ ---
46
+
47
+ ## 2. 对插件有影响的破坏性变更
48
+
49
+ | 点 | 0.1.5-rc.2 → 0.2.0-rc.2 | 处置 |
50
+ |---|---|---|
51
+ | 依赖范围 | `^0.1.5-rc.1` 解析不到 `0.2.0-rc.2`(预发布必须同一 major.minor.patch 元组) | 下限改为 `^0.2.0-rc.2`。0.1.5 用户留在 v0.6.0 |
52
+ | `agent/session-start` | 事件删除。`agent/created` 变为 `{ agent, source: 'startup'\|'resume'\|'clear'\|'compact', signal? }`,`ctx.serial` 派发 | 删除该监听。现有 `agent/created` 只解构 `agent`,仍然成立。构造时 `liveAgents()` 补扫保留 |
53
+ | Config | 两个传输各增加可选 `maxInstructionBytes`(int ≥ 1,默认 32768)。超限拒绝这一次连接 | schema / `toOfficialConfig` / JSON 透传。缺省不写键。CLI 不加参数 |
54
+ | cordis | 4.0.2 → 4.0.4。include 1.0.7 → 1.0.9,loader 1.0.3 → 1.0.5 | 开发依赖 `^4.0.4`。`ctx.plugin` / `ctx.on` / `ctx.provide` / `config.patches` 仍在 |
55
+ | client peer | 发布包 peer 钉死 `0.2.0-rc.2`(tools、scope、timeout、attachment、llm、subprocess;resources 与 system-prompt 可选)。直接依赖换成 schemastery、`dsh-util-values`、MCP client SDK 2.0.0 | 锁文件跟着解析。pnpm 12 把这批 rc 写入 `minimumReleaseAgeExclude` |
56
+ | stdio 环境 | 子进程环境 = 清洗后的父环境 + 配置 `env`。名字匹配凭据或 `DSH_*` 的环境变量不继承 | 文档说明。本插件展开进 `env` 的值仍是显式配置 |
57
+ | 结果形状 | 不再接受 legacy `toolResult`;无 tools capability 的服务器保持连接、工具集为空 | 不改装载逻辑。调用期错误仍不经过本插件 |
58
+ | `apply` | 等待初次连接。`failOnStartupError: false` 时失败只打日志并进入官方重连,fiber 仍 resolve | 与现有 `trackMount` 一致:fiber resolve 即 `active`,0 工具的首连交给官方重连 |
59
+ | `tools.restrict` / `schemas()` | 仍要求 agent 作用域上的精确工具名;`schemas()` 多了可选 `deferLoading` | 本插件本来就走 `agent.ctx.tools.restrict` 并按 `name` 读 schema |
60
+ | `session.header.cwd` | 仍在 | 项目发现路径不变 |
61
+ | 传输集合 / serverName / `MAX_TIMER_DELAY_MS` | 仍是 `stdio` \| `streamable-http`,`[A-Za-z0-9_-]{1,32}`,`2147483647` | 常量注释改为 0.2.0-rc.2,值未改 |
62
+
63
+ 0.2.0-rc.1 → 0.2.0-rc.2 的 client 源码差异是一处类型断言,没有新的配置字段。
64
+
65
+ ---
66
+
67
+ ## 3. 实机(headless profile)
68
+
69
+ `dsh --profile headless --dump-config` 合成树含 `# == dsh-project-mcp-manager` /
70
+ `- id: mcp-project`。插件包是 junction:`link:D:/Projects/Agent/dsh-mcp-project`。
71
+
72
+ 临时 git 仓库 `compatprobe`(stdio 探针,`initialize` 带回 `instructions: "hello from probe"`,工具 `probe` → `pong:compat-020`):
73
+
74
+ | 检查 | 结果 |
75
+ |---|---|
76
+ | `maxInstructionBytes: 16` | 子进程收到 `initialize` 与 `notifications/initialized`,**没有** `tools/list`(超限发生在同步工具之前)。`failOnStartupError` 默认 false,诊断仍记 `active` |
77
+ | `maxInstructionBytes: 65536` | 收到 `tools/list`。模型调用 `mcp__compatprobe__probe` → `pong:compat-020` |
78
+ | mcp-resources 协同 | 同一轮里 `list_mcp_resources` 的 `server` 参数接受 `compatprobe`(共享资源工具能看见本插件挂上的服务器) |
79
+ | 单测 | `pnpm test` 六套全绿 |
80
+
81
+ 未改本机 `~/.dsh` 的模型设置。headless 使用当前默认模型完成了上述调用。
82
+
83
+ ---
84
+
85
+ ## 4. 代码改动
86
+
87
+ - `package.json`:`dsh-mcp-client` `^0.2.0-rc.2`,`cordis` `^4.0.4`,版本 0.7.0。
88
+ - `src/registry.ts`:去掉 `agent/session-start`。
89
+ - `src/model.ts`、`src/json-file.ts`、`src/json-write.ts`:透传 `maxInstructionBytes`。
@@ -238,6 +238,8 @@
238
238
  **提案**:以官方 storageDomain/service 模式 `ctx.provide("projectMcp", { snapshot, serverView, globalState, reload })`,并补类型声明合并。插件现有导出面(`inject = ["tools","agents"]`、`globalNames()`、`activeProfile()`)保持不变。
239
239
 
240
240
  > **落地口径(v0.6.0)**:`snapshot` / `serverView` / `globalState` 进 enqueue 与对账互斥,按上一轮 `lastScanFiles` / `userLayer` 内存拼装,不读盘、不跑 `reconcileAll`;`reload` 才对账一次。
241
+ >
242
+ > **后续口径**:配套 UI 独立成包后即为稳定消费方。方法、`projectMcp/updated`(对账成功结束、无载荷)与包入口导出的视图类型按语义化版本承诺;UI 包以精确版本 peer-depend 本包。浏览器 SSE 仍由 UI 自建。见 [查询面](../guide/service.zh.md)。上面「不承诺稳定 API」只保留为 v0.6.0 当时的裁决。
241
243
 
242
244
  **风险**:服务名与稳定性承诺需评审;`reload` 的语义限定为"触发一次 reconcileAll"。
243
245
 
@@ -375,6 +377,6 @@ C3 本轮跳过。
375
377
  2. **A4 键名与语法**:`tools.allow`/`tools.deny`,同时读取 `includeTools`/`excludeTools`;**完整 glob**(`*`、`**`、`?`、`[…]`)。
376
378
  3. **A5 作用域**:允许 `--scope project|user|profile`;缺省 **project**(与 `add` 一致)。
377
379
  4. **B1 卸载策略**:无会话且非进程 cwd 后 **5 分钟宽限**再卸载。
378
- 5. **B3 服务名与稳定性**:服务名 `projectMcp`;**不承诺稳定 API**。
380
+ 5. **B3 服务名与稳定性**:服务名 `projectMcp`。v0.6.0 落地时不承诺稳定 API;配套 UI 独立成包后改为对它按语义化版本承诺,见 [查询面](../guide/service.zh.md)。
379
381
  6. **C1 opt-in**:**只报错**,无 `sseAsStreamableHttp`,无环境开关。
380
382
  7. **C3 默认值与层序**:**本轮不做**(不读 `.vscode/mcp.json`)。
@@ -24,3 +24,10 @@ the reason rides on a separate `skipReason` field (`env-missing` /
24
24
  [configuration sources and layers](layers.md)). Values are never
25
25
  persisted anywhere by the plugin; the CLI writes `${VAR}` through literally,
26
26
  so secrets can live in the environment while configs live in git.
27
+
28
+ From dsh 0.2, the official stdio transport builds the child environment from a
29
+ scrubbed parent (ambient names matching `/KEY|PASSWORD|SECRET|TOKEN/i` and
30
+ ambient `DSH_*` are dropped) and then overlays the configured `env`. A secret
31
+ this plugin interpolates into `env` or `headers` is explicit config and is
32
+ kept. A secret that exists only as an ambient variable, and is not written
33
+ into `env`, is no longer inherited by the child.
@@ -18,3 +18,8 @@
18
18
  `plugin-throw`;其它如 `name-taken` / `idle` / `give-up` 见
19
19
  [配置来源与分层](layers.zh.md))。插件任何写路径都不落盘展开后的值;
20
20
  CLI 写入时 `${VAR}` 原样保留——配置可以进 git,凭据留在环境里。
21
+
22
+ dsh 0.2 起,官方 stdio 传输的子进程环境是「清洗后的父进程环境 + 配置里的
23
+ `env`」。父进程里名字匹配 `/KEY|PASSWORD|SECRET|TOKEN/i` 的变量,以及环境中的
24
+ `DSH_*`,不会继承给子进程。本插件把 `${VAR}` 展开进 `env` / `headers` 的值属于
25
+ 显式配置,会保留。只存在于环境、没有写进 `env` 的凭据,子进程再也拿不到。
@@ -67,7 +67,12 @@ file fail with an explicit error (logged and written to `.dsh/.mcp-diag.json`)
67
67
  instead of silently mounting the expression text as a literal string. Values
68
68
  in `env`/`headers` are otherwise literal, except for `${VAR}` references
69
69
  which are interpolated at mount time (see [`${VAR}` expansion](env-expansion.md));
70
- `disabled` must be `true`/`false`. The project file is otherwise a superset
70
+ `disabled` must be `true`/`false`. Optional `maxInstructionBytes` (integer ≥ 1)
71
+ is the official cap on attributed server instructions, default 32768 when the
72
+ key is omitted. An oversized instruction rejects that connection inside
73
+ `dsh-mcp-client` (the row still activates when `failOnStartupError` is false,
74
+ then follows the official reconnect loop). `dsh-mcp` has no flag for this
75
+ field; write it in the file. The project file is otherwise a superset
71
76
  grammar: `env`/`headers` accept `KEY: null` to delete a key (stripped at
72
77
  mount), which the official mcp-client schema rejects — such lines would fail
73
78
  if moved back to `cordis.patch.yml`.
@@ -97,7 +102,7 @@ Follows the ecosystem (Cursor / Claude Code's `mcpServers` shape):
97
102
  already speaks Streamable HTTP, change `type` to `"http"`; or drop `type`
98
103
  and keep `url` (this plugin infers streamable-http). There is no implicit
99
104
  fallback. The DSH passthrough keys
100
- `toolCallTimeoutMs`/`failOnStartupError`/`reconnect`, `enabled: false`
105
+ `toolCallTimeoutMs`/`failOnStartupError`/`maxInstructionBytes`/`reconnect`, `enabled: false`
101
106
  (silently skipped, claims no name) and `disabled: true` (claims its name but
102
107
  is not mounted).
103
108
  - Unknown keys are tolerated and ignored; names must match
@@ -62,7 +62,11 @@ process.env.GITHUB_TOKEN }`)在项目文件里**不支持**——受管块内
62
62
  标签会使该文件整体报错跳过(写入 `.dsh/.mcp-diag.json` 并打日志),不会把
63
63
  表达式当字面量字符串静默装载。`env`/`headers` 的值其余情况下是字面量,仅
64
64
  `${VAR}` 引用会在装载时做串内插值(见 [`${VAR}` 展开](env-expansion.zh.md));
65
- `disabled` 只能是 `true`/`false`。反之项目文件是超集语法:`env`/`headers`
65
+ `disabled` 只能是 `true`/`false`。可选 `maxInstructionBytes`(整数 ≥ 1)是官方对
66
+ 「带归属头的服务器 instructions」的字节上限;省略该键时用官方默认 32768。
67
+ 超限由 `dsh-mcp-client` 拒绝这一次连接(`failOnStartupError` 为 false 时本插件
68
+ 仍把 fiber 标成 active,随后走官方重连)。`dsh-mcp` 没有对应参数,直接写进文件。
69
+ 反之项目文件是超集语法:`env`/`headers`
66
70
  允许 `KEY: null` 表示删除该键(装载时被剔除),这在官方 mcp-client 校验里会被
67
71
  拒绝——把这类行原样挪回 `cordis.patch.yml` 会装载失败。
68
72
 
@@ -89,7 +93,7 @@ process.env.GITHUB_TOKEN }`)在项目文件里**不支持**——受管块内
89
93
  (`dsh-mcp-client`)只支持 `stdio` 与 streamable-http——服务端已支持
90
94
  Streamable HTTP 时把 `type` 改为 `"http"`;或删除 `type` 只留 `url`
91
95
  (本插件按 streamable-http 推断)。没有隐式回退。DSH 透传键
92
- `toolCallTimeoutMs`/`failOnStartupError`/`reconnect`、`enabled: false`
96
+ `toolCallTimeoutMs`/`failOnStartupError`/`maxInstructionBytes`/`reconnect`、`enabled: false`
93
97
  (静默跳过、不占名)与 `disabled: true`(占名但不装载)。
94
98
  - 未知键容忍忽略;名字须匹配 `[A-Za-z0-9_-]{1,32}`;坏条目逐条报错、不影响其余。
95
99
  - **JSON 文件由 `dsh-mcp` CLI 独占**:写入保留其他顶层键与键序,但 JSON 没有注释,
@@ -0,0 +1,95 @@
1
+ # Query surface (`projectMcp`)
2
+
3
+ English | [中文](service.zh.md)
4
+
5
+ [← README](../../README.md) | Related: [configuration sources and layers](layers.md) · [CLI `dsh-mcp`](cli.md)
6
+
7
+ Other plugins and the companion UI read mount state through the cordis service
8
+ `ctx.projectMcp`. This package stays in-process: it emits one event when a
9
+ reconcile finishes. The browser still uses the SSE channel the companion UI
10
+ builds itself.
11
+
12
+ ## Methods
13
+
14
+ | Method | Behavior |
15
+ |---|---|
16
+ | `snapshot()` | In-memory partitions from the previous reconcile (project files and the user layer). Joins the reconcile queue, does not read disk, and does not start a reconcile. |
17
+ | `serverView(projectRoot, rawName)` | One row's in-memory view. Same queue rules as `snapshot()`. |
18
+ | `globalState(rawName)` | Live global-layer state for that raw name, or `undefined` for a project row. |
19
+ | `reload()` | One full reconcile (`reconcileNow`). |
20
+
21
+ `snapshot()` / `serverView()` / `globalState()` do not emit.
22
+
23
+ ## Event
24
+
25
+ After every **successful** `reconcileAll` — including a fingerprint hit that
26
+ still runs the health check, session deny sweep, tool budget, and summary —
27
+ the registry emits:
28
+
29
+ ```ts
30
+ ctx.on("projectMcp/updated", async () => {
31
+ const snapshot = await ctx.projectMcp.snapshot();
32
+ });
33
+ ```
34
+
35
+ The event name is `PROJECT_MCP_UPDATED_EVENT` (`"projectMcp/updated"`). There
36
+ is no payload and no diff. Dispatch is synchronous and does not await a
37
+ listener's promise. The listener reads `snapshot()` or `serverView()` and
38
+ compares with what it last pushed. An early return (`disposed`) or a throw
39
+ inside reconcile does not emit. A listener that throws is logged
40
+ (`项目 MCP 变更事件投递失败:…`) and does not fail the reconcile.
41
+
42
+ ## Types
43
+
44
+ Named view types are exported from the package entry. The `exports` map does
45
+ not allow a deep import of `lib/registry.js`.
46
+
47
+ ```ts
48
+ import {
49
+ PROJECT_MCP_SERVICE,
50
+ PROJECT_MCP_UPDATED_EVENT,
51
+ type FiberPhaseView,
52
+ type McpRowSource,
53
+ type McpScopeInfo,
54
+ type McpServerRuntimeView,
55
+ type McpServerView,
56
+ type McpTransport,
57
+ type PatchRow,
58
+ type ProjectFileState,
59
+ type ProjectMcpService,
60
+ type ProjectServerPhase,
61
+ type ProjectServerState,
62
+ type ReconnectConfig,
63
+ type ToolFilter,
64
+ } from "dsh-project-mcp-manager";
65
+ ```
66
+
67
+ `ProjectFileState` is what `snapshot()` returns. Each row is an
68
+ `McpServerRuntimeView` (`McpServerView` plus `source`, `fiberPhase`,
69
+ `skipReason`, and `toolCount`). `globalState()` returns `ProjectServerState`.
70
+
71
+ ## Semver for the companion UI
72
+
73
+ `projectMcp`'s methods, the `projectMcp/updated` event, and the view types
74
+ exported above follow semantic versioning **for the companion UI**:
75
+
76
+ | Bump | What it means for this surface |
77
+ |---|---|
78
+ | patch | Fixes that keep the methods, the event, and the view shape. |
79
+ | minor | Additive fields, methods, or events. |
80
+ | major | Removals, renames, or a change in meaning. |
81
+
82
+ The companion UI lists this package as a peer dependency pinned to the **same
83
+ exact version** (no `^` or `~`). When the surface and the UI move, they move
84
+ together:
85
+
86
+ ```json
87
+ {
88
+ "peerDependencies": {
89
+ "dsh-project-mcp-manager": "<exact version of this package>"
90
+ }
91
+ }
92
+ ```
93
+
94
+ Replace the placeholder with the version in this package's `package.json` at
95
+ the release the UI is built against.
@@ -0,0 +1,88 @@
1
+ # 查询面(`projectMcp`)
2
+
3
+ [English](service.md) | 中文
4
+
5
+ [← 返回 README](../README.zh.md) | 相关:[配置来源与分层](layers.zh.md) · [CLI `dsh-mcp`](cli.zh.md)
6
+
7
+ 其它插件和配套 UI 经 cordis 服务 `ctx.projectMcp` 读装载状态。本包只在进程内
8
+ 发事件:对账成功结束时通知一次。浏览器仍走配套 UI 自己建的 SSE,本包不打开那条通道。
9
+
10
+ ## 方法
11
+
12
+ | 方法 | 行为 |
13
+ |---|---|
14
+ | `snapshot()` | 上一轮对账的内存分区(项目文件与用户层)。进对账队列,不读盘,不发起对账。 |
15
+ | `serverView(projectRoot, rawName)` | 一行的内存 view。排队规则与 `snapshot()` 相同。 |
16
+ | `globalState(rawName)` | 该原名的全局层运行态;项目行返回 `undefined`。 |
17
+ | `reload()` | 一次全量对账(`reconcileNow`)。 |
18
+
19
+ `snapshot()` / `serverView()` / `globalState()` 不发事件。
20
+
21
+ ## 事件
22
+
23
+ 每次 **成功** 的 `reconcileAll` 结束都会 emit——指纹未变、因而跳过重读、但仍跑完
24
+ 健康巡检、会话 deny、工具预算和摘要的那一轮也算。注册表调用:
25
+
26
+ ```ts
27
+ ctx.on("projectMcp/updated", async () => {
28
+ const snapshot = await ctx.projectMcp.snapshot();
29
+ });
30
+ ```
31
+
32
+ 事件名是 `PROJECT_MCP_UPDATED_EVENT`(`"projectMcp/updated"`)。无载荷,也不带
33
+ diff。派发是同步的,不会等待监听方返回的 Promise。监听方读 `snapshot()` 或
34
+ `serverView()`,和自己上次推出去的结果比较。对账中途因 `disposed` 返回,或对账
35
+ 内部抛错,都不发。监听方抛错会记一条 `项目 MCP 变更事件投递失败:…`,对账本身
36
+ 仍算成功。
37
+
38
+ ## 类型
39
+
40
+ 具名视图类型从包入口导出。`exports` 不开放 `lib/registry.js` 深导入。
41
+
42
+ ```ts
43
+ import {
44
+ PROJECT_MCP_SERVICE,
45
+ PROJECT_MCP_UPDATED_EVENT,
46
+ type FiberPhaseView,
47
+ type McpRowSource,
48
+ type McpScopeInfo,
49
+ type McpServerRuntimeView,
50
+ type McpServerView,
51
+ type McpTransport,
52
+ type PatchRow,
53
+ type ProjectFileState,
54
+ type ProjectMcpService,
55
+ type ProjectServerPhase,
56
+ type ProjectServerState,
57
+ type ReconnectConfig,
58
+ type ToolFilter,
59
+ } from "dsh-project-mcp-manager";
60
+ ```
61
+
62
+ `snapshot()` 的元素类型是 `ProjectFileState`。每一行是 `McpServerRuntimeView`
63
+ (`McpServerView` 再加 `source`、`fiberPhase`、`skipReason`、`toolCount`)。
64
+ `globalState()` 返回 `ProjectServerState`。
65
+
66
+ ## 对配套 UI 的语义化版本
67
+
68
+ `projectMcp` 的方法、`projectMcp/updated`,以及上面导出的视图类型,**对配套 UI**
69
+ 按语义化版本承诺:
70
+
71
+ | 版本位 | 对这个面意味着 |
72
+ |---|---|
73
+ | patch | 修复,方法、事件和视图形状不变。 |
74
+ | minor | 只增加字段、方法或事件。 |
75
+ | major | 删除、改名,或改变含义。 |
76
+
77
+ 配套 UI 把本包写成 peer dependency,并锁定到**同一精确版本**(不要写 `^` 或 `~`)。
78
+ 查询面和 UI 一起动时,两边版本一起动:
79
+
80
+ ```json
81
+ {
82
+ "peerDependencies": {
83
+ "dsh-project-mcp-manager": "<本包的精确版本>"
84
+ }
85
+ }
86
+ ```
87
+
88
+ 把占位符换成 UI 所配套的那一版里、本包 `package.json` 的 `version`。
@@ -0,0 +1,41 @@
1
+ # 发布说明 v0.7.0(适配 dsh 0.2.0-rc.2)
2
+
3
+ [← 返回 README](../README.zh.md) | 相关:[适配记录](../design/adaptation-dsh-0.2.0-rc.2.md) ·
4
+ [v0.6.0 发布说明](v0.6.0.md) ·
5
+ [0.1.6-alpha.2 适配方案](../design/adaptation-dsh-0.1.6-alpha.2.md)
6
+
7
+ **发布日期**:2026-09-30 | **区间**:`v0.6.0 → v0.7.0`
8
+ **宿主**:dsh `0.2.0` 线,从 `0.2.0-rc.2` 起。dsh 0.1.5 继续使用插件 v0.6.0。
9
+
10
+ 官方 dsh 仍不发现项目级 MCP 文件、不按会话 cwd 隔离工具、也没有 MCP 专用 CLI。
11
+ v0.7.0 把装载后端对齐到宿主自带的 `@deepseek-ai/dsh-mcp-client@0.2.0-rc.2`,
12
+ 并补上这一版新增的配置字段。
13
+
14
+ ---
15
+
16
+ ## 亮点速览
17
+
18
+ | 主题 | 一句话 |
19
+ |---|---|
20
+ | 依赖下限 | `@deepseek-ai/dsh-mcp-client` 抬到 `^0.2.0-rc.2`;cordis 开发依赖 `^4.0.4` |
21
+ | 会话事件 | 删除已不存在的 `agent/session-start`;补扫由 `agent/created`(含 `source`)承担 |
22
+ | instructions 上限 | 可选透传 `maxInstructionBytes`;缺省不写键,官方默认 32768 |
23
+ | 定位 | 承认官方 profile patch / HMR / resources;本插件留下项目发现、会话隔离、CLI |
24
+
25
+ ---
26
+
27
+ ## 用户可见变化
28
+
29
+ - 支持矩阵改为 **dsh ≥ 0.2.0-rc.2**(`0.2.0` 元组)。`^0.1.5-rc.1` 解析不到
30
+ `0.2.0-rc.*`,不改范围就会在新宿主里嵌一套旧 client。
31
+ - `.dsh/mcp.yml` 与 JSON 方言可以写 `maxInstructionBytes`(整数 ≥ 1)。省略时
32
+ 不把 32768 抄进配置。超限由官方 client 拒绝该次连接。`dsh-mcp` 不增加对应参数。
33
+ - 官方 stdio 会丢掉父进程里名字像凭据的环境变量和 `DSH_*`。写进 `env` 并经
34
+ `${VAR}` 展开的值仍然传给子进程。
35
+
36
+ ---
37
+
38
+ ## 验证
39
+
40
+ `pnpm test` 六套全绿(model 33 / mcp-file 7 / json-file 13 / json-write 6 /
41
+ registry 46 / cli 21)。headless profile 实机见适配记录。
@@ -0,0 +1,49 @@
1
+ # 发布说明 v0.7.1(代码审查整改)
2
+
3
+ [← 返回 README](../README.zh.md) | 相关:[审查记录](../code-review/review-feat-adapt-dsh-0.2.0-rc.2.zh.md) ·
4
+ [v0.7.0 发布说明](v0.7.0.md) ·
5
+ [dsh 0.2.0-rc.2 适配记录](../design/adaptation-dsh-0.2.0-rc.2.md)
6
+
7
+ **发布日期**:2026-09-30 | **区间**:`v0.7.0 → v0.7.1`
8
+ **宿主**:dsh `0.2.0` 线(从 `0.2.0-rc.2` 起),与 v0.7.0 相同;纯补丁版,配置口径与装载语义不变。
9
+
10
+ v0.7.1 只落地 [feat/adapt-dsh-0.2.0-rc.2 代码审查](../code-review/review-feat-adapt-dsh-0.2.0-rc.2.zh.md)
11
+ 的意见:一处可靠性修复、一处死代码清理、两条回归测试,以及不改变语义的小重构。
12
+
13
+ ---
14
+
15
+ ## 亮点速览
16
+
17
+ | 主题 | 一句话 |
18
+ |---|---|
19
+ | 后台任务失败可见 | 所有不 await 的入队统一走 `schedule()`,失败记一条 warn(原先完全静默) |
20
+ | 死导出 | 删除无人引用的 `DEFAULT_MAX_INSTRUCTION_BYTES`,避免与上游默认漂移 |
21
+ | 回归测试 | 覆盖 `agent/created`(`source: "resume"`)事件路径与「连接字段变更必拆连接」的装载计划 |
22
+ | 小重构 | 抽 `enqueueDiag()`、放宽 `enqueue()` 签名;配置指纹的 `stat` 并发发起 |
23
+
24
+ ---
25
+
26
+ ## 变更明细
27
+
28
+ - **后台入队(审查 S1 / Sonar S9383)**:构造函数的 `agent/created`、`agent/disposed`
29
+ 与插件热更补扫三处,加上文件事件 `kick`、宽限卸载 timer、`kickSweep`,全部改走新的
30
+ `schedule()`——`enqueue()` 的返回值不再被丢弃,失败会记一条
31
+ `项目 MCP 后台任务失败:<原因>`。此前这类失败被链路完全吞掉:对账没跑成,日志里
32
+ 什么也看不到。诊断落盘(fiber settle 后的 `active` / `failed`)仍走独立的
33
+ `enqueueDiag()`:同样排回对账链,但按最佳努力静默失败。
34
+ - **死代码(审查 B1)**:删掉 `DEFAULT_MAX_INSTRUCTION_BYTES`。「缺省不写键、让官方
35
+ 默认值生效」的口径由字段注释与四处输出(官方配置 / 受管行 / view / JSON 条目)的
36
+ 测试固定,不需要再在本地留一份 32768。
37
+ - **回归测试(审查 B2、B3)**:新增两条用例——
38
+ `agent/created` 带 `source: "resume"` 时补扫并挂载会话项目,且对被自己项目行遮蔽的
39
+ 全局实例应用 deny(项目侧压制展开成精确工具名);`planProjectChanges` 在
40
+ `maxInstructionBytes` 这类连接字段变化时产出 unmount → mount,而 `tools` 变化不拆连接。
41
+ - **小重构(审查 S7503、S9381、S9382)**:`enqueue()` 签名放宽为 `() => T | Promise<T>`,
42
+ `serverView()` / `snapshot()` 去掉只为满足签名的 `async`;`trackMount` 的 fiber settle
43
+ 回调改调 `enqueueDiag()`,消掉嵌套 Promise;配置指纹的逐文件 `stat` 改 `Promise.all`
44
+ 并发发起(签名仍按排序后的路径逐条拼接,结果与串行一致)。
45
+
46
+ ## 验证
47
+
48
+ `tsc --noEmit` 通过;六套测试全绿(model 33 / mcp-file 7 / json-file 13 /
49
+ json-write 6 / registry 48 / cli 21,registry 比 v0.7.0 多两条新用例)。
@@ -0,0 +1,37 @@
1
+ # 发布说明 v0.7.2(查询面推送)
2
+
3
+ [← 返回 README](../README.zh.md) | 相关:[查询面](../guide/service.zh.md) ·
4
+ [v0.7.1 发布说明](v0.7.1.md)
5
+
6
+ **发布日期**:2026-10-02 | **区间**:`v0.7.1 → v0.7.2`
7
+ **宿主**:dsh `0.2.0` 线(从 `0.2.0-rc.2` 起),与 v0.7.1 相同。装载语义不变。
8
+
9
+ v0.7.2 让配套 UI 不用再定时轮询 `snapshot()`。对账成功结束时发一条进程内事件,
10
+ 视图类型从包入口具名导出。浏览器 SSE 仍由 UI 包自建。
11
+
12
+ ---
13
+
14
+ ## 亮点速览
15
+
16
+ | 主题 | 一句话 |
17
+ |---|---|
18
+ | 变更事件 | 对账成功结束 `ctx.emit("projectMcp/updated")`,无载荷;监听方再读 `snapshot()` |
19
+ | 具名类型 | 包入口导出 `ProjectFileState`、`McpServerRuntimeView`、`McpServerView`、`McpRowSource` 等,并开始生成 `.d.ts` |
20
+ | 稳定性 | 方法、事件与这些视图类型对配套 UI 按语义化版本承诺;UI 包以同一精确版本 peer-depend |
21
+
22
+ ---
23
+
24
+ ## 变更明细
25
+
26
+ - **变更事件**:每次成功的 `reconcileAll` 结束(含指纹未变、跳过重读但仍跑完巡检的那一轮)
27
+ emit `projectMcp/updated`。查询方法本身不发。监听方抛错记一条
28
+ `项目 MCP 变更事件投递失败:…`,对账仍算成功。
29
+ - **类型出口**:`declaration` 打开后 `lib/index.d.ts` 才存在;`package.json` 的 `types`
30
+ 指向它。`exports` 仍不开放 `lib/` 深导入。
31
+ - **配套 UI**:查询面从「不承诺稳定」改为对该消费方按语义化版本演进。patch 保持形状,
32
+ minor 只增加,major 才删除或改名。peer 写成精确版本,不要 `^` / `~`。
33
+
34
+ ## 验证
35
+
36
+ `pnpm test` 六套全绿(model 33 / mcp-file 7 / json-file 13 / json-write 6 /
37
+ registry 48 / cli 21)。
package/lib/cli.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ export interface CliIo {
3
+ out(line: string): void;
4
+ err(line: string): void;
5
+ }
6
+ export interface CliDeps {
7
+ /** 用户主目录(测试注入):给出时用户层固定为 `<home>/.dsh`,忽略 `$DSH_HOME`。 */
8
+ home?: string;
9
+ /** 项目根解析(默认向上找 .git,测试注入)。 */
10
+ resolveProjectRoot?: (cwd: string) => Promise<string>;
11
+ /** `import --from -` 的 stdin(测试注入;缺省读 `process.stdin`)。 */
12
+ readStdin?: () => Promise<string>;
13
+ }
14
+ interface ParsedArgs {
15
+ positional: string[];
16
+ /** 未显式给出时为 undefined:list 展示全部层;add/remove/get 缺省按 project。 */
17
+ scope?: "project" | "user" | "profile";
18
+ /** 写入格式:缺省时取 DSH_MCP_CLI_FORMAT,再缺省 yml。 */
19
+ format?: "yml" | "json";
20
+ /** --scope profile 的目标 profile 名。 */
21
+ profile?: string;
22
+ transport: "stdio" | "http";
23
+ env: Record<string, string>;
24
+ headers: Record<string, string>;
25
+ cwd?: string;
26
+ help: boolean;
27
+ /** import 输入:文件路径或 `-`(stdin)。 */
28
+ from?: string;
29
+ dryRun: boolean;
30
+ overwrite: boolean;
31
+ }
32
+ /** CLI 写入格式的默认值开关:`yml`(默认)或 `json`;`--format` 优先。 */
33
+ export declare const CLI_FORMAT_ENV = "DSH_MCP_CLI_FORMAT";
34
+ /**
35
+ * 手搓 argv 解析(零依赖):`--` 之后全按位置参数;未知「选项」视为服务器命令行
36
+ * token 透传(刻意策略:宁可透传也不误伤 spawn 参数)。
37
+ */
38
+ export declare function parseArgs(argv: string[]): ParsedArgs | {
39
+ error: string;
40
+ };
41
+ export declare function runCli(argv: string[], io: CliIo, deps?: CliDeps): Promise<number>;
42
+ export {};
@@ -0,0 +1,42 @@
1
+ /** dsh 家目录重定位环境变量。 */
2
+ export declare const DSH_HOME_ENV = "DSH_HOME";
3
+ /** 显式指定 profile 名的环境变量(覆盖自动解析;CLI/无 profile 启动场景用)。 */
4
+ export declare const PROFILE_ENV = "DSH_MCP_PROFILE";
5
+ /** 原生受管块文件名(项目层 `<root>/.dsh/mcp.yml` 与用户层 `<dshHome>/mcp.yml` 同名)。 */
6
+ export declare const MCP_YML_FILE = "mcp.yml";
7
+ /** 项目/用户层配置目录名(项目层为 `<root>/.dsh`)。 */
8
+ export declare const DSH_DIR = ".dsh";
9
+ /** 诊断文件名(项目层写 `<root>/.dsh/`,全局层写 `<dshHome>/`)。 */
10
+ export declare const DIAG_FILE = ".mcp-diag.json";
11
+ /**
12
+ * dsh 家目录:`DSH_HOME` 非空则取其绝对化值,否则 `<home>/.dsh`。
13
+ * 显式传入的 home 只影响缺省分支——注入方(测试/CLI `--home`)想完全接管路径时
14
+ * 应直接给 dshHome。
15
+ */
16
+ export declare function dshHomeDir(home?: string, env?: NodeJS.ProcessEnv): string;
17
+ /**
18
+ * 注入优先的 dsh 家目录:显式给出的 home(CLI `deps.home`、测试注入)视为完整接管,
19
+ * 直接取 `<home>/.dsh`;未给出时才解析 `DSH_HOME`。注入点若被 env 反超,注入就失去
20
+ * 隔离意义(测试会读到真实用户配置)。
21
+ */
22
+ export declare function dshHomeFor(home: string | undefined, env?: NodeJS.ProcessEnv): string;
23
+ /** 用户层(全局装载)三个来源文件的路径。 */
24
+ export interface UserLayerPaths {
25
+ mcpYml: string;
26
+ mcpJson: string;
27
+ profilesDir: string;
28
+ }
29
+ /** dsh 家目录 → 用户层三个来源路径。 */
30
+ export declare function userLayerPathsIn(dshHome: string): UserLayerPaths;
31
+ /** profile 层配置文件路径(`<dshHome>/profiles/<name>/mcp.json`)。 */
32
+ export declare function profileMcpJsonFile(profilesDir: string, profile: string): string;
33
+ /** 对方插件全局存储路径(`$DSH_HOME/dsh-mcp.json`);本插件不读取内容,只在存在时诊断。 */
34
+ export declare function foreignUserMcpJsonFile(dshHome: string): string;
35
+ /**
36
+ * 合法 profile 名:字母数字开头,其后允许字母数字、`.`、`_`、`-`。
37
+ * 用于把外部输入(`DSH_MCP_PROFILE`)挡在 profiles 目录内——`..`、
38
+ * `../../somewhere`、绝对路径都会被拒。
39
+ */
40
+ export declare const PROFILE_NAME_RE: RegExp;
41
+ /** profile 名是否可安全拼进 profiles 目录(显式拒绝 `.` 与 `..`)。 */
42
+ export declare function isValidProfileName(name: string): boolean;
package/lib/index.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * dsh-project-mcp-manager —— 独立项目级 MCP 自动加载插件(宿主半区,无 UI)。
3
+ *
4
+ * 用法:在项目根创建 <projectRoot>/.dsh/mcp.yml(原生受管块,格式与
5
+ * profile cordis.patch.yml 一致)或 .dsh/mcp.json(JSON 方言),在该项目
6
+ * 开启 dsh 会话时自动装载其中的 MCP 服务器(经 @deepseek-ai/dsh-mcp-client),
7
+ * 文件改动经 watch 热重载。
8
+ *
9
+ * 装载模型见 README 与 docs/guide/layers.md;查询面见 docs/guide/service.md;
10
+ * 运行时行为见 registry.ts 的类注释。
11
+ */
12
+ import { Context } from "@deepseek-ai/cordis";
13
+ export { bindProjectMcpService, PROJECT_MCP_SERVICE, PROJECT_MCP_UPDATED_EVENT, type ProjectMcpService } from "./service.js";
14
+ export type { FiberPhaseView, McpServerRuntimeView, ProjectFileState, ProjectServerPhase, ProjectServerState } from "./registry.js";
15
+ export type { McpScopeInfo, McpServerView, McpTransport, ReconnectConfig, ToolFilter } from "./model.js";
16
+ export type { PatchRow } from "./mcp-file.js";
17
+ export type { McpRowSource } from "./json-file.js";
18
+ export declare const name = "dsh-project-mcp-manager";
19
+ /** agents 为硬依赖:宿主启动早期插件行先于 agents 服务装载时,等待其就绪后再 apply,
20
+ * 保证构造时的 liveAgents 补扫能看到已恢复/已存在的会话。 */
21
+ export declare const inject: string[];
22
+ /** 显式指定 profile 名的环境变量(实现在 dsh-paths.ts,registry 的校验共用)。 */
23
+ export { PROFILE_ENV } from "./dsh-paths.js";
24
+ export declare function apply(ctx: Context): void;
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { MCP_PLUGIN_NAME } from "./mcp-file.js";
2
2
  import { PROFILE_ENV } from "./dsh-paths.js";
3
3
  import { ProjectMcpRegistry, profileNameFromConfigPath } from "./registry.js";
4
4
  import { bindProjectMcpService, PROJECT_MCP_SERVICE } from "./service.js";
5
- export { bindProjectMcpService, PROJECT_MCP_SERVICE } from "./service.js";
5
+ export { bindProjectMcpService, PROJECT_MCP_SERVICE, PROJECT_MCP_UPDATED_EVENT } from "./service.js";
6
6
  export const name = "dsh-project-mcp-manager";
7
7
  /** agents 为硬依赖:宿主启动早期插件行先于 agents 服务装载时,等待其就绪后再 apply,
8
8
  * 保证构造时的 liveAgents 补扫能看到已恢复/已存在的会话。 */