dsh-plugin-tool-management 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 god2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # dsh-plugin-tool-management
2
+
3
+ [![npm version](https://img.shields.io/npm/v/dsh-plugin-tool-management?logo=npm&color=cb3837)](https://www.npmjs.com/package/dsh-plugin-tool-management)
4
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
6
+ [![GitHub](https://img.shields.io/badge/GitHub-ouli--1242%2Fdsh--plugin--tool--management-181717?logo=github)](https://github.com/ouli-1242/dsh-plugin-tool-management)
7
+
8
+ **DeepSeek Harness 的 MCP 服务与技能管理插件。** 一个设置面板同时管好两件事:
9
+
10
+ - **MCP**:连接了哪些服务、每个服务有哪些工具、哪些工具该让模型用——增删改查、启停、重启,全部即改即生效;
11
+ - **Skills**:本机各处的技能(DSH / Agents / Codex / Claude / 项目级 / 你自己指定的任意目录)一目了然,逐个或整组启停、创建、导入、回收。
12
+
13
+ 不手改 `cordis.patch.yml`,不碰任何技能源文件,重启与升级后配置依旧。
14
+
15
+ ---
16
+
17
+ <!-- 图片占位 1:MCP 管理页截图 → docs/images/mcp-page.png -->
18
+
19
+ ![MCP 管理](docs/images/mcp-page.png)
20
+
21
+ <!-- 图片占位 2:Skills 管理页截图 → docs/images/skills-page.png -->
22
+
23
+ ![Skills 管理](docs/images/skills-page.png)
24
+
25
+ ## 核心亮点
26
+
27
+ | 能力 | 说明 |
28
+ |---|---|
29
+ | 工具级开关 | MCP 服务器内的**单个工具可独立启停**:模型看不见也调不到,随时恢复;整台服务器还支持批量启停 |
30
+ | 重启语义 | 重启只重连、**不改变启停状态**(对已停用的服务执行重启不会意外启用它) |
31
+ | 密钥安全 | `env` / `headers` 中的密钥**默认打码**、URL 查询串遮蔽;查看明文与所有写操作一样受 token 保护 |
32
+ | 写入保护 | 每次改写补丁前自动留 `.bak` 时间戳备份(保留 5 份);重复 loader id 写前拦截、跨级迁移失败自动回滚 |
33
+ | 备份恢复 | JSON 导入支持 `overwrite` 覆盖同 id 条目,不再只能跳过 |
34
+ | 技能来源 | 接入 `~/.agents` / `~/.codex` / `~/.claude` 三个官方不加载的技能目录,并支持**自定义任意技能目录**(只读接入、重叠拒绝) |
35
+ | 技能操作 | 创建技能、ZIP/文件夹导入、插件回收站(恢复 / 永久删除 / 系统回收站兜底)、系统编辑器打开源文件 |
36
+ | 即时刷新 | 技能目录由后台线程监听,编辑器里改完技能页面自动刷新 |
37
+ | 斜杠命令 | 聊天框直接输入 `/mcp`、`/skills` 查看状态 |
38
+ | 模型工具 | **7 个**:`skill_mcp_manager_*` 管 MCP,`skill_manager_*` 管技能(创建前需用户确认) |
39
+ | 界面 | 独立的 `dsm-*` 设计系统,两页风格统一 |
40
+
41
+ ## 快速开始
42
+
43
+ 前置:已装好 DSH(`dsh web` 可运行),Node.js ≥ 18。
44
+
45
+ ```sh
46
+ # 安装(装包 + 自动挂载)
47
+ dsh plugin --profile web add dsh-plugin-tool-management@latest
48
+
49
+ # 更新:重复执行同一命令
50
+ # 卸载:
51
+ dsh plugin --profile web remove dsh-plugin-tool-management
52
+ ```
53
+
54
+ 装完硬刷新浏览器(Cmd/Ctrl+Shift-R),设置里出现 **MCP** 与 **Skills** 两页即安装成功(客户端改动由 DSH 热加载,无需重启)。
55
+
56
+ 也可以直接对任意 DSH 会话说:
57
+
58
+ ```text
59
+ 安装 dsh-plugin-tool-management 插件:
60
+ dsh plugin --profile web add dsh-plugin-tool-management@latest
61
+ 装完提醒我硬刷新浏览器。
62
+ ```
63
+
64
+ ## 功能指南
65
+
66
+ ### 管 MCP 服务
67
+
68
+ - **接入一个服务**:「新增服务」填 `serverName`(1–32 位 `[A-Za-z0-9_-]`,全局唯一)、传输方式与对应字段,选项目级或全局。写入的是 `cordis.patch.yml` 的 loader 行,HMR 自动生效。
69
+ - **看清现状**:每张卡片实时显示启停状态、loader 加载阶段与已注册工具数;页面顶部是统计卡,重复 loader id 这类会导致 DSH 起不来的问题会直接告警。
70
+ - **只关掉某个工具**:「详情」弹窗里逐个停用工具——比如模型总是乱调的搜索工具,停掉后它的 schema 从模型视野消失、调用也会被拦截,随时可恢复。
71
+ - **换密钥不泄露**:默认所有形似密钥的值显示为 `••••••`,排查问题时再点「显示密钥」。
72
+ - **迁移与备份**:编辑可改 serverName 甚至跨项目级/全局迁移(失败自动回滚);JSON 导出/导入用于整份备份与换机。
73
+
74
+ ### 管技能
75
+
76
+ - **看全貌**:按来源分组列出所有技能——项目级、运行时、内置、插件自带,以及四个用户目录(`~/.dsh` / `~/.agents` / `~/.codex` / `~/.claude`,后三个由本插件接入)和你自己添加的自定义目录。
77
+ - **启停**:单个技能、整个来源、整个项目,随时切换;实现是 override provider 的遮蔽策略,源文件一个字节都不动,换机或重装只要复制状态文件。
78
+ - **自定义目录**:点「添加目录」输入绝对路径,该目录即成为只读技能来源——适合管理散落在仓库、网盘同步目录里的技能合集;与已有来源重叠的路径会被拒绝,避免遮蔽失效。
79
+ - **创建与导入**:表单直接创建;ZIP、`.md`、技能文件夹拖进来就能装;删除先进回收站,可恢复,永久删除前还会尝试移入系统回收站兜底。
80
+
81
+ ### 让模型和脚本参与管理
82
+
83
+ | 入口 | 能做什么 |
84
+ |---|---|
85
+ | `/mcp`、`/skills` | 聊天框查看当前状态 |
86
+ | `skill_mcp_manager_list / set_enabled / restart / add` | 模型查询与操作 MCP 服务 |
87
+ | `skill_manager_list / set_enabled / create` | 模型查询与操作技能(创建前会征求你同意) |
88
+ | `POST /dsh-plugin-tool-management/api` | 脚本调用的 HTTP API(`{op, args}` 协议) |
89
+
90
+ ## 配置与安全
91
+
92
+ 插件 loader 行支持以下可选字段(`dsh plugin add` 会自动插入,一般无需手写):
93
+
94
+ | 字段 | 说明 |
95
+ |---|---|
96
+ | `token` | 可选访问令牌。设置后**所有写操作与「显示密钥」**都要求 `x-dsh-token` 请求头。客户端从 localStorage 读取(键 `dsh-plugin-tool-management-token`,DevTools Console 设置后刷新即可),也可用环境变量 `DSH_PLUGIN_TOOL_MANAGEMENT_TOKEN`。 |
97
+ | `maxBodyBytes` | 请求体上限,默认 88 MiB(技能 ZIP 上传需要)。 |
98
+
99
+ 关于为什么需要 token:插件的跨站防护(POST-only + 自定义头 + 同源校验)默认「DSH 只监听本机」。如果你把端口转发到局域网/公网,token 就是防止陌生人注入 MCP 命令(等同远程执行)与窃取明文密钥的最后防线——本地单机使用可不配置。
100
+
101
+ ## 数据落点
102
+
103
+ | 内容 | 位置 |
104
+ |---|---|
105
+ | MCP 服务器定义 | `profiles/<profile>/cordis.patch.yml`(项目级)或 `~/.dsh/cordis.patch.yml`(全局),改写前自动 `.bak` |
106
+ | 服务器备注 / 页面设置 / 工具停用列表 / 导出 | DSH 主目录下的旁路 JSON(`skill-mcp-manager-*.json`) |
107
+ | 技能启停策略 / 自定义目录 | `~/.dsh/skill-mcp-manager/state.json` |
108
+ | 技能回收站 / 导入暂存 | `~/.dsh/skill-mcp-manager/trash`、`uploads` |
109
+ | 运行日志 | `~/.dsh/dsh-plugin-tool-management.log`(滚动) |
110
+
111
+ ## 常见问题
112
+
113
+ | 现象 | 解决 |
114
+ |---|---|
115
+ | 装完设置里没有页面 | 硬刷新;不行就重启 DSH。 |
116
+ | 出现重复的 MCP 页签 / 工具 | 与旧 loader 行双挂载,删掉 `cordis.patch.yml` 里的旧条目后重启。 |
117
+ | 改坏了配置 DSH 起不来 | 同目录取最近的 `cordis.patch.yml.bak-<时间戳>` 恢复。 |
118
+ | 页面数据不刷新 | 等待页面自动轮询(默认 5 秒);或手动点「刷新」。 |
119
+ | 镜像源装不到最新版 | 加 `--registry=https://registry.npmjs.org` 稍后再试。 |
120
+
121
+ ## 开发
122
+
123
+ ```bash
124
+ npm install
125
+ npm test # 构建 + 全量测试(node:test,约 1 秒)
126
+ npm run test:fast # 跳过构建直接跑测试
127
+ npm run build # 仅构建(tsc + 同步客户端 bundle)
128
+ ```
129
+
130
+ 结构:宿主端 `src/index.ts`(Cordis 对象插件,`lib/index.js` 为发布产物);技能核心 `src/skills/core.js`(纯 Node,可独立单测);浏览器端 `src/client.js`(ModuleLoader CJS bundle,`dsm-*` 设计系统,经同源 API 与宿主通信)。运行时依赖仅 `fflate`(ZIP 解压)。
131
+
132
+ 发布:`npm version patch && npm publish`(`prepublishOnly` 自动构建)。
133
+
134
+ ## 许可证
135
+
136
+ MIT
package/README_EN.md ADDED
@@ -0,0 +1,136 @@
1
+ # dsh-plugin-tool-management
2
+
3
+ [![npm version](https://img.shields.io/npm/v/dsh-plugin-tool-management?logo=npm&color=cb3837)](https://www.npmjs.com/package/dsh-plugin-tool-management)
4
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js)](package.json)
6
+ [![GitHub](https://img.shields.io/badge/GitHub-ouli--1242%2Fdsh--plugin--tool--management-181717?logo=github)](https://github.com/ouli-1242/dsh-plugin-tool-management)
7
+
8
+ **An MCP server & skills manager for DeepSeek Harness.** One settings panel keeps two things under control:
9
+
10
+ - **MCP**: which servers are configured, what tools each one exposes, and which tools the model may call — add, edit, remove, toggle, restart; every change takes effect immediately;
11
+ - **Skills**: every skill on the machine (DSH / Agents / Codex / Claude / project-level / any directory you add) at a glance — toggle individually or per source, create, import, recycle.
12
+
13
+ No hand-editing of `cordis.patch.yml`, and skill source files are never touched. Configuration survives restarts and upgrades.
14
+
15
+ ---
16
+
17
+ <!-- Image slot 1: MCP management page screenshot → docs/images/mcp-page.png -->
18
+
19
+ ![MCP management](docs/images/mcp-page.png)
20
+
21
+ <!-- Image slot 2: Skills management page screenshot → docs/images/skills-page.png -->
22
+
23
+ ![Skills management](docs/images/skills-page.png)
24
+
25
+ ## Highlights
26
+
27
+ | Capability | Description |
28
+ |---|---|
29
+ | Per-tool switches | **Toggle individual tools** inside one MCP server: hidden from the model and blocked at call time, restorable at any moment; whole-server batch enable/disable also supported |
30
+ | Restart semantics | Restart only reconnects — it **never flips the enabled state** (restarting a disabled server does not silently enable it) |
31
+ | Secret safety | Secret-looking values in `env` / `headers` are **masked by default**, URL query strings are always redacted; revealing plaintext is token-gated just like writes |
32
+ | Write protection | Every patch rewrite keeps a timestamped `.bak` backup (last 5); duplicate loader ids are rejected before write; failed cross-level migration rolls back |
33
+ | Backup / restore | JSON import supports `conflict: 'overwrite'` to replace entries with the same id, not just skip them |
34
+ | Skill sources | Hooks up `~/.agents` / `~/.codex` / `~/.claude` (three directories official DSH does not load) plus **any custom skill directory** you add (read-only, overlapping paths rejected) |
35
+ | Skill operations | Create skills, import ZIP / folders, plugin recycle bin (restore / permanent delete with OS-trash fallback), open the source file in the system editor |
36
+ | Live refresh | Skill directories are watched from a background thread — edits made in an editor show up automatically |
37
+ | Slash commands | `/mcp` and `/skills` right from the chat box |
38
+ | Model tools | **7 tools**: `skill_mcp_manager_*` for MCP servers, `skill_manager_*` for skills (creating asks for user confirmation first) |
39
+ | UI | Its own `dsm-*` design system, consistent across both pages |
40
+
41
+ ## Getting started
42
+
43
+ Prerequisites: DSH installed (`dsh web` runs), Node.js ≥ 18.
44
+
45
+ ```sh
46
+ # Install (package + auto-mount)
47
+ dsh plugin --profile web add dsh-plugin-tool-management@latest
48
+
49
+ # Update: run the same command again
50
+ # Uninstall:
51
+ dsh plugin --profile web remove dsh-plugin-tool-management
52
+ ```
53
+
54
+ Hard-refresh the browser (Cmd/Ctrl+Shift-R) after installing — the **MCP** and **Skills** pages appear in Settings (client changes are hot-loaded by DSH, no restart needed).
55
+
56
+ You can also tell any DSH session:
57
+
58
+ ```text
59
+ Install the dsh-plugin-tool-management plugin:
60
+ dsh plugin --profile web add dsh-plugin-tool-management@latest
61
+ Then remind me to hard-refresh the browser.
62
+ ```
63
+
64
+ ## Feature guide
65
+
66
+ ### Managing MCP servers
67
+
68
+ - **Add a server**: fill in `serverName` (unique, 1–32 chars `[A-Za-z0-9_-]`), the transport and its fields (`streamable-http` → URL / headers; `stdio` → command / args / env), and choose project or global level. The write lands as a loader row in `cordis.patch.yml` and applies via HMR.
69
+ - **See the state**: every card shows live status, loader phase and registered tool count; a summary bar sits on top, and fatal issues such as duplicate loader ids are flagged right on the page.
70
+ - **Turn off just one tool**: the "Details" dialog lists every tool with its parameter summary — disable the ones the model keeps misusing; the schema disappears from the model's view and calls are denied, ready to re-enable anytime.
71
+ - **Inspect secrets safely**: secret-looking values render as `••••••` by default; click "Reveal" only when you need them.
72
+ - **Move and back up**: editing can rename a server or migrate it between project/global level (with automatic rollback on failure); JSON export/import covers full backups.
73
+
74
+ ### Managing skills
75
+
76
+ - **See everything**: skills are grouped by source — project, runtime, built-in, plugin-shipped, the four user directories (`~/.dsh` / `~/.agents` / `~/.codex` / `~/.claude`; the last three are hooked up by this plugin) and any custom directories you added.
77
+ - **Toggle**: individual skills, whole sources or whole projects — implemented as an override-provider shadow policy, so not a single byte of the source file changes; moving machines is just copying the state file.
78
+ - **Custom directories**: click "Add directory", enter an absolute path, and that directory becomes a read-only skill source — ideal for skill collections living in repos or synced folders; overlapping paths are rejected to keep the shadow policy sound.
79
+ - **Create / import / recycle**: create from a form; drag in a ZIP, a `.md` file or a skill folder; deleted skills go to the plugin recycle bin first, and permanent delete still tries the OS trash as a last safety net.
80
+
81
+ ### Let the model and scripts help
82
+
83
+ | Entry point | What it does |
84
+ |---|---|
85
+ | `/mcp`, `/skills` | Check the current state from the chat box |
86
+ | `skill_mcp_manager_list / set_enabled / restart / add` | Let the model query and operate MCP servers |
87
+ | `skill_manager_list / set_enabled / create` | Let the model query and operate skills (creating asks for your consent) |
88
+ | `POST /dsh-plugin-tool-management/api` | HTTP API for scripts (`{op, args}` protocol) |
89
+
90
+ ## Configuration & security
91
+
92
+ Optional fields on the plugin loader row (`dsh plugin add` inserts it automatically):
93
+
94
+ | Field | Description |
95
+ |---|---|
96
+ | `token` | Optional access token. When set, **every write operation and "Reveal"** requires the `x-dsh-token` header. The client reads it from localStorage (key `dsh-plugin-tool-management-token`; set it in the DevTools console and refresh), or via the `DSH_PLUGIN_TOOL_MANAGEMENT_TOKEN` environment variable. |
97
+ | `maxBodyBytes` | Request body cap, default 88 MiB (skill ZIP uploads need it). |
98
+
99
+ Why a token: the cross-site protection (POST-only + custom header + same-origin check) assumes DSH listens on localhost only. If you forward the port to a LAN or the public internet, the token is the last line of defense against strangers injecting MCP commands (equivalent to remote code execution) and reading plaintext secrets — not needed for local single-user setups.
100
+
101
+ ## Where data lives
102
+
103
+ | Content | Location |
104
+ |---|---|
105
+ | MCP server definitions | `profiles/<profile>/cordis.patch.yml` (project) or `~/.dsh/cordis.patch.yml` (global), auto-`.bak` before every rewrite |
106
+ | Server notes / page settings / disabled tools / export | Sidecar JSON files under the DSH home (`dsh-plugin-tool-management-*.json`) |
107
+ | Skill toggle policy / custom directories | `~/.dsh/dsh-plugin-tool-management/state.json` |
108
+ | Skill recycle bin / import staging | `~/.dsh/dsh-plugin-tool-management/trash`, `uploads` |
109
+ | Runtime log | `~/.dsh/dsh-plugin-tool-management.log` (rolling) |
110
+
111
+ ## FAQ
112
+
113
+ | Symptom | Fix |
114
+ |---|---|
115
+ | Pages missing in Settings after install | Hard refresh; if that fails, restart DSH once. |
116
+ | Duplicate MCP tabs / duplicated tools | Stale loader row double-mounting the plugin — remove the old entry from `cordis.patch.yml` and restart. |
117
+ | Broken config, DSH won't boot | Restore the newest `cordis.patch.yml.bak-<timestamp>` next to it. |
118
+ | Page data not refreshing | Wait for the automatic polling (default 5s) or click "Refresh". |
119
+ | Latest version not found on a mirror | Add `--registry=https://registry.npmjs.org` and retry later. |
120
+
121
+ ## Development
122
+
123
+ ```bash
124
+ npm install
125
+ npm test # build + full test suite (node:test, ~1s)
126
+ npm run test:fast # run tests without building
127
+ npm run build # build only (tsc + sync client bundle)
128
+ ```
129
+
130
+ Layout: host half `src/index.ts` (object-form Cordis plugin, `lib/index.js` is the shipped artifact); skill core `src/skills/core.js` (pure Node, unit-testable); browser half `src/client.js` (ModuleLoader CJS bundle, `dsm-*` design system, talks to the host through the same-origin API). The only runtime dependency is `fflate` (ZIP extraction).
131
+
132
+ Publish: `npm version patch && npm publish` (`prepublishOnly` builds automatically).
133
+
134
+ ## License
135
+
136
+ MIT
@@ -0,0 +1,31 @@
1
+ # Bundle patch layer for the official CLI install:
2
+ #
3
+ # dsh plugin --profile web add dsh-plugin-tool-management@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
+ # `dsh-plugin-tool-management` 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: an aggregate bundle (or a hand-written loader row) may
12
+ # already mount this plugin under a different entry id. Two mounts both register the
13
+ # /dsh-plugin-tool-management/api route and fail the whole plugin tree at boot
14
+ # ("duplicate prefix route"). The `!!js` disabled expression backs THIS row
15
+ # off when another *enabled* entry already mounts the plugin; the existing
16
+ # instance then owns the API. The loader evaluates the expression at entry
17
+ # activation — the full entry tree is composed by then, so user-layer
18
+ # (profile cordis.patch.yml) rows are visible too.
19
+ #
20
+ # ORDER MATTERS — do not move `!e.disabled` ahead of the id/name checks.
21
+ # `Entry.disabled` is an uncached getter that re-evaluates the expression,
22
+ # so touching `e.disabled` of THIS entry re-enters this very expression
23
+ # (infinite recursion → "Maximum call stack size exceeded" at boot). The
24
+ # `id !==` check short-circuits on our own row and the `name` check
25
+ # short-circuits on every unrelated entry (e.g. dsh-better-sidebar's own
26
+ # guard), so `!e.disabled` is only ever read on a genuinely matching
27
+ # legacy/aggregate row (a plain boolean, no re-entry).
28
+ - insert:
29
+ - id: dsh-plugin-tool-management
30
+ name: 'dsh-plugin-tool-management'
31
+ disabled: !!js "[...ctx.loader.entries()].some((e) => e.options.id !== 'dsh-plugin-tool-management' && e.options.name === 'dsh-plugin-tool-management' && !e.disabled)"