@dreanzy/dsh-mcp-pack 0.0.0-stage → 0.1.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.
Files changed (56) hide show
  1. package/README.en.md +96 -0
  2. package/README.md +105 -2
  3. package/client/client.js +2737 -0
  4. package/cordis.patch.yml +3 -0
  5. package/icon.svg +7 -0
  6. package/lib/adapter.d.ts +32 -0
  7. package/lib/adapter.js +80 -0
  8. package/lib/carrier.d.ts +40 -0
  9. package/lib/carrier.js +27 -0
  10. package/lib/effective.d.ts +20 -0
  11. package/lib/effective.js +25 -0
  12. package/lib/filter.d.ts +32 -0
  13. package/lib/filter.js +74 -0
  14. package/lib/gate.d.ts +45 -0
  15. package/lib/gate.js +118 -0
  16. package/lib/hooks.d.ts +98 -0
  17. package/lib/hooks.js +341 -0
  18. package/lib/index.d.ts +10 -0
  19. package/lib/index.js +399 -0
  20. package/lib/inventory-section.d.ts +18 -0
  21. package/lib/inventory-section.js +43 -0
  22. package/lib/inventory.d.ts +16 -0
  23. package/lib/inventory.js +126 -0
  24. package/lib/mcp-connection.d.ts +49 -0
  25. package/lib/mcp-connection.js +130 -0
  26. package/lib/mount.d.ts +29 -0
  27. package/lib/mount.js +49 -0
  28. package/lib/policy.d.ts +64 -0
  29. package/lib/policy.js +494 -0
  30. package/lib/pool.d.ts +32 -0
  31. package/lib/pool.js +69 -0
  32. package/lib/public-name.d.ts +9 -0
  33. package/lib/public-name.js +23 -0
  34. package/lib/reconcile.d.ts +18 -0
  35. package/lib/reconcile.js +60 -0
  36. package/lib/routes.d.ts +129 -0
  37. package/lib/routes.js +496 -0
  38. package/lib/servers.d.ts +50 -0
  39. package/lib/servers.js +188 -0
  40. package/lib/shield.d.ts +11 -0
  41. package/lib/shield.js +21 -0
  42. package/lib/snapshot.d.ts +95 -0
  43. package/lib/snapshot.js +275 -0
  44. package/lib/state.d.ts +29 -0
  45. package/lib/state.js +47 -0
  46. package/lib/store.d.ts +45 -0
  47. package/lib/store.js +520 -0
  48. package/lib/suppress.d.ts +35 -0
  49. package/lib/suppress.js +62 -0
  50. package/lib/tools.d.ts +47 -0
  51. package/lib/tools.js +322 -0
  52. package/lib/types.d.ts +197 -0
  53. package/lib/types.js +12 -0
  54. package/locale/en.json +6 -0
  55. package/locale/zh.json +6 -0
  56. package/package.json +80 -4
