@xxxyz/dsh-mcp-manager 2.0.6 → 2.1.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 CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  <!-- Hero -->
4
4
  <div align="center">
5
- <b style="font-size: 1.15em;">DeepSeek Harness 的 MCP 服务管理器:装没装、连没连、一页管完。</b><br /><br />
5
+ <b style="font-size: 1.15em;">DeepSeek Harness 的 MCP 服务与技能管理器:装没装、连没连、一页管完。</b><br /><br />
6
6
  <code>服务器列表</code> <code>新增 / 编辑 / 删除</code> <code>启用 / 停用</code> <code>重启</code> <code>工具数健康</code> <code>JSON 导出 / 导入</code><br />
7
- <code>4 个模型工具</code> <code>HTTP API</code> <code>npx / npm / dsh plugin / 脚本</code><br /><br />
8
- <b>设置 → MCP 管理</b> 管理项目级与全局 <code>cordis.patch.yml</code> 中的 <code>@deepseek-ai/dsh-mcp-client</code> 行——<br />
9
- 无需再手改配置文件,所有修改即改即生效(HMR 热应用),重启、升级后依然存在。
7
+ <code>Skills 浏览 / 搜索 / 停用</code> <code>4 个模型工具</code> <code>HTTP API</code> <code>dsh plugin 一条命令</code><br /><br />
8
+ <b>设置 → MCP 管理</b> 管理项目级与全局 <code>cordis.patch.yml</code> 中的 <code>@deepseek-ai/dsh-mcp-client</code> 行,<br />
9
+ <b>设置 → Skills 管理</b> 浏览并停用各来源的技能——无需再手改配置文件,所有修改即改即生效(HMR 热应用),重启、升级后依然存在。
10
10
  </div>
11
11
 
12
12
  <div align="center">
