dsh-skills-mcp-manager
Manifest validDSH 插件:在设置面板里管理技能(SKILL.md)与 MCP 服务器,全局 / 项目级分层,改完即生效。Manage skills and MCP servers from the DSH settings panel.
[!IMPORTANT] 这是自有版本(0.6.0 起),不是任何第三方的发布版。 代码基于 MIT 许可的既有实现演进, 自 0.6.0 起包名统一为
dsh-skills-mcp-manager(无 scope)。 仓库:https://github.com/zx2021210538/dsh-skills-mcp-manager; npm:https://www.npmjs.com/package/dsh-skills-mcp-manager(由 GitHub Actions 的 OIDC Trusted Publishing 自动发布,带 provenance)。安装命令见下;「设置 → Skills & MCP → 获取技能 → 插件更新」的更新检查读本仓库的 Release。能力已扩展到与
@michengai/dsh-skills-manager对齐:外部 Agent 来源、逐技能/逐来源启停 (不改源文件)、分类、回收站、GitHub 仓库、在线市场(skills.sh / ClawHub)、导入、工作区 视图、侧栏入口。功能对照、模块地图、必须守住的安全边界、以及安装/迭代流程见 FORK.md; 逐版本改动见 CHANGELOG.md。
DSH 插件:把 Skills 与 MCP 的管理搬进 设置 → Skills & MCP。页面是「两个 tab × 两个层级」——MCP 与技能各自分「全局」和「项目级」;卡片、按钮、间距都对齐宿主内置的「内置插件」页,不 import 任何 Harness Client 包。
- 🗂️ 技能:列表 / 搜索 / 预览全文 / 新建 / 重命名 / 删除,落盘就是
<技能根>/<名字>/SKILL.md;删除先备份再删,可手动恢复 - 🔌 MCP:全局条目就是 profile
cordis.patch.yml里的真实 Loader 行,增删改停写回配置文件;项目级条目只挂到该项目里运行的 agent - 🎛️ 按服务器筛选工具:展开卡片就能看到这一行实际注册了哪些工具(名字 + 描述,来自 harness 自己的工具注册表),并按服务器选择「全部保留 / 只保留勾选 / 只挡勾选」。被挡下的工具不进工具注册表,所以也不占用每次组装工具面的 token——实测一个
chrome-devtools就有 30 个工具、约 25 KB schema - 🚦 连接状态圆点 + 一键重连:每张卡片用一个小圆点加一行小字说明状态 —— 🟢
可用 · N 个工具/ 🟡连接中 · 约 Ns/ 🔴已放弃重试/ 🔴异常/ 🟡需要授权/ ⚪未挂载,需要时在…菜单里点「重连」即可恢复——不用重启 DSH。它解决的是一个很坑的场景:DSH 启动时连不上的 MCP,等重试预算用完就永久失效(默认只等 151.5 秒),而以前界面上完全看不出来。「需要授权」是其中单列的一种:需要 OAuth 的服务器被拒绝连接不是故障(dsh-mcp-client没有 OAuth 支持,401 会被当成网络故障一路重试到放弃),所以它用黄色而不是红色,并说明补授权的办法 - 🎛 每行一个开关 +
…菜单:启用/停用是小号开关(同时表达当前档位),重连/编辑/删除收在竖向菜单里。停用的行不显示操作,重连只在启用且确实异常时出现 - 📥 MCP 导入:一键扫描 Claude Code / Cursor / VS Code / Gemini / Codex 等 26 个来源的用户级或项目级配置,预览后导入
- 🗂 一份列表,跨工作区:全局在前、然后按工作区顺序;卡片上的标签说明它属于谁(「全局」或工作区名)
- 🧯 不动坏你的配置:插件里绝不把异常抛出去(DSH 会因此隔离整个 profile 配置),写
cordis.patch.yml前自动备份(只留一个滚动.bak)、写前重新校验 - 📦 零运行时依赖:
react/yaml都用宿主已有的那份,插件本体不带第三方运行时依赖
目录:界面预览 · 安装 · 使用 · 工作原理 · 已知边界 · 更多文档 · License
界面预览
MCP 页:全局 / 项目级行、连接状态、逐行开关与 … 菜单。