package/README.en.md ADDED
@@ -0,0 +1,96 @@
1
+ # dsh-mcp-pack
2
+
3
+ Per-workspace control over MCP servers: which projects see which server, and when its tools enter the context.
4
+
5
+ ## What it does
6
+
7
+ In the host, one `@deepseek-ai/dsh-mcp-client` entry is one MCP server, declared in the profile's `cordis.patch.yml` or an agent preset. Those entries mount by default: the connection opens, tools register globally, and the model sees every schema on every turn.
8
+
9
+ This plugin takes over that decision:
10
+
11
+ - **On-demand loading** — rows set to `dynamic` or `lazy` do not mount at all. No connection, no tool registration, and the host's `mcp-resources` has no target. The model reads a roster in the system prompt, then `mcp_load`s what it needs.
12
+ - **Tool filtering** — a server that publishes fifty tools and is used for three pays for all fifty. `!`/wildcard rules narrow what reaches the model.
13
+ - **Packs and groups** — one project can disable or defer a server without touching another.
14
+
15
+ ## Loading modes
16
+
17
+ | Mode | Connection | How tools enter the context | Carrier |
18
+ |---|---|---|---|
19
+ | `eager` | Host start, mounted by `mcp-client` | Full schemas live in the tool section | none |
20
+ | `dynamic` (default) | First `mcp_load` | Visible tools are registered natively into the calling agent's scope | `native` |
21
+ | `lazy` | First `mcp_load` | Nothing is registered; `mcp_load` returns schemas and calls go through `mcp_call` | `proxy` |
22
+
23
+ Trade-off: `dynamic` binds arguments best, but every `mcp_load` rewrites the tool section and invalidates the request prefix cache. `lazy` keeps the tool list fixed — the prefix never breaks — at the cost of one proxy hop per call.
24
+
25
+ ## Packs and groups
26
+
27
+ - A **pack** is a named policy table. A workspace binds exactly one pack; an unbound workspace follows the built-in `default`. Policies no longer live inside the project: `<cwd>/.dsh/mcp-manager.json` is legacy and is no longer read either.
28
+ - A **group** says which rows belong together; it is orthogonal to a pack. `groups.json` keys assignments by row key (`own:<name>` / `global:<name>`).
29
+ - Every field resolves **independently**:
30
+
31
+ ```
32
+ enabled: pack.rows[row].enabled ?? pack.groups[group] ?? default.rows[row].enabled ?? default.groups[group] ?? true
33
+ mode: default.rows[row].mode ?? dynamic
34
+ tools: default.rows[row].tools ?? []
35
+ description: default.rows[row].description ?? ''
36
+ ```
37
+
38
+ `mode`, `tools` and `description` describe how the server loads, which is the same everywhere, so they are read from the built-in `default` pack only. A pack overrides `enabled` and nothing else.
39
+
40
+ The group level carries a single on/off boolean. If a group could also set a mode, the next question would be "the group says dynamic, the row says disabled — which wins", and that merge rule has no cheap answer. So mode, tool rules and description stay at the row level.
41
+ - State lives in three independently atomic files under `$DSH_HOME/packs/mcp/`: `packs.json`, `groups.json`, `workspaces.json`.
42
+ - Changing `workingDirectory` mid-session recomputes that session's policy.
43
+
44
+ ## Panel
45
+
46
+ Settings page → **MCP Pack**: three tabs, Groups / Packs / Workspace. The Groups tab edits structure only; switches live on the Packs tab, because one switch with two entry points drifts apart sooner or later.
47
+
48
+ The footer's runtime self-check reports whether the host adapters were found, whether the on-demand tools registered, which rows the gate suppressed, and which connections the pool holds.
49
+
50
+ ## Install
51
+
52
+ ```sh
53
+ dsh plugin --profile <profile> add @dreanzy/dsh-mcp-pack
54
+ ```
55
+
56
+ From source (pnpm must be allowed to run the package's build):
57
+
58
+ ```sh
59
+ dsh plugin --profile <profile> add "github:dreanzy/dsh-plg#<sha>&path:/packages/dsh-mcp-pack"
60
+ ```
61
+
62
+ ## Managed area or your patch
63
+
64
+ The plugin manages two sources, keyed `global:` and `own:` respectively:
65
+
66
+ | | Rows in your patch | Panel-managed |
67
+ |---|---|---|
68
+ | Definition lives in | your `cordis.patch.yml` | `$DSH_HOME/packs/mcp/servers.json` |
69
+ | Mounted by | the host's Loader composition | `ctx.plugin` at runtime |
70
+ | Lifetime | follows the host | follows the plugin — disconnects if the plugin is disabled or reloaded |
71
+ | Editable in the panel | no (read-only) | yes |
72
+
73
+ **Split by how often you change them, do not migrate everything.** Servers whose URL / args / env you touch belong in the managed area; servers that never change can stay in the patch. New servers added through the panel land there by default.
74
+
75
+ **One hard rule**: do not put your only working server in the managed area and set it to `eager`. That path is tied to the plugin's lifetime; a patch row is not.
76
+
77
+ **Security note**: `servers.json` stores connection parameters in clear text, tokens in URLs included — same as your `cordis.patch.yml`. It sits under `$DSH_HOME`, outside any repository's `.gitignore`; do not commit it.
78
+
79
+ ## Known limits
80
+
81
+ 1. **Row definitions are not created by this plugin** — it controls visibility and loading for existing rows; `command`/`url`/`env` still live in your `cordis.patch.yml`. Servers you add through the panel are stored separately and mounted at runtime.
82
+ 2. **The gate only consults the global layer.** Mounting is a process-level decision; per-workspace differences are handled by a per-agent restriction instead.
83
+ 3. **Switching modes interrupts the connection once** — the gate really does kill the child process or drop the HTTP connection.
84
+ 4. **`eager` rows ignore tool filters** — the host mounts them whole and there is no seam to intercept.
85
+ 5. **On-demand modes lose server instructions** — that section is only registered on the mount path. Use `eager` if you need it.
86
+ 6. The connection pool does not survive a host restart; reload after restarting.
87
+
88
+ ## Development
89
+
90
+ ```sh
91
+ pnpm typecheck
92
+ pnpm test
93
+ pnpm build
94
+ ```
95
+
96
+ Decisions live in `docs/adr/`; vocabulary in `GLOSSARY.md`.
package/README.md CHANGED
@@ -1,3 +1,106 @@
1
- # Temporary Holding Version
1
+ # dsh-mcp-pack
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 按工作区控制 MCP 服务器的可见性与加载方式:哪些工程能用哪台 server、它的工具什么时候进入上下文。
4
+
5
+ ## 它做什么
6
+
7
+ 宿主里的 `@deepseek-ai/dsh-mcp-client` 一行一台 server,声明在 profile 的 `cordis.patch.yml` 或 preset 里。那些行默认全部挂载:连接建立、工具注册进全局工具表,模型每一轮都看得见全部工具 schema。
8
+
9
+ 本插件接管这段:
10
+
11
+ - **按需加载**:`dynamic`/`lazy` 的行在启动时根本不挂载——连接不建立、工具不注册、宿主 `mcp-resources` 也没有目标可访问。模型先从名册里看到有哪些 server,再 `mcp_load` 需要的那些。
12
+ - **工具过滤**:一台 server 几十个工具、常用只有几个时,用 `!` 通配规则只放行要用的。
13
+ - **工作区分层**:同一个工程可以关掉某台 server,或不加载它,而不影响别的工程。
14
+
15
+ ## 三种加载模式
16
+
17
+ | 模式 | 连接时机 | 工具怎么进上下文 | 载体 |
18
+ |---|---|---|---|
19
+ | `eager`(全部加载) | 宿主启动,由 `mcp-client` 挂载 | 全部 schema 常驻工具段 | 无 |
20
+ | `dynamic`(动态插入,默认) | 首次 `mcp_load` | 加载后把可见工具原生注册进调用方 agent 作用域 | `native` |
21
+ | `lazy`(延迟加载) | 首次 `mcp_load` | 一个都不注册;`mcp_load` 只返回 schema,经 `mcp_call` 调用 | `proxy` |
22
+
23
+ 取舍:`dynamic` 绑定质量最好,代价是每次 `mcp_load` 都会让工具段变一次,请求前缀缓存随之失效。`lazy` 的工具列表永不变,前缀不失效,但每次调用多一层代理。
24
+
25
+ ## 分包与分组
26
+
27
+ - **Pack(分包)**:一张具名的策略表。工作区只绑一个 pack,未绑定走内置的 `default`。策略不再落在工程目录里:`<cwd>/.dsh/mcp-manager.json` 已废弃,兼容读也已移除。
28
+ - **分组**:描述「哪些行是一类」,与 pack 正交。`groups.json` 的 `assignments` 以行键(`own:<name>` / `global:<name>`)为键。
29
+ - **逐字段独立求值**:
30
+
31
+ ```
32
+ enabled: pack.rows[行].enabled ?? pack.groups[组] ?? default.rows[行].enabled ?? default.groups[组] ?? 开
33
+ mode: pack.rows[行].mode ?? default.rows[行].mode ?? dynamic
34
+ tools: pack.rows[行].tools ?? default.rows[行].tools ?? []
35
+ description: pack.rows[行].description ?? default.rows[行].description ?? ''
36
+ ```
37
+
38
+ 组级**只有开/关**一个布尔:组若也能设模式,立刻要回答「组说 dynamic、行说 disabled 算哪个」,那条合并规则没有便宜答案。所以模式、工具规则、描述只留在行级。
39
+ - 落盘三份,各自原子:`$DSH_HOME/packs/mcp/` 下的 `packs.json`(pack 名册 + UI 开关)、`groups.json`(分组与归属)、`workspaces.json`(工作区绑定)。
40
+ - 会话中途 `workingDirectory` 变化会重算该会话的策略。
41
+
42
+ ## 面板
43
+
44
+ 设置页 →「MCP Pack」:「分组 | 分包 | 工作区」三页。分组页只做结构(新增/改名/删除/改归属),开关只出现在分包页——同一个开关两个入口,两边状态迟早会不一致。工作区页把目录绑到 pack,未绑即默认。
45
+
46
+ 面板底部「运行期自检」展开后是插件的运行状态:宿主适配器是否取到、按需工具是否注册、哪些行被闸门摘掉、连接池里有哪些 key。
47
+
48
+ ## 安装
49
+
50
+ 需要 DSH `0.2.x`(`0.2.0-rc.2` 及以上)。
51
+
52
+ **在 Web 界面安装**:设置 → 插件 → 添加插件 → 安装第三方插件,输入包名:
53
+
54
+ ```
55
+ @dreanzy/dsh-mcp-pack
56
+ ```
57
+
58
+ **命令行安装**:
59
+
60
+ ```sh
61
+ dsh plugin --profile <profile> add @dreanzy/dsh-mcp-pack
62
+ ```
63
+
64
+ 装完重启 Host、刷新页面。
65
+
66
+ 从源码安装(需要 pnpm 允许该包执行构建):
67
+
68
+ ```sh
69
+ dsh plugin --profile <profile> add "github:dreanzy/dsh-plg#<sha>&path:/packages/dsh-mcp-pack"
70
+ ```
71
+
72
+ ## 托管区还是留在 patch
73
+
74
+ 本插件管理两处来源,行键分别以 `global:` 与 `own:` 开头:
75
+
76
+ | | patch 里的行 | 面板托管 |
77
+ |---|---|---|
78
+ | 定义写在哪 | 你的 `cordis.patch.yml` | `$DSH_HOME/packs/mcp/servers.json` |
79
+ | 挂载方式 | 宿主的 Loader 组合 | `ctx.plugin` 运行时挂载 |
80
+ | 生命周期 | 跟宿主 | 跟插件:插件被禁用或重载时断开 |
81
+ | 面板可改定义 | 否(只读展示) | 是 |
82
+
83
+ **建议按改动频率分配,不必全部搬**:经常改 URL / args / env 的放托管区,长期不动的留在 patch。新加的用面板加,默认就进托管。
84
+
85
+ **一条硬约束**:不要把唯一可用的 server 放进托管区还设成 `eager`。那条路径绑在插件生命周期上,插件掉线它就断;patch 里的行不会。
86
+
87
+ **安全提醒**:`servers.json` 与你的 `cordis.patch.yml` 一样会把连接参数(包括 URL 里的 token)明文存盘。它位于 `$DSH_HOME`,不在任何仓库的 `.gitignore` 覆盖范围内,不要提交出去。
88
+
89
+ ## 已知限制
90
+
91
+ 1. **行定义不由本插件创建**。它只控制已有行的可见性与加载方式;server 的 `command`/`url`/`env` 仍写在你的 `cordis.patch.yml` 里。
92
+ 2. **闸门按 `default` pack 的判断执行**。挂载是进程级的事,与会话所在目录无关;工作区差异由 per-agent 的 `restrict` 屏蔽处理。
93
+ 3. **切换模式有一次连接中断**:提交后闸门会真的 kill 子进程或断开 HTTP 连接。
94
+ 4. **`eager` 行不受工具过滤影响**:它由 `mcp-client` 整台挂载,本插件没有插手的点。
95
+ 5. **按需模式拿不到 server instructions**:那是挂载路径才注册的段。需要就把它设成 `eager`。
96
+ 6. 连接池不跨宿主重启,重启后需要重新 `mcp_load`。
97
+
98
+ ## 开发
99
+
100
+ ```sh
101
+ pnpm typecheck
102
+ pnpm test
103
+ pnpm build
104
+ ```
105
+
106
+ 决策记录见 `docs/adr/`,术语见 `GLOSSARY.md`。