@xxxyz/dsh-mcp-manager 2.0.6 → 2.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.
- package/README.md +41 -82
- package/README_EN.md +41 -82
- package/cordis.patch.yml +29 -4
- package/lib/client.js +91 -6
- package/lib/index.js +109 -1
- package/package.json +3 -13
- package/install.mjs +0 -369
- package/install.ps1 +0 -43
- package/install.sh +0 -33
- package/uninstall.mjs +0 -161
- package/uninstall.ps1 +0 -33
- package/uninstall.sh +0 -28
- package//345/256/211/350/243/205/346/226/271/345/274/217.md +0 -144
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div align="center">
|
|
5
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>
|
|
7
|
+
<code>4 个模型工具</code> <code>HTTP API</code> <code>dsh plugin 一条命令</code><br /><br />
|
|
8
8
|
<b>设置 → MCP 管理</b> 管理项目级与全局 <code>cordis.patch.yml</code> 中的 <code>@deepseek-ai/dsh-mcp-client</code> 行——<br />
|
|
9
9
|
无需再手改配置文件,所有修改即改即生效(HMR 热应用),重启、升级后依然存在。
|
|
10
10
|
</div>
|
|
@@ -15,9 +15,15 @@
|
|
|
15
15
|
[](LICENSE)
|
|
16
16
|
[](package.json)
|
|
17
17
|
[](https://github.com/xxxyz/DeepSeekHarness-MCP-Manager)
|
|
18
|
+
[](https://dsh.market)
|
|
19
|
+
[](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,64 @@
|
|
|
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
|
-
- **📦
|
|
45
|
+
- **🌐 HTTP API**:`POST /dsh-mcp-manager/api`(JSON `{op, args}` → `{ok, ...}`),供客户端与脚本调用。带跨站(CSRF)防护:仅接受 POST、必须携带 `x-dsh-plugin: dsh-mcp-manager` 请求头、校验同源 Origin(curl 等本地脚本无需 Origin)
|
|
46
|
+
- **📦 一键安装**:`dsh plugin --profile web add` 一条命令装包 + 自动挂载(Windows / macOS / Linux)
|
|
41
47
|
|
|
42
48
|
## 🚀 安装
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
**前置**:已装好 DSH(`dsh web` 能正常运行),Node.js ≥ 18、pnpm ≥ 9。
|
|
45
51
|
|
|
46
|
-
### 方式一 ·
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
npx -y @xxxyz/dsh-mcp-manager
|
|
50
|
-
```
|
|
52
|
+
### 方式一 · dsh 命令安装(推荐)
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### 方式二 · npm 全局安装(适合经常使用)
|
|
54
|
+
一条命令装包 + **自动挂载**(`dsh.bundle.patch` 机制,无需手动改任何配置文件):
|
|
55
55
|
|
|
56
56
|
```sh
|
|
57
|
-
|
|
58
|
-
dsh-mcp-manager # 安装插件
|
|
59
|
-
dsh-mcp-manager-uninstall # 卸载插件
|
|
60
|
-
npm i -g @xxxyz/dsh-mcp-manager@latest # 升级
|
|
57
|
+
dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
61
58
|
```
|
|
62
59
|
|
|
63
|
-
|
|
60
|
+
装完**硬刷新浏览器**(Cmd/Ctrl+Shift+R)即可看到 **设置 → MCP 管理**(DSH 对 client 改动热加载,无需重启;仅 host 半更新时需要重启)。
|
|
64
61
|
|
|
65
|
-
|
|
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
|
-
```
|
|
62
|
+
### 方式二 · 让 DSH 自己装
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
把下面这段提示词发给任意一个 DSH 会话:
|
|
72
65
|
|
|
73
|
-
```
|
|
74
|
-
|
|
66
|
+
```text
|
|
67
|
+
帮我安装 dsh-mcp-manager 插件(DSH MCP 服务管理器),步骤:
|
|
68
|
+
1. 执行 dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
69
|
+
2. 完成后提醒我硬刷新浏览器(Cmd/Ctrl+Shift+R)
|
|
70
|
+
遇到报错先查 https://github.com/xxxyz/DeepSeekHarness-MCP-Manager README 的常见问题表。
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
<summary><b>脚本安装</b>(源码方式:下载仓库后执行,幂等)</summary>
|
|
79
|
-
|
|
80
|
-
**Windows(PowerShell)**:
|
|
81
|
-
|
|
82
|
-
```powershell
|
|
83
|
-
.\dsh-mcp-manager\install.ps1 # 默认 ~/.dsh + web profile
|
|
84
|
-
.\dsh-mcp-manager\install.ps1 -DshHome D:\path\.dsh -Profile web
|
|
85
|
-
```
|
|
86
|
-
|
|
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
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**任何平台直接运行**:
|
|
73
|
+
**更新**
|
|
95
74
|
|
|
96
75
|
```sh
|
|
97
|
-
|
|
76
|
+
dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
98
77
|
```
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</details>
|
|
103
|
-
|
|
104
|
-
装完**硬刷新浏览器**(Cmd/Ctrl+Shift+R),打开 **设置 → MCP 管理** 即可看到管理页。若未出现,重启一次 DSH(host 半首次挂载需要)。
|
|
79
|
+
也可把 `~/.dsh/profiles/web/package.json` 里的版本号改高后 `pnpm install`。改完**硬刷新浏览器**(Cmd/Ctrl+Shift+R)即可(client 改动无需重启 DSH)。
|
|
105
80
|
|
|
106
81
|
<details>
|
|
107
|
-
<summary><b
|
|
108
|
-
|
|
109
|
-
```sh
|
|
110
|
-
dsh-mcp-manager-uninstall # 若用 npm -g 安装
|
|
111
|
-
# 或:.\uninstall.ps1 | ./uninstall.sh | node uninstall.mjs [--dsh-home <path>] [--profile <name>]
|
|
112
|
-
```
|
|
82
|
+
<summary><b>常见问题</b></summary>
|
|
113
83
|
|
|
114
|
-
|
|
84
|
+
| 现象 | 原因与解决 |
|
|
85
|
+
|---|---|
|
|
86
|
+
| 装完设置里没有「MCP 管理」 | 硬刷新(Cmd/Ctrl+Shift+R);仍没有就重启 DSH 一次。 |
|
|
87
|
+
| 页面出现**两个 MCP 页签 / 工具重复** | 双挂载:同时存在旧的 loader 行与新的 bundle 条目。删掉 `cordis.patch.yml` 里的旧 loader 行,或 `dsh.profile.bundles` 里的条目,重启 DSH。 |
|
|
88
|
+
| 之前用旧方式装过,现在想升级 | 新版 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。 |
|
|
89
|
+
| 提示 `dsh: command not found` | 先安装 DSH;或直接用 `npx -y --package @deepseek-ai/dsh dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest`。 |
|
|
90
|
+
| `npm view` 报 404 | 国内镜像(npmmirror)同步有延迟:加 `--registry=https://registry.npmjs.org` 或稍等再试。 |
|
|
91
|
+
| 修改配置后未生效 | 所有修改走 HMR 热应用,等 1–2 秒自动刷新;页面会自动轮询。 |
|
|
115
92
|
|
|
116
93
|
</details>
|
|
117
94
|
|
|
118
95
|
<details>
|
|
119
|
-
<summary><b
|
|
120
|
-
|
|
121
|
-
DSH 升级(或 HMR 状态异常)后若 **设置里没有"MCP 管理"** 或 **`mcp_manager_*` 工具消失**,运行一次修复命令:重新部署 → 递增 loader 行 `config.version`(触发 HMR 重应用)→ 轮询 API 直到 `{ok:true}`(默认 30 秒)。
|
|
96
|
+
<summary><b>卸载</b></summary>
|
|
122
97
|
|
|
123
98
|
```sh
|
|
124
|
-
|
|
125
|
-
# PowerShell: .\install.ps1 -Repair -Port 3080 bash: ./install.sh --repair --port 3080
|
|
99
|
+
dsh plugin --profile web remove @xxxyz/dsh-mcp-manager
|
|
126
100
|
```
|
|
127
101
|
|
|
128
|
-
|
|
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 秒自动刷新;页面会自动轮询。 |
|
|
102
|
+
然后重启 DSH。
|
|
143
103
|
|
|
144
104
|
</details>
|
|
145
105
|
|
|
@@ -158,24 +118,23 @@ node dsh-mcp-manager/install.mjs --repair --port 3080
|
|
|
158
118
|
|
|
159
119
|
| 字段 | 说明 |
|
|
160
120
|
|---|---|
|
|
161
|
-
| `version` | loader 行 `config.version
|
|
121
|
+
| `version` | loader 行 `config.version`,仅用于触发 HMR 重应用;官方通道安装下由 bundle 自动管理,无需手动修改。 |
|
|
162
122
|
|
|
163
123
|
loader 行必须为 **`insert` 块**形式(DSH patch 方言中普通 `- id:` 行只是对已存在条目的覆盖,无法新增插件):
|
|
164
124
|
|
|
165
125
|
```yaml
|
|
166
126
|
- insert:
|
|
167
127
|
- id: dsh-mcp-manager
|
|
168
|
-
name: dsh-mcp-manager
|
|
169
|
-
config:
|
|
170
|
-
version: 1
|
|
128
|
+
name: '@xxxyz/dsh-mcp-manager'
|
|
171
129
|
```
|
|
172
130
|
|
|
131
|
+
> 无需手动写这行——`dsh plugin add` 的 bundle patch 会自动插入(见 `cordis.patch.yml`)。
|
|
132
|
+
|
|
173
133
|
## 🏗️ 架构
|
|
174
134
|
|
|
175
135
|
- **宿主端**(`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
136
|
- **浏览器端**(`lib/client.js`,ModuleLoader CJS bundle):注册 设置 → MCP 管理 页(`settings.section` 槽位,order 16),经同源 `fetch('/dsh-mcp-manager/api')` 与宿主通信,不直接访问文件系统。
|
|
177
|
-
- **loader
|
|
178
|
-
- **安装器**:`install.mjs`(跨平台核心)/ `install.ps1` / `install.sh` + 对应的 `uninstall.*`;npm 包 `@xxxyz/dsh-mcp-manager` 的 bin 直接执行安装器(`dsh-mcp-manager` / `dsh-mcp-manager-uninstall`)。
|
|
137
|
+
- **loader 行**:由 `dsh plugin add` 的 bundle patch 自动插入,client-modules 服务扫描启用的条目并下发客户端 bundle。
|
|
179
138
|
|
|
180
139
|
## 🛠️ 开发
|
|
181
140
|
|
package/README_EN.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div align="center">
|
|
5
5
|
<b style="font-size: 1.15em;">Manage every MCP server 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>
|
|
7
|
+
<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
9
|
global <code>cordis.patch.yml</code> — no hand-editing, every change applies live via HMR, survives restarts and upgrades.
|
|
10
10
|
</div>
|
|
@@ -15,9 +15,15 @@
|
|
|
15
15
|
[](LICENSE)
|
|
16
16
|
[](package.json)
|
|
17
17
|
[](https://github.com/xxxyz/DeepSeekHarness-MCP-Manager)
|
|
18
|
+
[](https://dsh.market)
|
|
19
|
+
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/2078)
|
|
18
20
|
|
|
19
21
|
</div>
|
|
20
22
|
|
|
23
|
+
<div align="center">
|
|
24
|
+
🛒 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)
|
|
25
|
+
</div>
|
|
26
|
+
|
|
21
27
|
<div align="center">
|
|
22
28
|
🌏 <a href="./README.md">中文</a> · <a href="./README_EN.md"><b>English</b></a>
|
|
23
29
|
</div>
|
|
@@ -36,110 +42,64 @@
|
|
|
36
42
|
- **🩺 Health check** — live tool counts and loader phase per server, problems visible at a glance
|
|
37
43
|
- **📦 Backup / Restore** — JSON export / import; merges new entries, skips existing ones
|
|
38
44
|
- **🤖 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
|
-
- **📦
|
|
45
|
+
- **🌐 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)
|
|
46
|
+
- **📦 One-command install** — `dsh plugin --profile web add` installs and mounts automatically (Windows / macOS / Linux)
|
|
41
47
|
|
|
42
48
|
## 🚀 Install
|
|
43
49
|
|
|
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
|
-
```
|
|
51
|
-
|
|
52
|
-
All flags pass through: `npx -y @xxxyz/dsh-mcp-manager --dsh-home /path/.dsh --profile web --repair --port 3080`.
|
|
53
|
-
|
|
54
|
-
### Method 2 · Global npm install (for frequent use)
|
|
55
|
-
|
|
56
|
-
```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
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Method 3 · dsh command (bundle)
|
|
50
|
+
**Prerequisite**: DSH installed and running (`dsh web` works), Node.js ≥ 18, pnpm ≥ 9.
|
|
64
51
|
|
|
65
|
-
|
|
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
|
-
```
|
|
52
|
+
### Method 1 · dsh command (recommended)
|
|
70
53
|
|
|
71
|
-
|
|
54
|
+
One command installs the package and **auto-mounts** it (the `dsh.bundle.patch` mechanism — no manual config file edits needed):
|
|
72
55
|
|
|
73
56
|
```sh
|
|
74
|
-
|
|
57
|
+
dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
75
58
|
```
|
|
76
59
|
|
|
77
|
-
|
|
78
|
-
<summary><b>Script install</b> (source checkout; idempotent)</summary>
|
|
60
|
+
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).
|
|
79
61
|
|
|
80
|
-
|
|
62
|
+
### Method 2 · Let DSH install it
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
.\dsh-mcp-manager\install.ps1 # default: ~/.dsh, web profile
|
|
84
|
-
.\dsh-mcp-manager\install.ps1 -DshHome D:\path\.dsh -Profile web
|
|
85
|
-
```
|
|
64
|
+
Paste this prompt into any DSH conversation:
|
|
86
65
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
66
|
+
```text
|
|
67
|
+
Install the dsh-mcp-manager plugin (DSH MCP server manager):
|
|
68
|
+
1. Run dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
69
|
+
2. When done, remind me to hard-refresh the browser (Cmd/Ctrl+Shift+R)
|
|
70
|
+
If you hit an error, check https://github.com/xxxyz/DeepSeekHarness-MCP-Manager README's FAQ table.
|
|
92
71
|
```
|
|
93
72
|
|
|
94
|
-
**
|
|
73
|
+
**Update**
|
|
95
74
|
|
|
96
75
|
```sh
|
|
97
|
-
|
|
76
|
+
dsh plugin --profile web add @xxxyz/dsh-mcp-manager@latest
|
|
98
77
|
```
|
|
99
78
|
|
|
100
|
-
|
|
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).
|
|
79
|
+
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
80
|
|
|
106
81
|
<details>
|
|
107
|
-
<summary><b>
|
|
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
|
-
```
|
|
82
|
+
<summary><b>FAQ</b></summary>
|
|
113
83
|
|
|
114
|
-
|
|
84
|
+
| Symptom | Cause & fix |
|
|
85
|
+
|---|---|
|
|
86
|
+
| No "MCP 管理" in Settings after install | Hard-refresh (Cmd/Ctrl+Shift+R); if still missing, restart DSH once. |
|
|
87
|
+
| **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. |
|
|
88
|
+
| 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. |
|
|
89
|
+
| `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`. |
|
|
90
|
+
| `npm view` reports 404 | Local mirror (npmmirror) sync lag: add `--registry=https://registry.npmjs.org` or wait a moment. |
|
|
91
|
+
| Config changes don't take effect | All changes apply via HMR within 1–2 s; the page auto-polls. |
|
|
115
92
|
|
|
116
93
|
</details>
|
|
117
94
|
|
|
118
95
|
<details>
|
|
119
|
-
<summary><b>
|
|
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).
|
|
96
|
+
<summary><b>Uninstall</b></summary>
|
|
122
97
|
|
|
123
98
|
```sh
|
|
124
|
-
|
|
125
|
-
# PowerShell: .\install.ps1 -Repair -Port 3080 bash: ./install.sh --repair --port 3080
|
|
99
|
+
dsh plugin --profile web remove @xxxyz/dsh-mcp-manager
|
|
126
100
|
```
|
|
127
101
|
|
|
128
|
-
|
|
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. |
|
|
102
|
+
Then restart DSH.
|
|
143
103
|
|
|
144
104
|
</details>
|
|
145
105
|
|
|
@@ -158,24 +118,23 @@ Configuration of the plugin itself on its loader row:
|
|
|
158
118
|
|
|
159
119
|
| Field | Description |
|
|
160
120
|
|---|---|
|
|
161
|
-
| `version` | The loader row's `config.version
|
|
121
|
+
| `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
122
|
|
|
163
123
|
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
124
|
|
|
165
125
|
```yaml
|
|
166
126
|
- insert:
|
|
167
127
|
- id: dsh-mcp-manager
|
|
168
|
-
name: dsh-mcp-manager
|
|
169
|
-
config:
|
|
170
|
-
version: 1
|
|
128
|
+
name: '@xxxyz/dsh-mcp-manager'
|
|
171
129
|
```
|
|
172
130
|
|
|
131
|
+
> You don't need to write this row manually — `dsh plugin add` inserts it automatically via the bundle patch (see `cordis.patch.yml`).
|
|
132
|
+
|
|
173
133
|
## 🏗️ Architecture
|
|
174
134
|
|
|
175
135
|
- **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
136
|
- **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**:
|
|
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`).
|
|
137
|
+
- **Loader row**: inserted automatically by `dsh plugin add`'s bundle patch; the client-modules service scans enabled entries and serves the client bundle.
|
|
179
138
|
|
|
180
139
|
## 🛠️ Development
|
|
181
140
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
# Bundle patch layer for
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
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
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
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 })
|