dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
在 DSH 设置面板里一站式管理技能与 MCP 服务器。技能以磁盘为准:浏览、搜索、预览正文、新建、重命名、删除,删除前自动写一份备份。全局 MCP 就是 cordis.patch.yml 里的真实 Loader 行,项目级条目只挂到对应项目的会话。展开卡片会列出该服务器实际注册的工具,并可只保留需要的那些——被挡下的工具不进工具注册表,因此不占用每一轮的上下文。每张卡片带连接状态标签(已连接并显示工具数 / 重试中 / 已放弃重试 / 激活失败)与一键重连,让启动时连不上、工具悄悄消失的服务器看得见。另可从 26 个 MCP 客户端导入既有配置。
DSH 插件:把 Skills 与 MCP 的管理搬进 设置 → Skills & MCP。页面是「两个 tab × 两个层级」——MCP 与技能各自分「全局」和「项目级」;卡片、按钮、间距都对齐宿主内置的「内置插件」页,不 import 任何 Harness Client 包。
<技能根>/<名字>/SKILL.md;删除先备份再删,可手动恢复cordis.patch.yml 里的真实 Loader 行,增删改停写回配置文件;项目级条目只挂到该项目里运行的 agentchrome-devtools 就有 30 个工具、约 25 KB schema可用 · N 个工具 / 🟡连接中 · 约 Ns / 🔴已放弃重试 / 🔴异常 / 🟡需要授权 / ⚪未挂载,需要时在 … 菜单里点「重连」即可恢复——不用重启 DSH。它解决的是一个很坑的场景:DSH 启动时连不上的 MCP,等重试预算用完就永久失效(默认只等 151.5 秒),而以前界面上完全看不出来。「需要授权」是其中单列的一种:需要 OAuth 的服务器被拒绝连接不是故障(dsh-mcp-client 没有 OAuth 支持,401 会被当成网络故障一路重试到放弃),所以它用黄色而不是红色,并说明补授权的办法… 菜单:启用/停用是小号开关(同时表达当前档位),重连/编辑/删除收在竖向菜单里。停用的行不显示操作,重连只在启用且确实异常时出现cordis.patch.yml 前自动备份(只留一个滚动 .bak)、写前重新校验react / yaml 都用宿主已有的那份,插件本体不带第三方运行时依赖目录:界面预览 · 安装 · 使用 · 工作原理 · 已知边界 · 更多文档 · License
两张图都是用本仓库构建出的客户端 bundle 离线渲染的(同一套 stub 加载器 + 示例数据), 不是真机截图——真机截图会把作者自己的服务器地址与项目路径带进 README。 重新生成:
node scripts/screenshots.mjs。
MCP 面板 · 全局(配置文件行 + 停用/编辑/删除)· 项目级 · 导入 MCP
展开卡片 · 这一行注册了哪些工具 + 只保留勾选(未勾的淡化,且不会注册给模型)
技能面板 · 与 MCP 页签同一套布局 · 全局在前、按工作区排序 · 预览 / 编辑 / 删除 收在 … 菜单
本插件装进 DSH 的某个 profile($DSH_HOME/profiles/<name>),Host 半在 harness 进程里跑。
打开 设置 → 插件市场,搜 dsh-skills-mcp-manager,点安装,刷新页面即可。
桌面端请走市场或内置插件安装器:
desktopprofile 由桌面应用独占,宿主 CLI 的dsh plugin --profile desktop会被直接拒绝。
# npm 包(预构建,安装时不需要编译)
dsh plugin --profile web add @j62268781-alt/dsh-skills-mcp-manager
# 或 GitHub Release 上的预构建 tarball(链接恒定指向最新版)
dsh plugin --profile web add https://github.com/j62268781-alt/dsh-skills-mcp-manager/releases/latest/download/j62268781-alt-dsh-skills-mcp-manager.tgz
重启 DSH,再刷新页面:设置 → Skills & MCP。
卸载:
dsh plugin --profile web remove @j62268781-alt/dsh-skills-mcp-manager
git clone https://github.com/j62268781-alt/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/@j62268781-alt/dsh-skills-mcp-manager,
旧版本备份成同级的 .backup-<时间戳>(回滚只需改名回来)。仓库不提交构建产物,所以克隆后必须先 build。
之后把包名挂进 profile 的 bundles 并重启:
// $DSH_HOME/profiles/desktop/package.json
{ "dsh": { "profile": { "bundles": [ /* … */ "@j62268781-alt/dsh-skills-mcp-manager" ] } } }
入口:设置 → 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), 只留常用的几个收益很明显。
两点如实说明:
register 实现——因为 dsh-mcp-client 自身没有白名单字段,
而 ctx.tools.restrict() 要求 scoped 上下文(全局调用会抛错)、且只遮罩不注销。
这是唯一能真正把工具挡在注册表外的接缝。装不上时面板会显示原因,而不是假装在工作。停用只是把该行的 enabled 置 false,不删你的配置
连接与重连(添加/编辑 MCP 时的最后一组输入,全局与项目级都支持):直接对应 @deepseek-ai/dsh-mcp-client 自己的配置字段
| 表单里填的 | 落到 config 的 | 默认值 | 作用 |
|---|---|---|---|
| 连不上时拒绝激活 | failOnStartupError | false | 勾上后:首次连接或工具同步失败会让插件激活失败(不勾则照常启动,只是没有该服务器的工具) |
| 工具调用超时(ms) | toolCallTimeoutMs | 60000 | 单次 tools/call 的超时。导入/分析这类分钟级操作会被默认 60 秒打断(本机实测一次 Ghidra 导入+全量分析约 158 秒),按需调大 |
| 重连首次延迟(ms) | reconnect.initialDelayMs | 500 | 第一次重连等待多久,之后逐次翻倍 |
| 重连最大延迟(ms) | reconnect.maxDelayMs | 30000 | 退避延迟的上限 |
| 重连最大次数 | reconnect.maxAttempts | 10 | 单次断线最多连续尝试几次,超出则放弃 |
输入框预填真实默认值,落库也写成显式值(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)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 正是如此)只看得见实例上的写入;原型属于恰好被导入的那份包副本。cordis.patch.yml(用户补丁层),profile 组装时先应用 bundle 补丁、再应用用户补丁——所以劫持一定在 MCP 客户端注册第一批工具之前装好,且这个顺序不依赖用户手工排行。真机上 filterState.installed === true 且 preExisting 为空,证实了这一点。Reflect.set 在不可写属性上会返回 false 且可能静默失败,所以写入结果是被验证的——否则会出现「报告安装成功、实际一个都没过滤」这种最坏情况。github 与 github__enterprise 并存时不会挂错)。register 可被写入:若某个版本的 DSH 把它设成不可写,
面板会显示「过滤未生效」并给出原因,而不是静默失效mcp__<serverName>__<原始名>,名字过长时
会规范化并加哈希后缀——勾选界面里的短名就是按这个公开名切的,与服务器端的原始名可能不同npx 首次下载包约 1–2 分钟,这期间挂载已建立但工具还没注册dsh-mcp-client 的重试预算用完就永久放弃,只能重连或重启。
默认预算是 151.5 秒(initialDelayMs: 500、maxDelayMs: 30000、maxAttempts: 10 的退避
之和)。想让它长时间自己等,就把「重连最大次数」设大(如 10000)、「重连最大延迟」设
60000;不要勾「连不上时拒绝激活」——勾上等于一次都不重试(首次尝试失败即让插件实例
失败并清掉重试定时器)。面板上的状态标签与「重连」按钮就是为这件事准备的dsh-mcp-client 不对外暴露重试计数(failedAttempts 在闭包
里),所以「重试中 / 已放弃」是按该行配置算出的预算与已耗时推出来的,界面写「预计」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| 文档 | 给谁看 |
|---|---|
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 秒重写的根因、三条可选路线 |