@@ -15,9 +15,15 @@
15
15
  [![License](https://img.shields.io/github/license/xxxyz/DeepSeekHarness-MCP-Manager?color=blue)](LICENSE)
16
16
  [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
17
17
  [![GitHub](https://img.shields.io/badge/GitHub-xxxyz%2FDeepSeekHarness--MCP--Manager-181717?logo=github)](https://github.com/xxxyz/DeepSeekHarness-MCP-Manager)
18
+ [![dsh.market](https://img.shields.io/badge/dsh.market-%E2%9C%93-3fb950)](https://dsh.market)
19
+ [![awesome-dsh-plugin](https://img.shields.io/badge/awesome--dsh--plugin-%E5%BE%85%E5%90%88%E5%B9%B6-ffd93d)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/2078)
18
20
 
19
21
  </div>
20
22
 
23
+ <div align="center">
24
+ 🛒 已收录于 <a href="https://dsh.market"><b>dsh.market</b></a> · 已提交 <a href="https://awesome-dsh-plugin.com"><b>awesome-dsh-plugin.com</b></a> 官方列表收录(<a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/2078"><b>PR #2078</b></a> 待合并)
25
+ </div>
26
+
21
27
  <div align="center">
22
28
  🌏 <a href="./README.md"><b>中文</b></a> · <a href="./README_EN.md">English</a>
23
29
  </div>
@@ -36,110 +42,65 @@
36
42
  - **🩺 健康检查**:每台服务器实时工具数与 loader 阶段,异常一目了然
37
43
  - **📦 备份 / 恢复**:JSON 导出 / 导入,合并新增、已存在自动跳过
38
44
  - **🤖 模型工具**:宿主注册 4 个 `mcp_manager_*` 工具,模型可直接查询与操作 MCP 服务
39
- - **🌐 HTTP API**:`POST /dsh-mcp-manager/api`(JSON `{op, args}` `{ok, ...}`),供客户端与脚本调用
40
- - **📦 跨平台安装**:Windows / macOS / Linux 一条命令(npx / npm / `dsh plugin` / 脚本)
45
+ - **🧠 技能管理**:**设置 Skills 管理** 页列出 DSH 全部技能,按来源分组(项目级 / 运行时 / 自定义 / 用户级 / 内置 / 插件自带)并支持搜索与按 provider 折叠;一键停用 / 启用任意技能(rank-0 override provider,任何来源层级都可禁),状态持久化到 `dsh-skill-manager.json`,HMR 即时生效
46
+ - **🌐 HTTP API**:`POST /dsh-mcp-manager/api`(JSON `{op, args}` `{ok, ...}`),供客户端与脚本调用。带跨站(CSRF)防护:仅接受 POST、必须携带 `x-dsh-plugin: dsh-mcp-manager` 请求头、校验同源 Origin(curl 等本地脚本无需 Origin)
47
+ - **📦 一键安装**:`dsh plugin --profile web add` 一条命令装包 + 自动挂载(Windows / macOS / Linux)
41
48
 
42
49
  ## 🚀 安装
43
50
 
44
- **前置**:DSH 已装好(`dsh web` 能正常运行),Node.js ≥ 18。
45
-
46
- ### 方式一 · npx 一条命令(推荐)
47
-
48
- ```sh
49
- npx -y @xxxyz/dsh-mcp-manager
50
- ```
51
-
52
- 所有参数照常透传:`npx -y @xxxyz/dsh-mcp-manager --dsh-home /path/.dsh --profile web --repair --port 3080`。
53
-
54
- ### 方式二 · npm 全局安装(适合经常使用)
55
-
56
- ```sh
57
- npm i -g @xxxyz/dsh-mcp-manager
58
- dsh-mcp-manager # 安装插件
59
- dsh-mcp-manager-uninstall # 卸载插件
60
- npm i -g @xxxyz/dsh-mcp-manager@latest # 升级
61
- ```
62
-
63
- ### 方式三 · dsh 命令安装(bundle 方式)
51
+ **前置**:已装好 DSH(`dsh web` 能正常运行),Node.js ≥ 18、pnpm ≥ 9
64
52
 
65
- ```sh
66
- dsh plugin --profile web add @xxxyz/dsh-mcp-manager
67
- # 或 GitHub 源(构建产物 lib/ 已入库,无需本地构建)
68
- dsh plugin --profile web add github:xxxyz/DeepSeekHarness-MCP-Manager
69
- ```
53
+ ### 方式一 · dsh 命令安装(推荐)
70
54
 
71
- ### 方式四 · 免 npm 账号:GitHub 直拉
55
+ 一条命令装包 + **自动挂载**(`dsh.bundle.patch` 机制,无需手动改任何配置文件):
72
56
 
73
57
  ```sh
74
- npx -y github:xxxyz/DeepSeekHarness-MCP-Manager
58
+ dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
75
59
  ```
76
60
 
77
- <details>
78
- <summary><b>脚本安装</b>(源码方式:下载仓库后执行,幂等)</summary>
61
+ 装完**硬刷新浏览器**(Cmd/Ctrl+Shift+R)即可看到 **设置 → MCP 管理**(DSH 对 client 改动热加载,无需重启;仅 host 半更新时需要重启)。
79
62
 
80
- **Windows(PowerShell)**:
63
+ ### 方式二 · 让 DSH 自己装
81
64
 
82
- ```powershell
83
- .\dsh-mcp-manager\install.ps1 # 默认 ~/.dsh + web profile
84
- .\dsh-mcp-manager\install.ps1 -DshHome D:\path\.dsh -Profile web
85
- ```
65
+ 把下面这段提示词发给任意一个 DSH 会话:
86
66
 
87
- **macOS / Linux(bash)**(无执行权限先 `chmod +x dsh-mcp-manager/install.sh`):
88
-
89
- ```sh
90
- ./dsh-mcp-manager/install.sh # 默认 ~/.dsh + web profile
91
- ./dsh-mcp-manager/install.sh --dsh-home /path/.dsh --profile web
67
+ ```text
68
+ 帮我安装 dsh-mcp-manager 插件(DSH MCP 服务管理器),步骤:
69
+ 1. 执行 dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
70
+ 2. 完成后提醒我硬刷新浏览器(Cmd/Ctrl+Shift+R)
71
+ 遇到报错先查 https://github.com/xxxyz/DeepSeekHarness-MCP-Manager README 的常见问题表。
92
72
  ```
93
73
 
94
- **任何平台直接运行**:
74
+ **更新**
95
75
 
96
76
  ```sh
97
- node dsh-mcp-manager/install.mjs [--dsh-home <path>] [--profile <name>] [--port <n>] [--repair] [--skip-patch]
77
+ dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
98
78
  ```
99
79
 
100
- 安装脚本会:① 复制到 `local-packages/`(真源备份,DSH 升级不动它)② 复制到 `profiles/node_modules/`(普通复制而非软链接,保证 ESM 能解析 `@deepseek-ai/dsh-tools`)③ 幂等追加 loader 行(insert 块)。
101
-
102
- </details>
103
-
104
- 装完**硬刷新浏览器**(Cmd/Ctrl+Shift+R),打开 **设置 → MCP 管理** 即可看到管理页。若未出现,重启一次 DSH(host 半首次挂载需要)。
80
+ 也可把 `~/.dsh/profiles/web/package.json` 里的版本号改高后 `pnpm install`。改完**硬刷新浏览器**(Cmd/Ctrl+Shift+R)即可(client 改动无需重启 DSH)。
105
81
 
106
82
  <details>
107
- <summary><b>卸载</b></summary>
108
-
109
- ```sh
110
- dsh-mcp-manager-uninstall # 若用 npm -g 安装
111
- # 或:.\uninstall.ps1 | ./uninstall.sh | node uninstall.mjs [--dsh-home <path>] [--profile <name>]
112
- ```
83
+ <summary><b>常见问题</b></summary>
113
84
 
114
- 然后重启 DSH。卸载删除部署副本、`local-packages` 真源,并清理 loader 行(补丁保持合法)。
85
+ | 现象 | 原因与解决 |
86
+ |---|---|
87
+ | 装完设置里没有「MCP 管理」 | 硬刷新(Cmd/Ctrl+Shift+R);仍没有就重启 DSH 一次。 |
88
+ | 页面出现**两个 MCP 页签 / 工具重复** | 双挂载:同时存在旧的 loader 行与新的 bundle 条目。删掉 `cordis.patch.yml` 里的旧 loader 行,或 `dsh.profile.bundles` 里的条目,重启 DSH。 |
89
+ | 之前用旧方式装过,现在想升级 | 新版 bundle 自带防双挂载 guard,直接 `dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest` 不会重复挂载。要切到新代码:删掉 `~/.dsh/profiles/web/cordis.patch.yml` 里的 `- id: mcp-manager` 行,再删 `local-packages/dsh-mcp-manager` 与 `profiles/node_modules/dsh-mcp-manager` 两个副本,重启 DSH。 |
90
+ | 提示 `dsh: command not found` | 先安装 DSH;或直接用 `npx -y --package @deepseek-ai/dsh dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest`。 |
91
+ | `npm view` 报 404 | 国内镜像(npmmirror)同步有延迟:加 `--registry=https://registry.npmjs.org` 或稍等再试。 |
92
+ | 修改配置后未生效 | 所有修改走 HMR 热应用,等 1–2 秒自动刷新;页面会自动轮询。 |
115
93
 
116
94
  </details>
117
95
 
118
96
  <details>
119
- <summary><b>DSH 升级后:--repair</b></summary>
120
-
121
- DSH 升级(或 HMR 状态异常)后若 **设置里没有"MCP 管理"** 或 **`mcp_manager_*` 工具消失**,运行一次修复命令:重新部署 → 递增 loader 行 `config.version`(触发 HMR 重应用)→ 轮询 API 直到 `{ok:true}`(默认 30 秒)。
97
+ <summary><b>卸载</b></summary>
122
98
 
123
99
  ```sh
124
- node dsh-mcp-manager/install.mjs --repair --port 3080
125
- # PowerShell: .\install.ps1 -Repair -Port 3080 bash: ./install.sh --repair --port 3080
100
+ dsh plugin --profile web remove @xxxyz/dsh-mcp-manager
126
101
  ```
127
102
 
128
- 仍不恢复则重启一次 DSH(loader 启动时重新导入)。
129
-
130
- </details>
131
-
132
- <details>
133
- <summary><b>常见问题</b></summary>
134
-
135
- | 现象 | 原因与解决 |
136
- |---|---|
137
- | 装完设置里没有"MCP 管理" | 硬刷新(Cmd/Ctrl+Shift+R);仍没有就重启 DSH 一次。 |
138
- | 页面出现**两个 MCP 页签 / 工具重复** | 双挂载:同时用了 install.mjs 与 `dsh plugin add` 两种方式。卸载其中一种(`dsh-mcp-manager-uninstall` 或删掉对应的 loader 行 / `dsh.profile.bundles` 条目)。 |
139
- | DSH 升级后工具消失 | 跑一次 `--repair`(见上)。 |
140
- | `npx` / `npm view` 报 404 | 国内镜像(npmmirror)同步有延迟:加 `--registry=https://registry.npmjs.org` 或稍等再试。 |
141
- | 安装脚本报错 `EPERM` | DSH 正在运行占用了文件:先退出 DSH 再装。 |
142
- | 修改配置后未生效 | 所有修改走 HMR 热应用,等 1–2 秒自动刷新;页面会自动轮询。 |
103
+ 然后重启 DSH
143
104
 
144
105
  </details>
145
106
 
@@ -152,30 +113,35 @@ node dsh-mcp-manager/install.mjs --repair --port 3080
152
113
  - **备份 / 恢复**:一键导出 JSON,或粘贴 JSON 导入(合并新增,已存在自动跳过)。
153
114
  - 页面底部显示正在编辑的补丁文件路径。
154
115
 
116
+ 打开 **设置 → Skills 管理**:
117
+
118
+ - **浏览 / 搜索**:列出 DSH 全部技能,按来源分组(项目级 / 运行时 / 自定义 / 用户级 / 内置 / 插件自带),组内按 provider 折叠;搜索框实时过滤。
119
+ - **启用 / 停用**:一键切换任意技能的启用状态——通过 rank-0 override provider(`dsh-mcp-manager-override`)实现,任何来源层级(含项目级)都能禁用。
120
+ - **持久化**:停用状态写入 `<profileDir>/dsh-skill-manager.json`,重启后保留;改动经 HMR 即时生效。
121
+
155
122
  ## ⚙️ 配置
156
123
 
157
124
  插件自身在 loader 行中的配置:
158
125
 
159
126
  | 字段 | 说明 |
160
127
  |---|---|
161
- | `version` | loader 行 `config.version`。`--repair` 会将其递增以强制 HMR 重应用,无需手动修改。 |
128
+ | `version` | loader 行 `config.version`,仅用于触发 HMR 重应用;官方通道安装下由 bundle 自动管理,无需手动修改。 |
162
129
 
163
130
  loader 行必须为 **`insert` 块**形式(DSH patch 方言中普通 `- id:` 行只是对已存在条目的覆盖,无法新增插件):
164
131
 
165
132
  ```yaml
166
133
  - insert:
167
134
  - id: dsh-mcp-manager
168
- name: dsh-mcp-manager
169
- config:
170
- version: 1
135
+ name: '@xxxyz/dsh-mcp-manager'
171
136
  ```
172
137
 
138
+ > 无需手动写这行——`dsh plugin add` 的 bundle patch 会自动插入(见 `cordis.patch.yml`)。
139
+
173
140
  ## 🏗️ 架构
174
141
 
175
142
  - **宿主端**(`src/index.ts` → `lib/index.js`,对象形态 Cordis 插件 `{name, inject, apply}`):`inject` 声明 `timer/fs/settings/sandboxPolicy/webServer/tools`,框架保证就绪并在依赖消失时自动重载——这是插件跨 DSH 升级存活的机制。注册 4 个模型工具(`ctx.tools.register(defineTool(...))`)与精确路由 `POST /dsh-mcp-manager/api`(`ctx.effect` 作用域化清理);对 `cordis.patch.yml` 做行级 CRUD(迷你 YAML 解析 + 按文件写锁)。
176
143
  - **浏览器端**(`lib/client.js`,ModuleLoader CJS bundle):注册 设置 → MCP 管理 页(`settings.section` 槽位,order 16),经同源 `fetch('/dsh-mcp-manager/api')` 与宿主通信,不直接访问文件系统。
177
- - **loader 行**:写入 profile`cordis.patch.yml`,client-modules 服务扫描启用的条目并下发客户端 bundle。
178
- - **安装器**:`install.mjs`(跨平台核心)/ `install.ps1` / `install.sh` + 对应的 `uninstall.*`;npm 包 `@xxxyz/dsh-mcp-manager` 的 bin 直接执行安装器(`dsh-mcp-manager` / `dsh-mcp-manager-uninstall`)。
144
+ - **loader 行**:由 `dsh plugin add` bundle patch 自动插入,client-modules 服务扫描启用的条目并下发客户端 bundle。
179
145
 
180
146
  ## 🛠️ 开发
181
147
 
package/README_EN.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  <!-- Hero -->
4
4
  <div align="center">
5
- <b style="font-size: 1.15em;">Manage every MCP server in DeepSeek Harness from one settings page — install, configure, monitor.</b><br /><br />
5
+ <b style="font-size: 1.15em;">Manage every MCP server and skill in DeepSeek Harness from one settings page — install, configure, monitor.</b><br /><br />
6
6
  <code>server list</code> <code>add / edit / delete</code> <code>enable / disable</code> <code>restart</code> <code>tool-count health</code> <code>JSON export / import</code><br />
7
- <code>4 model tools</code> <code>HTTP API</code> <code>npx / npm / dsh plugin / scripts</code><br /><br />
7
+ <code>Skills browse / search / disable</code> <code>4 model tools</code> <code>HTTP API</code> <code>dsh plugin one-command</code><br /><br />
8
8
  <b>Settings → MCP 管理</b> manages <code>@deepseek-ai/dsh-mcp-client</code> rows in your project-level and
9
- global <code>cordis.patch.yml</code> no hand-editing, every change applies live via HMR, survives restarts and upgrades.
9
+ global <code>cordis.patch.yml</code>; <b>Settings Skills 管理</b> browses and disables skills from every
10
+ source — no hand-editing, every change applies live via HMR, survives restarts and upgrades.
10
11
  </div>
11
12
 
12
13
  <div align="center">
@@ -15,9 +16,15 @@
15
16
  [![License](https://img.shields.io/github/license/xxxyz/DeepSeekHarness-MCP-Manager?color=blue)](LICENSE)
16
17
  [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
17
18
  [![GitHub](https://img.shields.io/badge/GitHub-xxxyz%2FDeepSeekHarness--MCP--Manager-181717?logo=github)](https://github.com/xxxyz/DeepSeekHarness-MCP-Manager)
19
+ [![dsh.market](https://img.shields.io/badge/dsh.market-%E2%9C%93-3fb950)](https://dsh.market)
20
+ [![awesome-dsh-plugin](https://img.shields.io/badge/awesome--dsh--plugin-pending-ffd93d)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/2078)
18
21
 
19
22
  </div>
20
23
 
24
+ <div align="center">
25
+ 🛒 Listed on <a href="https://dsh.market"><b>dsh.market</b></a> · submitted to <a href="https://awesome-dsh-plugin.com"><b>awesome-dsh-plugin.com</b></a> (<a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/2078"><b>PR #2078</b></a> pending merge)
26
+ </div>
27
+
21
28
  <div align="center">
22
29
  🌏 <a href="./README.md">中文</a> · <a href="./README_EN.md"><b>English</b></a>
23
30
  </div>
@@ -36,110 +43,65 @@
36
43
  - **🩺 Health check** — live tool counts and loader phase per server, problems visible at a glance
37
44
  - **📦 Backup / Restore** — JSON export / import; merges new entries, skips existing ones
38
45
  - **🤖 Model tools** — four `mcp_manager_*` tools registered on the host, so the model can query and manage MCP servers directly
39
- - **🌐 HTTP API** — `POST /dsh-mcp-manager/api` (JSON `{op, args}` `{ok, ...}`) for the client and scripts
40
- - **📦 Cross-platform install** — one command on Windows / macOS / Linux (npx / npm / `dsh plugin` / scripts)
46
+ - **🧠 Skill management** — the **Settings → Skills 管理** page lists every DSH skill grouped by source (project / runtime / custom / user / built-in / plugin-bundled) with search and per-provider collapse; enable / disable any skill in one click (rank-0 override provider, so every source level including project-level is disable-able), persisted to `dsh-skill-manager.json`, applied live via HMR
47
+ - **🌐 HTTP API** — `POST /dsh-mcp-manager/api` (JSON `{op, args}` `{ok, ...}`) for the client and scripts. Cross-site (CSRF) protected: POST-only, requires the `x-dsh-plugin: dsh-mcp-manager` request header, and checks the Origin is same-origin (local scripts without an Origin are fine)
48
+ - **📦 One-command install** — `dsh plugin --profile web add` installs and mounts automatically (Windows / macOS / Linux)
41
49
 
42
50
  ## 🚀 Install
43
51
 
44
- **Prerequisite**: DSH installed and running (`dsh web` works), Node.js ≥ 18.
45
-
46
- ### Method 1 · One-shot npx (recommended)
47
-
48
- ```sh
49
- npx -y @xxxyz/dsh-mcp-manager
50
- ```
52
+ **Prerequisite**: DSH installed and running (`dsh web` works), Node.js ≥ 18, pnpm ≥ 9.
51
53
 
52
- All flags pass through: `npx -y @xxxyz/dsh-mcp-manager --dsh-home /path/.dsh --profile web --repair --port 3080`.
54
+ ### Method 1 · dsh command (recommended)
53
55
 
54
- ### Method 2 · Global npm install (for frequent use)
56
+ One command installs the package and **auto-mounts** it (the `dsh.bundle.patch` mechanism — no manual config file edits needed):
55
57
 
56
58
  ```sh
57
- npm i -g @xxxyz/dsh-mcp-manager
58
- dsh-mcp-manager # install the plugin
59
- dsh-mcp-manager-uninstall # uninstall the plugin
60
- npm i -g @xxxyz/dsh-mcp-manager@latest # upgrade
59
+ dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
61
60
  ```
62
61
 
63
- ### Method 3 · dsh command (bundle)
62
+ After installing, **hard-refresh the browser** (Cmd/Ctrl+Shift+R) and open **Settings → MCP 管理** (DSH hot-reloads client changes; a full restart is only needed for host-half changes).
64
63
 
65
- ```sh
66
- dsh plugin --profile web add @xxxyz/dsh-mcp-manager
67
- # or from GitHub (build artifacts are committed, no local build needed)
68
- dsh plugin --profile web add github:xxxyz/DeepSeekHarness-MCP-Manager
69
- ```
64
+ ### Method 2 · Let DSH install it
70
65
 
71
- ### Method 4 · No npm account: straight from GitHub
66
+ Paste this prompt into any DSH conversation:
72
67
 
73
- ```sh
74
- npx -y github:xxxyz/DeepSeekHarness-MCP-Manager
68
+ ```text
69
+ Install the dsh-mcp-manager plugin (DSH MCP server manager):
70
+ 1. Run dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
71
+ 2. When done, remind me to hard-refresh the browser (Cmd/Ctrl+Shift+R)
72
+ If you hit an error, check https://github.com/xxxyz/DeepSeekHarness-MCP-Manager README's FAQ table.
75
73
  ```
76
74
 
77
- <details>
78
- <summary><b>Script install</b> (source checkout; idempotent)</summary>
79
-
80
- **Windows (PowerShell)**:
81
-
82
- ```powershell
83
- .\dsh-mcp-manager\install.ps1 # default: ~/.dsh, web profile
84
- .\dsh-mcp-manager\install.ps1 -DshHome D:\path\.dsh -Profile web
85
- ```
86
-
87
- **macOS / Linux (bash)** (run `chmod +x dsh-mcp-manager/install.sh` if needed):
75
+ **Update**
88
76
 
89
77
  ```sh
90
- ./dsh-mcp-manager/install.sh # default: ~/.dsh, web profile
91
- ./dsh-mcp-manager/install.sh --dsh-home /path/.dsh --profile web
78
+ dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
92
79
  ```
93
80
 
94
- **Any platform (direct)**:
95
-
96
- ```sh
97
- node dsh-mcp-manager/install.mjs [--dsh-home <path>] [--profile <name>] [--port <n>] [--repair] [--skip-patch]
98
- ```
99
-
100
- The installer ① copies the package to `local-packages/` (source of record, untouched by DSH upgrades) ② copies it into `profiles/node_modules/` (a plain copy, not a symlink, so ESM resolves `@deepseek-ai/dsh-tools`) ③ appends the loader row (an `insert` block, idempotent).
101
-
102
- </details>
103
-
104
- After installing, **hard-refresh the browser** (Cmd/Ctrl+Shift+R) and open **Settings → MCP 管理**. If the page is missing, restart DSH once (the host half needs a first mount).
81
+ Or bump the version in `~/.dsh/profiles/web/package.json` and run `pnpm install`. Hard-refresh the browser afterwards (Cmd/Ctrl+Shift+R) — client changes are hot-reloaded; only host-half changes need a restart.
105
82
 
106
83
  <details>
107
- <summary><b>Uninstall</b></summary>
108
-
109
- ```sh
110
- dsh-mcp-manager-uninstall # if installed via npm -g
111
- # or: .\uninstall.ps1 | ./uninstall.sh | node uninstall.mjs [--dsh-home <path>] [--profile <name>]
112
- ```
84
+ <summary><b>FAQ</b></summary>
113
85
 
114
- Then restart DSH. Uninstall removes the deployed copy, the `local-packages` source, and the loader row (the patch stays a valid array).
86
+ | Symptom | Cause & fix |
87
+ |---|---|
88
+ | No "MCP 管理" in Settings after install | Hard-refresh (Cmd/Ctrl+Shift+R); if still missing, restart DSH once. |
89
+ | **Two MCP tabs / duplicated tools** | Double mount: both a legacy loader row and the new bundle entry exist. Delete the old loader row from `cordis.patch.yml`, or the `dsh.profile.bundles` entry, then restart DSH. |
90
+ | Upgrading from a legacy script install | The new bundle has a built-in double-mount guard — `dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest` won't crash. To actually switch to the new code: remove the old `- id: mcp-manager` row from `~/.dsh/profiles/web/cordis.patch.yml`, delete `local-packages/dsh-mcp-manager` and `profiles/node_modules/dsh-mcp-manager`, then restart DSH. |
91
+ | `dsh: command not found` | Install DSH first, or use `npx -y --package @deepseek-ai/dsh dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest`. |
92
+ | `npm view` reports 404 | Local mirror (npmmirror) sync lag: add `--registry=https://registry.npmjs.org` or wait a moment. |
93
+ | Config changes don't take effect | All changes apply via HMR within 1–2 s; the page auto-polls. |
115
94
 
116
95
  </details>
117
96
 
118
97
  <details>
119
- <summary><b>After a DSH upgrade: --repair</b></summary>
120
-
121
- If a DSH upgrade (or a broken HMR state) leaves the settings page or the `mcp_manager_*` tools missing, run one repair command: redeploy → bump the loader row's `config.version` (forces an HMR re-apply) → poll the API until `{ok:true}` (default 30 s).
98
+ <summary><b>Uninstall</b></summary>
122
99
 
123
100
  ```sh
124
- node dsh-mcp-manager/install.mjs --repair --port 3080
125
- # PowerShell: .\install.ps1 -Repair -Port 3080 bash: ./install.sh --repair --port 3080
101
+ dsh plugin --profile web remove @xxxyz/dsh-mcp-manager
126
102
  ```
127
103
 
128
- If the API still does not answer, restart DSH once (the loader re-imports at boot).
129
-
130
- </details>
131
-
132
- <details>
133
- <summary><b>FAQ</b></summary>
134
-
135
- | Symptom | Cause & fix |
136
- |---|---|
137
- | No "MCP 管理" in Settings after install | Hard-refresh (Cmd/Ctrl+Shift+R); if still missing, restart DSH once. |
138
- | **Two MCP tabs / duplicated tools** | Double mount: both install.mjs and `dsh plugin add` were used. Remove one (run `dsh-mcp-manager-uninstall`, or delete the extra loader row / `dsh.profile.bundles` entry). |
139
- | Tools missing after a DSH upgrade | Run `--repair` (see above). |
140
- | `npx` / `npm view` reports 404 | Local mirror (npmmirror) sync lag: add `--registry=https://registry.npmjs.org` or wait a moment. |
141
- | Installer fails with `EPERM` | DSH is running and holds the files: quit DSH before installing. |
142
- | Config changes don't take effect | All changes apply via HMR within 1–2 s; the page auto-polls. |
104
+ Then restart DSH.
143
105
 
144
106
  </details>
145
107
 
@@ -152,30 +114,35 @@ Open **Settings → MCP 管理**:
152
114
  - **Backup / Restore**: export the configuration as JSON with one click, or paste JSON to import (merges new entries, skips existing ones).
153
115
  - The file path being edited is shown at the bottom of the page.
154
116
 
117
+ Open **Settings → Skills 管理**:
118
+
119
+ - **Browse / Search**: lists every DSH skill grouped by source (project / runtime / custom / user / built-in / plugin-bundled), collapsed per provider; the search box filters live.
120
+ - **Enable / Disable**: toggle any skill in one click — implemented with a rank-0 override provider (`dsh-mcp-manager-override`), so every source level (including project-level) can be disabled.
121
+ - **Persistence**: disabled state is written to `<profileDir>/dsh-skill-manager.json`, survives restarts, and applies live via HMR.
122
+
155
123
  ## ⚙️ Configuration
156
124
 
157
125
  Configuration of the plugin itself on its loader row:
158
126
 
159
127
  | Field | Description |
160
128
  |---|---|
161
- | `version` | The loader row's `config.version`. `--repair` increments it to force an HMR re-apply; no manual edits needed. |
129
+ | `version` | The loader row's `config.version`, only used to trigger an HMR re-apply; auto-managed by the bundle channel — no manual edits needed. |
162
130
 
163
131
  The loader row must be an **`insert` block** (in DSH's patch dialect a plain `- id:` row only overrides existing entries and can never add a new plugin):
164
132
 
165
133
  ```yaml
166
134
  - insert:
167
135
  - id: dsh-mcp-manager
168
- name: dsh-mcp-manager
169
- config:
170
- version: 1
136
+ name: '@xxxyz/dsh-mcp-manager'
171
137
  ```
172
138
 
139
+ > You don't need to write this row manually — `dsh plugin add` inserts it automatically via the bundle patch (see `cordis.patch.yml`).
140
+
173
141
  ## 🏗️ Architecture
174
142
 
175
143
  - **Host half** (`src/index.ts` → `lib/index.js`, an object-form Cordis plugin `{name, inject, apply}`): `inject` declares `timer/fs/settings/sandboxPolicy/webServer/tools` — the framework guarantees they are ready and reloads the plugin if one disappears, which is how the plugin survives DSH upgrades. Registers four model tools (`ctx.tools.register(defineTool(...))`) and the exact route `POST /dsh-mcp-manager/api` (scoped cleanup via `ctx.effect`); line-level CRUD on `cordis.patch.yml` (mini YAML parser + per-file write lock).
176
144
  - **Browser half** (`lib/client.js`, ModuleLoader CJS bundle): registers the Settings → MCP 管理 page (`settings.section` slot, order 16) and talks to the host through same-origin `fetch('/dsh-mcp-manager/api')` — it never touches the filesystem directly.
177
- - **Loader row**: written into the profile's `cordis.patch.yml`; the client-modules service scans enabled entries and serves the client bundle.
178
- - **Installers**: `install.mjs` (cross-platform core) / `install.ps1` / `install.sh`, plus matching `uninstall.*`; the npm package `@xxxyz/dsh-mcp-manager` exposes them as bins (`dsh-mcp-manager` / `dsh-mcp-manager-uninstall`).
145
+ - **Loader row**: inserted automatically by `dsh plugin add`'s bundle patch; the client-modules service scans enabled entries and serves the client bundle.
179
146
 
180
147
  ## 🛠️ Development
181
148
 
package/cordis.patch.yml CHANGED
@@ -1,7 +1,32 @@
1
- # Bundle patch layer for `dsh plugin add` installs: pnpm installs the scoped
2
- # package as @xxxyz/dsh-mcp-manager, so the insert row names the scoped
3
- # package. The loader-entry installer (install.mjs) mounts the same plugin
4
- # under `dsh-mcp-manager` — both names are registered by the client bundle.
1
+ # Bundle patch layer for the official CLI install:
2
+ #
3
+ # dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
4
+ #
5
+ # `dsh plugin add` reconciles `dsh.profile.bundles` against installed packages
6
+ # and, seeing the `dsh.bundle.patch` declaration in package.json, appends
7
+ # `@xxxyz/dsh-mcp-manager` to the bundle stack. The profile boot then merges
8
+ # THIS patch (a single `insert` of the plugin row). No profile file edits
9
+ # needed — one command installs and mounts.
10
+ #
11
+ # Double-mount guard: a legacy loader row (the removed install.mjs channel,
12
+ # id `mcp-manager`, name `dsh-mcp-manager`) or an aggregate bundle may already
13
+ # mount this plugin under a different entry id. Two mounts both register the
14
+ # /dsh-mcp-manager/api route and fail the whole plugin tree at boot
15
+ # ("duplicate prefix route"). The `!!js` disabled expression backs THIS row
16
+ # off when another *enabled* entry already mounts the plugin; the existing
17
+ # instance then owns the API. The loader evaluates the expression at entry
18
+ # activation — the full entry tree is composed by then, so user-layer
19
+ # (profile cordis.patch.yml) rows are visible too.
20
+ #
21
+ # ORDER MATTERS — do not move `!e.disabled` ahead of the id/name checks.
22
+ # `Entry.disabled` is an uncached getter that re-evaluates the expression,
23
+ # so touching `e.disabled` of THIS entry re-enters this very expression
24
+ # (infinite recursion → "Maximum call stack size exceeded" at boot). The
25
+ # `id !==` check short-circuits on our own row and the `name` check
26
+ # short-circuits on every unrelated entry (e.g. dsh-better-sidebar's own
27
+ # guard), so `!e.disabled` is only ever read on a genuinely matching
28
+ # legacy/aggregate row (a plain boolean, no re-entry).
5
29
  - insert:
6
30
  - id: dsh-mcp-manager
7
31
  name: '@xxxyz/dsh-mcp-manager'
32
+ disabled: !!js "[...ctx.loader.entries()].some((e) => e.options.id !== 'dsh-mcp-manager' && (e.options.name === 'dsh-mcp-manager' || e.options.name === '@xxxyz/dsh-mcp-manager') && !e.disabled)"
package/lib/client.js CHANGED
@@ -47,7 +47,10 @@ const factory = (require) => {
47
47
  '.mcpm-dialog{background:color-mix(in srgb, currentColor 8%, transparent);backdrop-filter:blur(14px) saturate(1.2);border:1px solid rgba(128,128,128,.5);border-radius:10px;padding:16px 18px;min-width:300px;max-width:440px;box-shadow:0 10px 34px rgba(0,0,0,.35);color:inherit}' +
48
48
  '.mcpm-dialog-title{font-size:14px;font-weight:600;margin-bottom:8px}' +
49
49
  '.mcpm-dialog-body{font-size:12px;opacity:.88;line-height:1.7;margin-bottom:16px;word-break:break-all}' +
50
- '.mcpm-dialog-actions{display:flex;justify-content:flex-end;gap:8px}'
50
+ '.mcpm-dialog-actions{display:flex;justify-content:flex-end;gap:8px}' +
51
+ '.skm-search{font-size:12px;padding:5px 8px;border-radius:6px;border:1px solid rgba(128,128,128,.5);background:transparent;color:inherit;width:100%;box-sizing:border-box}' +
52
+ '.skm-group-title{font-size:12px;font-weight:600;opacity:.85;margin-top:4px;padding-bottom:2px;border-bottom:1px solid rgba(128,128,128,.25)}' +
53
+ '.skm-provider-title{font-size:12px;font-weight:600;cursor:pointer;user-select:none;margin-top:4px;opacity:.9}'
51
54
 
52
55
  function ensureCss() {
53
56
  if (typeof document === 'undefined') return
@@ -297,15 +300,97 @@ const factory = (require) => {
297
300
  { name: 'settings.section', id: 'mcp-manager', order: 16, label: 'MCP 管理' },
298
301
  () => React.createElement(MCPPage)
299
302
  ))
303
+
304
+ // Grouping: the registry's built-in providers are "filesystem"
305
+ // (project/user/custom/bundled roots) and "runtime" (register());
306
+ // any other provider name means a plugin-provided skill
307
+ // (e.g. superpowers-dsh). skmLevelOf is mutually exclusive.
308
+ function skmLevelOf(s) {
309
+ if (s.source === 'project-dsh' || s.source === 'project-agents') return '项目级'
310
+ if (s.source === 'runtime') return '运行时'
311
+ if (s.source === 'user-dsh' || s.source === 'user-agents') return '用户级'
312
+ if (s.source === 'bundled') return '内置'
313
+ if (s.source === 'custom' && s.provider === 'filesystem') return '自定义'
314
+ return '插件自带'
315
+ }
316
+ const SKM_GROUPS = ['项目级', '运行时', '自定义', '用户级', '内置', '插件自带']
317
+ function SkillPage() {
318
+ const [state, setState] = React.useState({ loading: true, error: null, skills: [] })
319
+ const [q, setQ] = React.useState('')
320
+ const [busy, setBusy] = React.useState(null)
321
+ const [msg, setMsg] = React.useState(null)
322
+ const [hidden, setHidden] = React.useState({})
323
+ const toggleGroup = (key) => setHidden((h) => ({ ...h, [key]: !h[key] }))
324
+ const refresh = () => {
325
+ apiCall('skill-list', {}).then((res) => {
326
+ setState({ loading: false, error: res && res.ok ? null : ((res && res.error) || '加载失败'), skills: (res && res.skills) || [] })
327
+ }).catch((e) => setState({ loading: false, error: String((e && e.message) || e), skills: [] }))
328
+ }
329
+ React.useEffect(() => { refresh() }, [])
330
+ React.useEffect(() => ctx.interval(() => refresh(), 5000), [])
331
+ React.useEffect(() => { if (!msg) return; return ctx.timeout(() => setMsg(null), 3000) }, [msg])
332
+ const toggle = (skill) => {
333
+ const enabled = skill.provider === 'dsh-mcp-manager-override'
334
+ setBusy(skill.name)
335
+ apiCall('skill-toggle', { name: skill.name, enabled }).then((res) => {
336
+ if (res && res.ok) { setMsg({ kind: 'ok', text: (enabled ? '已启用 ' : '已禁用 ') + skill.name }); refresh() }
337
+ else setMsg({ kind: 'err', text: (res && res.error) || '操作失败' })
338
+ }).catch((e) => setMsg({ kind: 'err', text: String((e && e.message) || e) })).then(() => setBusy(null))
339
+ }
340
+ const query = q.trim().toLowerCase()
341
+ const visible = state.skills.filter((s) => !query || (s.name + ' ' + (s.description || '')).toLowerCase().includes(query))
342
+ return React.createElement('div', { className: 'mcpm-wrap' },
343
+ React.createElement('h2', null, 'Skills 管理'),
344
+ React.createElement('div', { className: 'mcpm-sub' }, '查看与启停 DSH 技能(按层级分组,禁用即时生效,无需重启)'),
345
+ msg && React.createElement('div', { className: 'mcpm-msg ' + msg.kind }, msg.text),
346
+ React.createElement('input', { className: 'skm-search', placeholder: '搜索技能名称或描述…', value: q, onChange: (e) => setQ(e.target.value) }),
347
+ state.loading ? React.createElement('div', { className: 'mcpm-sub' }, '加载中…') :
348
+ state.error ? React.createElement('div', { className: 'mcpm-msg err' }, state.error) :
349
+ SKM_GROUPS.map((key) => {
350
+ const items = visible.filter((s) => skmLevelOf(s) === key)
351
+ if (!items.length) return null
352
+ // within a level, sub-group by provider (collapsible)
353
+ const byProvider = {}
354
+ items.forEach((s) => { const p = s.provider || 'unknown'; (byProvider[p] = byProvider[p] || []).push(s) })
355
+ const providerKeys = Object.keys(byProvider).sort()
356
+ return React.createElement(React.Fragment, { key },
357
+ React.createElement('div', { className: 'skm-group-title' }, key + '(' + items.length + ')'),
358
+ providerKeys.map((pk) => {
359
+ const pItems = byProvider[pk]
360
+ const collapsed = !query && !!hidden[pk]
361
+ return React.createElement(React.Fragment, { key: pk },
362
+ React.createElement('div', { className: 'skm-provider-title', onClick: () => toggleGroup(pk), title: '点击折叠/展开' },
363
+ (collapsed ? '▸ ' : '▾ ') + pk + '(' + pItems.length + ')'),
364
+ collapsed ? null : pItems.map((s) => {
365
+ const overridden = s.provider === 'dsh-mcp-manager-override'
366
+ const available = !!(s.invocation && s.invocation.modelInvocable)
367
+ return React.createElement('div', { className: 'mcpm-row', key: s.name },
368
+ React.createElement('div', { className: 'mcpm-row-head' },
369
+ React.createElement('span', { className: 'mcpm-name' }, s.name),
370
+ React.createElement('span', { className: 'mcpm-chip ' + (available ? 'on' : 'off') }, available ? '可用' : '禁用'),
371
+ overridden && React.createElement('span', { className: 'mcpm-chip warn' }, '手动禁用'),
372
+ React.createElement('span', { className: 'mcpm-chip live' }, s.provider)),
373
+ React.createElement('div', { className: 'mcpm-sub' }, s.description || ''),
374
+ React.createElement('div', { className: 'mcpm-row-actions' },
375
+ React.createElement('button', { className: 'mcpm-btn', disabled: busy === s.name, onClick: () => toggle(s) }, overridden ? '启用' : '禁用')))
376
+ }))
377
+ }))
378
+ }))
379
+ }
380
+
381
+ slots.inject('settings.section', () => slots.register(
382
+ { name: 'settings.section', id: 'skill-manager', order: 17, label: 'Skills 管理' },
383
+ () => React.createElement(SkillPage)
384
+ ))
300
385
  },
301
386
  }
302
387
  return module.exports
303
388
  }
304
389
 
305
- // Register the client bundle under both names: the loader-entry install
306
- // (install.mjs) mounts it as `dsh-mcp-manager`, while `dsh plugin add`
307
- // (via the dsh.bundle patch) installs the scoped npm package and mounts it
308
- // as `@xxxyz/dsh-mcp-manager`. The boot graph row id must match the id the
309
- // bundle registers, so both are registered — the unused one is inert.
390
+ // Register the client bundle under both names: the official CLI mounts the
391
+ // bundle under the loader entry id `dsh-mcp-manager` (cordis.patch.yml
392
+ // insert row), and the scoped npm package is `@xxxyz/dsh-mcp-manager`. The
393
+ // boot graph row id must match the id the bundle registers, so both are
394
+ // registered — the unused one is inert.
310
395
  window.__ModuleLoader__.load({ id: 'dsh-mcp-manager', factory })
311
396
  window.__ModuleLoader__.load({ id: '@xxxyz/dsh-mcp-manager', factory })