工具过滤:展开卡片看到该服务器实际注册的工具,按「全部保留 / 只保留勾选 / 只挡勾选」收窄。

技能页:按来源分组、逐技能/逐来源启停;只在磁盘上存在的目录才出现,只读来源不显示写操作。

截图由本仓库构建出的客户端 bundle 离线渲染生成(同一套 stub 加载器 + 示例数据), 不是真机截图——真机截图会把作者自己的服务器地址与项目路径带进 README。重新生成:
node scripts/screenshots.mjs需要 headless Chrome、
SMP_PREVIEW_DIR(含 React 18 UMD)与SMP_THEME_CSS(--dsw-*token 列表) 三样夹具,缺任何一样都会打印缺什么并跳过(不会假装成功)。
安装
本插件装进 DSH 的某个 profile($DSH_HOME/profiles/<name>),Host 半在 harness 进程里跑。
从 npm(一条命令)
dsh plugin --profile web add dsh-skills-mcp-manager
桌面端:设置 → 插件 → 安装插件,搜 dsh-skills-mcp-manager 即可(插件市场收录前也可以直接从
npm 或 Releases
下载/安装)。
桌面端:设置 → 插件 → 安装本地包
从 Releases 下载
dsh-skills-mcp-manager-0.6.1.tgz,在 设置 → 插件 → 安装本地包 里选中它,然后重启 DSH。
desktopprofile 由桌面应用独占,宿主 CLI 的dsh plugin --profile desktop会被直接拒绝。
命令行(GitHub Release / 本地 tarball)
# 装 GitHub Release 里预构建的包:
dsh plugin --profile web add \
https://github.com/zx2021210538/dsh-skills-mcp-manager/releases/download/v0.6.1/dsh-skills-mcp-manager-0.6.1.tgz
# 或者用本机打出来的 tarball:
npm pack
dsh plugin --profile web add ./dsh-skills-mcp-manager-0.6.1.tgz
重启 DSH,再刷新页面:设置 → Skills & MCP。
升级:dsh plugin --profile <name> add dsh-skills-mcp-manager(或换新 Release 的 tgz 重装一次);
插件自己的「获取技能 → 插件更新」会读本仓库的 releases/latest 并提示有没有新版(只提示,不自动替换文件)。
卸载:
dsh plugin --profile web remove dsh-skills-mcp-manager
从源码(开发)
git clone https://github.com/zx2021210538/dsh-skills-mcp-manager.git
cd dsh-skills-mcp-manager
npm install
npm run build # dist/(Host,tsc)+ lib/client.js(Client,esbuild 单文件)
npm run deploy # 拷贝进 profile;部署前先跑加载冒烟,失败则 profile 一字不动
npm run deploy 的目标是 $DSH_HOME/profiles/desktop/node_modules/dsh-skills-mcp-manager,
旧版本备份成同级的 .backup-<时间戳>(回滚只需改名回来)。仓库不提交构建产物,所以克隆后必须先 build。
之后把包名挂进 profile 的 bundles 并重启:
// $DSH_HOME/profiles/desktop/package.json
{ "dsh": { "profile": { "bundles": [ /* … */ "dsh-skills-mcp-manager" ] } } }
使用
入口:设置 → Skills & MCP(与内置插件页同一套设置槽位)。
- 搜索只匹配名称,不匹配地址 / 参数 / 环境变量
- 一份列表,没有分区标题:全局的排前面,然后按工作区顺序;卡片上的标签说明它属于谁
- 「添加」按钮与搜索框同一行,表单紧贴列表上方(点完就能看到它要追加到的位置)
- 没有项目筛选器:列表跨工作区展示(全局在前,然后按工作区顺序),卡片上的标签说明归属。 新条目要写到哪个工作区,在添加对话框里选
- 卡片标签统一:全局显示「全局」,项目级显示工作区名 —— MCP 与 Skills 同一套词表
- 卡片操作收进
…菜单(预览 / 编辑 / 删除);技能没有启用开关——磁盘上有文件就是启用
MCP
| 全局 MCP | 项目级 MCP | |
|---|---|---|
| 存在哪 | profile cordis.patch.yml 的 Loader 行 | profile cordis.patch.yml 里带 scope 的 Loader 行 |
| 谁去连接 | 官方 @deepseek-ai/dsh-mcp-client 直接加载这些行 | 插件在 agent/created 时按项目路径挂载 |
| 生效范围 | 所有会话(含 subagent) | 只有该项目里运行的会话 agent |
| 增删改 | 写回配置文件(加锁 + 原子写 + 写前校验 + 自动备份) | 同一个行命令路由,写回带 scope 的行 |
-
卡片上的「配置文件」标签表示这一行来自
cordis.patch.yml;这种行可以编辑 / 停用 / 删除,改动通过带鉴权的POST /dsh-skills-mcp-manager/rows立即写回(新增/删除在点击瞬间就生效,不等轮询) -
展开卡片能看传输方式、地址或命令、参数、环境变量(值打码)、范围与启停状态
-
展开卡片的「工具」一栏可以只保留你需要的工具。每个服务器的工具清单下面有三档:
档位 含义 全部保留(默认) 不做任何干预,该服务器列出的工具全部注册 只保留勾选 勾选的才注册,没勾的不会进入工具注册表(也不占 token) 只挡勾选 勾选的被挡住,其余照常注册 被挡下的工具不只是「不显示」——它们从未注册,所以也不占用每次组装工具面的 JSON schema 体积。 实测本机 chrome-devtools 一个服务器就占 30 个工具、约 25 KB(≈5.8k tokens), 只留常用的几个收益很明显。
两点如实说明:
- 改动要等该服务器重新注册工具时才完全生效。规则本身是即时读取的(改完保存即可, 不用重启),但已经注册进注册表的工具无法被插件注销,所以已存在的工具不会当场消失。 想立刻看到效果:把这张卡片**「停用」再「启用」**(几秒即可)——停用会销毁该服务器的桥接、 连带注销它的工具,启用时重新连接并重新注册,于是全部工具重新过一遍过滤;或者重启 DSH。
- 过滤靠劫持工具服务的
register实现——因为dsh-mcp-client自身没有白名单字段, 而ctx.tools.restrict()要求 scoped 上下文(全局调用会抛错)、且只遮罩不注销。 这是唯一能真正把工具挡在注册表外的接缝。装不上时面板会显示原因,而不是假装在工作。
-
停用只是把该行的
enabled置 false,不删你的配置 -
连接与重连(添加/编辑 MCP 时的最后一组输入,全局与项目级都支持):直接对应
@deepseek-ai/dsh-mcp-client自己的配置字段表单里填的 落到 config 的 默认值 作用 连不上时拒绝激活 failOnStartupErrorfalse勾上后:首次连接或工具同步失败会让插件激活失败(不勾则照常启动,只是没有该服务器的工具) 工具调用超时(ms) toolCallTimeoutMs60000单次 tools/call的超时。导入/分析这类分钟级操作会被默认 60 秒打断(本机实测一次 Ghidra 导入+全量分析约 158 秒),按需调大重连首次延迟(ms) reconnect.initialDelayMs500第一次重连等待多久,之后逐次翻倍 重连最大延迟(ms) reconnect.maxDelayMs30000退避延迟的上限 重连最大次数 reconnect.maxAttempts10单次断线最多连续尝试几次,超出则放弃 输入框预填真实默认值,落库也写成显式值( 500 / 30000 / 10):表单显示的就是实际生效的,不用去翻 dsh-mcp-client的源码才知道这一行怎么重连。清空输入框会回到默认值,而不是删掉键。failOnStartupError例外——false与「键不存在」完全等价,写它只是噪音,所以只在勾选时写入。填了非法值(非整数、超范围)会被拒绝并提示——因为写进去会让整行加载失败 -
导入 MCP:选来源 → 扫描 → 预览「哪些文件存在、识别出几个、几个形态不支持」→ 确认。全局与项目级都写成配置文件行(项目行带
scope)
技能
- 列表 = 磁盘扫描结果:
$DSH_HOME/skills、~/.agents/skills、以及各项目的.agents/skills/.dsh/skills。磁盘文件是唯一真源,config.skills[]/ 对账镜像已删除(旧条目会在升级时一次性写成文件) - 「添加技能」写入
<根>/<名字>/SKILL.md:区域选全局或项目,名字同时是目录名 - 预览/编辑按需通过
GET /<name>/skill?path=读取正文(只允许读扫描到的文件;正文不进 profile patch) - 重命名会同时移动目录并改写 frontmatter 里的
name(否则面板和文件夹会对不上) - 删除是两步确认:先把目录备份到同级的
.smp-backup-<名字>-<毫秒>,再删除;备份会保留,需要时手动改回原名即可
状态与失败
在途操作在卡片上显示进度(保存中… / 删除中… / 切换中…),期间该卡片的其它按钮不可点;操作失败会就地显示原因(例如「该技能不在当前扫描结果里」)。删除若 15 秒没有回执,会停止转圈并提示重试。
工作原理
插件分两半,装在同一个包里:
src/** Host 半(tsc → dist/index.js) 跑在 harness 进程
lib/src/** Client 半(esbuild → lib/client.js) 跑在浏览器,单文件、CSS 内联
cordis.patch.yml 声明插件行(本包自己的 bundle patch)
写通道:所有修改都是 HTTP 请求/响应,没有 settings 队列,也没有持久化的命令字段。
| 路由 | 作用 |
|---|---|
POST /dsh-skills-mcp-manager/rows | MCP 行增删改;Host 加锁、校验、原子写 cordis.patch.yml,并在响应里带回写后的行列表 |
POST /dsh-skills-mcp-manager/skills | 技能增删改;直接写/删 <根>/<名字>/SKILL.md |
GET /dsh-skills-mcp-manager/skill?path= | 读取一个已被扫描到的 SKILL.md 正文(路径必须来自扫描结果) |
POST /dsh-skills-mcp-manager/reconnect | 重连一台服务器;请求返回时命令已经跑完 |
POST /dsh-skills-mcp-manager/import | 扫描其它工具的 MCP 配置,直接返回预览 |
写路由先过 connection.requestRejection() 鉴权,只接受 POST + application/json,请求串行执行。为什么不用 settings:写一个文件只要 1ms,而队列通道要连等四个轮询(600ms 取请求 + 面板回读 + 2s 对账 + 5s 投影),实测最坏约 9 秒。旧 Host / 旧面板不再兼容,请一起升级。
数据通道:GET /dsh-skills-mcp-manager/runtime 一次返回整个读模型:MCP 行列表(Host 直接读 patch 文件,Loader 只补 phase)、工具清单(现读工具注册表)、连接/授权状态(探测缓存 + 重连预算)、磁盘技能扫描、工作区与过滤状态。响应禁用缓存;面板在页签打开/展开卡片/页面恢复可见时读取,可见期间每 5 秒刷新,写入成功后立刻重拉一次。读取失败会显示原因并保留上一次的行列表,只把观测状态标成「状态未知」。
命名与升级:插件行 id、settings 命名空间和运行时接口前缀统一为 dsh-skills-mcp-manager。升级前请读 docs/MIGRATION-0.5.6.md —— 0.5.6 删掉了 settings 里的技能、命令与读模型字段:config.skills[] 迁移成 SKILL.md,config.servers[] 迁移成带 scope 的 Loader 行,其余键删除。迁移全部自动完成,但需要重启一次 DSH。
配置持久化:插件的 settings 里只剩 toolFilters(用户偏好,改过滤规则时才写)。技能、MCP 行、工具清单、连接/授权状态、命令都不再进 settings;启动时用配置文件锁、备份和原子写一次性清掉旧字段,之后扫描时间变化也不会再改写 cordis.patch.yml。
技能:面板写的、导入的、手工放进根目录的,全部只以磁盘文件为准;Host 每 5 秒扫描一次四个根供 /runtime 使用,不做任何镜像或回收。升级时 config.skills[] 的旧条目会写成 SKILL.md(已存在的文件优先,不覆盖)。
项目级 MCP:不猜项目根——用「配置里的 scope 是否为会话 cwd 的目录前缀」判定,命中的 agent 才挂载,agent/disposed 时卸载。注意 subagent 是独立 agent,只继承 root 作用域,所以项目级工具对 subagent 不可见(全局工具两处都可见)。
全局 MCP:对 cordis.patch.yml 的写入照抄官方 config-editor 的做法——同一把文件锁、原子写、写前重新 parseDocument 校验、每次改写前自动备份,块外的用户内容逐字节保留(注释、!!js 表达式都不会被写坏)。
备份只留一个文件:改写前把当前内容写到 $DSH_HOME/dsh-skills-mcp-manager.backups/cordis.patch.yml.bak,
每次覆盖。它保存的是「当前状态之前的那一份」,也就是撤销上一次操作——这也是它唯一的用途,
所以不留带时间戳的历史副本(早先那样做,一天就堆了 135 个文件、其中 130 个是逐字节重复)。
需要长期历史请用版本控制。
安全边界:DSH 的 sanitizeProfile 会在插件加载或通道处理抛出未捕获异常时,把用户整份 cordis.patch.yml 改名隔离并禁用其它插件。所以本插件的每个通道处理都包了双层 try/catch:宁可回一条失败原因,也不让异常逃出去。
工具过滤:dsh-mcp-client 会把每个连接到的服务器列出的全部工具无条件注册,而它的配置里没有任何白名单字段(stdio / streamable-http 两种 schema 都没有),ctx.tools.restrict() 也不能替代——它要求 scoped 上下文(全局调用会抛错),而且只遮罩定义、定义仍留在注册表里可被内省。唯一能把工具挡在注册表外的接缝是 register 本身,所以面板在 apply() 里劫持工具服务实例上的 register:
- 打在实例上而不是原型:按名字查找的调用方(
syncTools正是如此)只看得见实例上的写入;原型属于恰好被导入的那份包副本。 - 顺序上正好占优:本插件是 bundle 层,而 MCP 行在 profile 的
cordis.patch.yml(用户补丁层),profile 组装时先应用 bundle 补丁、再应用用户补丁——所以劫持一定在 MCP 客户端注册第一批工具之前装好,且这个顺序不依赖用户手工排行。真机上filterState.installed === true且preExisting为空,证实了这一点。 - fail-open:判不了名字就放行。过滤器自身的 bug 不该表现成「工具凭空消失」。
Reflect.set在不可写属性上会返回false且可能静默失败,所以写入结果是被验证的——否则会出现「报告安装成功、实际一个都没过滤」这种最坏情况。 - 规则每次注册时实时读取,所以改完保存即对新注册生效;未配置的服务器与非 MCP 的内置工具一律放行;归属按最长前缀判定(
github与github__enterprise并存时不会挂错)。
已知边界
- 面板列出磁盘上的技能(其它工具放进去的也在),但只对其中「本插件能定位到的根」执行写操作
- 项目级 MCP 对 subagent 不可见:subagent 只继承 root 作用域;需要所有 agent 都用就配成全局
- 工具过滤对「已注册」的工具无能为力:插件只能在注册那一刻放行或拦下,无法注销别的插件 已经注册的工具。所以刚开启的档位要等该服务器重新注册工具才完全见效——把该卡片「停用」再 「启用」即可立即触发,或重启 DSH
- 工具过滤依赖工具服务的
register可被写入:若某个版本的 DSH 把它设成不可写, 面板会显示「过滤未生效」并给出原因,而不是静默失效 - 过滤看的是公开工具名:MCP 客户端把工具注册为
mcp__<serverName>__<原始名>,名字过长时 会规范化并加哈希后缀——勾选界面里的短名就是按这个公开名切的,与服务器端的原始名可能不同 - stdio MCP 首次启动要等:
npx首次下载包约 1–2 分钟,这期间挂载已建立但工具还没注册 - MCP 连不上之后不会永远重试:
dsh-mcp-client的重试预算用完就永久放弃,只能重连或重启。 默认预算是 151.5 秒(initialDelayMs: 500、maxDelayMs: 30000、maxAttempts: 10的退避 之和)。想让它长时间自己等,就把「重连最大次数」设大(如10000)、「重连最大延迟」设60000;不要勾「连不上时拒绝激活」——勾上等于一次都不重试(首次尝试失败即让插件实例 失败并清掉重试定时器)。面板上的状态标签与「重连」按钮就是为这件事准备的 - 连接状态里的时间是推导值:
dsh-mcp-client不对外暴露重试计数(failedAttempts在闭包 里),所以「重试中 / 已放弃」是按该行配置算出的预算与已耗时推出来的,界面写「预计」 - 需要 OAuth 的 MCP 服务器会显示「需要授权」,而不是「坏了」:这类服务器在未授权时拒绝
连接是协议规定的正常行为。但
dsh-mcp-client完全没有 OAuth 支持(它不向 MCP SDK 传authProvider),于是 401 被当成普通网络故障一路重试到放弃,界面上只会说「已放弃重试」—— 把「你去补一次授权就好」说成了「这台服务器坏了」。面板会用一次轻量 HTTP 探测读 401/403 的WWW-Authenticate挑战,把它单独标成「需要授权」(不是红色,因为不是故障)。去授权的办法 只能靠凭据:给这一行加一个Authorization请求头,或换成服务器提供的带 token 的地址 (DSH 不会弹出 OAuth 登录窗——那是客户端缺的能力,不是面板能补的) command: npx走用户自己的 npm 缓存;~/.npm里若有 root 所有的文件会EACCES,一次性修复:sudo chown -R $(whoami):staff ~/.npm- 装/更新之后:Client 半刷新页面即可,Host 半的改动需要重启 DSH(安装器与市场都会提示)
更多文档
| 文档 | 给谁看 |
|---|---|
CHANGELOG.md | 每个版本改了什么 |
docs/MIGRATION-0.5.5.md | 从 0.5.4 升级的人:这一版的破坏性变更、自动迁移覆盖了什么、怎么回退 |
docs/DEVELOPMENT.md | 改这个插件的人:自测命令、诊断文件、故障恢复 |
docs/PUBLISHING.md | 维护者:npm 发包、GitHub Release、上架插件市场 |
docs/ICON.md | 改图标的人:插件的绘制规范(画布 / 尺寸 / 配色)与两个易踩的坑 |
docs/ENGINEERING.md | 开发手记(真机验证记录 / 事故复盘 / 踩坑清单,历史归档) |
docs/oauth.md | 用到 OAuth 保护的 MCP 服务器的人:怎么点授权、安全边界在哪、失败了怎么排查、怎么在本机验证整条链路 |
docs/oauth-feasibility.md | 想弄清 OAuth 为什么必须打补丁的人:每条结论的源码出处与实测证据(调研;0.5.6 已实现,见上一条) |
docs/tools-persistence.md | 工具清单为什么不该落盘:实测体积占比、每 5 秒重写的根因、三条可选路线 |
License
Comments
Loading…
Similar plugins
by zebbkira
面向 DeepSeek Harness Web GUI 的正式插件包:在设置页的「Web UI 插件」分组中新增一张「技能与 MCP」卡片,用于在浏览器里管理技能(skills)与 MCP 服务器。
★ 23
↓ 193/wk
TypeScript
Aug 23, 2026
dsh plugin --profile web add @zebbkira/dsh-skills-mcp-managerDSH 设置面板:一个页面管理技能(SKILL.md)与 MCP 服务器,全局 / 项目级分层,改完即生效。Manage skills and MCP servers from one DSH settings panel.
★ 2
↓ 318/wk
MIT
JavaScript
dsh plugin --profile web add @j62268781-alt/dsh-skills-mcp-managerby lilyblessing
MCP 与技能管理面板:设置页展示 MCP 服务器与 Skill 目录,随时启停释放上下文占用。
★ 8
↓ 152/wk
MIT
TypeScript
Oct 19, 2026
dsh plugin --profile web add dsh-mcp-skill-panelby functy23
DeepSeek Harness 的 MCP 服务与 Skills 管理器:面板里增删改 MCP 行、扫描导入其他 Agent 配置、管理 Skills(DSH Web 插件)
★ 30
↓ 338/wk
MIT
TypeScript
Oct 6, 2026
dsh plugin --profile web add dsh-mcp-studioby n0pe-sled
dsh plugin: manage dsh skills and MCP servers from Settings > Skills & MCP, hot-reloaded without restarting the GUI
★ 0
TypeScript
Oct 2, 2026
dsh plugin --profile web add dsh-skill-mcp-managerby w123456789zy
deepseek harness插件,在设置页面显示MCP和技能加载情况
★ 0
MIT
JavaScript
Aug 15, 2026
dsh plugin --profile web add dsh-settings-mcp-skills