dsh-skill-mcp-panel
在 DSH 侧边栏管理「技能」与「MCP」服务器,并附统一终端命令 dsh-panel
简体中文 · English
DSH 插件:把技能与 MCP 服务器的管理搬进 DSH Web 的主页侧边栏——点左栏的「Skill / MCP」,中央主区直接切到整页面板(不是弹窗,也不是设置页里的 tab),改完即生效。随包附带统一终端命令 dsh-panel:面板能做的事命令行都能做,另外多一个跨 profile 升级。
- 🗂️ 技能管理 —— 列表预览、搜索、工作区分栏与分组筛选、卡片展开查看全文、热启停 / 删除,支持
.md / .zip / 技能文件夹的添加与批量迁移
- 🔌 MCP 服务器管理 —— 接管 profile
cordis.patch.yml 里的每一条 MCP 服务器(表单 + 原文),Stdio / HTTP 两种调用方式,支持测试连接,保存后由 DSH HMR 热加载
- 🧩 整页面板 —— 与宿主内置「插件」页同一套槽位机制,点左栏即在中央主区切页,页面左上角另有「← 返回会话」
- ⌨️
dsh-panel 命令行 —— dsh-panel skill … 与 dsh-panel mcp … 覆盖两个面板的全部能力
- 📦 装上就能用 —— npm 与 Release tarball 安装的都是预构建产物
profile 提示:mcp 子命令必须显式写 --profile <name>,且名字必须已存在——打错会被拒绝,不会新建 profile。skill 不按 profile 分家(技能按用户根 / 工作区存放),不需要该参数。不带 --profile 的 dsh-panel update 升级全部已安装本插件的 profile(desktop 由桌面应用独占,自动跳过)。
目录:界面预览 · 安装 · 功能 · 命令行 · 工作原理 · 开发 · 卸载 · 更新日志 · 链接 · License
界面预览
面板入口在主页左侧栏「插件」下方(v2.1.0 起由设置页迁移到侧边栏),点「技能」/「MCP」即在中央主区打开;两个页面左上角都有「← 返回会话」,可直接回到进面板前那个会话。
DSH 主页侧边栏:插件 / Skill / MCP
入口 · 主页侧边栏中的「Skill / MCP」(v2.1.5 起两个面板合并为一行,标签固定用英文写法)
技能面板(三栏布局):工作区 / 分组 / 内容三栏,搜索、全部启停、显示插件技能开关与技能卡片
技能面板 · 三栏布局(工作区 / 分组 / 内容)· 搜索 / 全部启停 / 显示插件技能开关
技能面板(简洁布局):收起左右两栏,工作区折叠成下拉、分组横排 chip、一行两个技能
技能面板 · 简洁布局(工具栏最右按钮切换)· 收起左右两栏 / 工作区折叠下拉 / 一行两个技能
分组编辑器:新建分组、选择工作区并批量勾选成员
技能分组 · 新建 / 重命名 / 批量勾选成员
批量迁移技能:源工作区、多选目标工作区、复制或移动
批量迁移 · 源工作区 / 多选目标 / 复制或移动
MCP 面板:注册机制说明、服务器卡片、工具数量、启停、测试连接与删除
MCP 面板 · 注册机制说明 / 服务器卡片 / 工具数 / 启停 / 测试连接
添加 MCP 服务器:STDIO 与 HTTP 两种调用方式
添加 MCP 服务器 · STDIO / HTTP 两种调用方式
安装
-
安装本包,二选一(桌面端用户请在内置插件安装器中输入'...add'后面的内容):
方式一:GitHub Release tarball(推荐,恒定指向最新版)
dsh plugin --profile web add https://github.com/Fishquito7/dsh-skill-mcp-panel/releases/latest/download/dsh-skill-mcp-panel.tgz
这是无版本号的稳定链接:每次发版都同时上传别名 dsh-skill-mcp-panel.tgz(恒定指向最新版)与带版本号的
dsh-skill-mcp-panel-<版本>.tgz,所以这条命令不随版本改。需要锁定具体版本时,把 latest/download/dsh-skill-mcp-panel.tgz
换成 download/v<版本>/dsh-skill-mcp-panel-<版本>.tgz 即可。
方式二:npm(预构建,插件市场同款通道)
dsh plugin --profile web add dsh-skill-mcp-panel
两种方式都安装预构建产物,无需本地构建。也可以从 Git 安装(Git 来源的依赖默认禁止运行 prepare 构建脚本;若报
git-hosted plugins build on install...,把 pnpm 在上面打印的 key 加到 profile 目录 pnpm-workspace.yaml
的 allowBuilds 下再重跑):
dsh plugin --profile web add github:Fishquito7/dsh-skill-mcp-panel
-
重启网关
重启后刷新页面:左侧栏「插件」下面就是合并后的「Skill / MCP」一行——点它把中央主区切成面板,面板内部再用页签切换「技能」与「MCP」。
功能
面板和宿主自带的「插件」页是同一套机制:左栏一行「Skill / MCP」,点它把中央主区切过去——不是设置页的 tab,也不是弹窗。面板内用页签切换「技能」与「MCP」,并记住你上次停在哪一页;左上角「← 返回会话」随时退回进面板前的会话。
技能面板
- 列表与卡片 —— 按名称搜索,点开卡片看全文;按工作区(全局 / 各工作区)分栏,可按分组或按来源筛选
- 启停与删除 —— 开关热生效(改的是磁盘上的
SKILL.md),删除可撤销;插件自带的技能可一键隐藏
- 添加 —— 点「+」或直接把文件 / 压缩包 / 技能文件夹拖进页面:
.md、.zip、整个文件夹都认,含多个技能的文件夹一次导入;不合法、目标作用域内重名的逐条跳过并说明原因
- 迁移 —— 批量复制或移动到多个工作区,单条失败不影响其余
- 分组 —— 新建 / 重命名 / 删除、批量勾选成员;只写插件自己的显示配置,不动技能目录
MCP 面板
- 服务器管理 —— 支持 Stdio 与 HTTP 两种方式:新增、编辑、启停、删除、测试连接;保存后由 DSH HMR 热加载,不用重启网关。文件里每一条
@deepseek-ai/dsh-mcp-client 都能操作(不区分受管块内外,也不看 id 前缀)。表单改常规项并保留还没做成表单的字段;原文按字节改这一条。非 MCP 的行(含块内别人的条目)仍然逐字节保留
- 密钥 ——
env / headers 在页面与 RPC 里脱敏,编辑时留空即保留旧值;值里写 ${NAME} 就是读该环境变量
- 单工具禁用 —— 按工具名关掉其中一部分,被禁的工具会从模型看到的工具表里消失(不是调用时才拒绝),上下文立刻省下来
- OAuth 登录 —— 远程
streamable-http 服务器支持时一键登录(按 RFC 8414 / OIDC / RFC 9728 逐级发现授权服务器,兼容带路径端点;动态注册客户端、授权码 + PKCE + RFC 8707 resource),令牌写回该行配置并热加载;可随时断开
DSH 版本兼容
只支持 DSH 0.2.x(peer 声明 >=0.2.0-rc.1 <0.3.0-0)。DSH 会在安装时和 profile 启动时校验这条声明,不满足就拒绝加载并打印精确豁免命令——不会静默崩。上界写成 <0.3.0-0 是因为该校验带 includePrerelease,<0.3.0 会把 0.3.0-rc.1 也放进来。
确需在老宿主上使用,用宿主打印的精确版本豁免:
dsh plugin --profile web allow-version dsh-skill-mcp-panel@2.3.7 --dsh-version <宿主版本> --accept-risk
命令行
统一父命令为 dsh-panel。
全局 shim:宿主启动时会在 npm 全局 bin 目录写入 dsh-panel。Windows 下写三份——dsh-panel.cmd(CMD)、dsh-panel.ps1(PowerShell)、以及无扩展名的 dsh-panel sh 脚本(Git Bash / MSYS2 / Cygwin 靠它,v2.1.5 之前缺这一份,bash 里会报 command not found);macOS / Linux 写一份可执行的 dsh-panel。三份都指向「最后启动的那个 profile」装的插件副本。
mcp 子命令必须用 --profile <name> 指定目标 profile,名字必须已存在(判据是
$DSH_HOME/profiles/<name>/package.json)。打错名字会以退出码 2 被拒绝,不会像 dsh plugin
那样顺手新建一个 profile。skill 子命令不需要 --profile——技能按用户根 / 工作区存放,不按
profile 分家。dsh-panel update 不带 --profile 时升级全部已安装本插件的 profile。
profile 概览
dsh-panel profiles # 每个 profile 的已装版本 / bundles 挂载情况 / 安装 spec
输出的第一行是当前 dsh-panel 入口的路径与版本。这条信息是必要的:dsh-panel 是全局单例 shim,
由最后启动的那个 profile 写入(src/global-shim.ts),因此它与表中任何一行都没有必然关系。
技能子命令
dsh-panel skill --help
dsh-panel skill list # 列出技能(含工作区:全局 / 工作区)
dsh-panel skill add <path> # 添加到全局(.md 文件、目录束或 .zip 压缩包)
dsh-panel skill add <path> --workspace D:\项目A # 直接添加到指定工作区
dsh-panel skill scope <name> --global # 迁移单个技能到全局
dsh-panel skill scope <name> --workspace D:\项目A # 迁移到指定工作区(--copy 复制)
dsh-panel skill migrate <name...|--all> --from <全局|路径> --to <全局|路径> [--copy] [--yes]
dsh-panel skill disable <name> # 停用
dsh-panel skill enable <name> # 启用
dsh-panel skill delete <name> # 删除(需确认)
技能文件全局共享(~/.dsh/skills 与 <工作区>/.dsh/skills),不按 profile 分家,所以
skill 子命令不需要 --profile。可选地加上它,会顺便确认该 profile 存在,并在它没把本插件
挂进 dsh.profile.bundles 时提醒你「这个 profile 看不到面板」。
MCP 子命令
dsh-panel mcp list [--ids] --profile web
dsh-panel mcp add --name <serverName> --stdio --command <cmd> [--args <arg> ...] [--env KEY=VALUE ...] [--cwd <path>] --profile web
dsh-panel mcp add --name <serverName> --http --url <url> [--header KEY=VALUE ...] --profile web
dsh-panel mcp enable|disable <serverName> [--id <entryId>] --profile web
dsh-panel mcp remove <serverName> [--id <entryId>] [--yes] --profile web
dsh-panel mcp raw <serverName> [--id <entryId>] [--set <file>] --profile web
dsh-panel mcp test <serverName> [--id <entryId>] --profile web
环境变量取值直接写 ${NAME}(保存时格式化成 cordis.patch.yml 的 !!js 表达式,由 DSH 装载时求值);以 !!js 开头的值按进阶写法原样透传:
dsh-panel mcp add --name github --stdio --command npx --args -y --args @modelcontextprotocol/server-github \
--env 'GITHUB_TOKEN=${GITHUB_TOKEN}' --profile web
dsh-panel mcp add --name web --http --url https://example.com/mcp \
--header 'Authorization=Bearer ${MCP_TOKEN}' --profile web
# 进阶:整段 JS 表达式原样写进 !!js 标签
dsh-panel mcp add --name web2 --http --url https://example.com/mcp \
--header 'Authorization=!!js `Bearer ${process.env.MCP_TOKEN}`' --profile web
dsh-panel mcp add --name github --stdio --command npx --args -y --args @modelcontextprotocol/server-github \
--env 'GITHUB_TOKEN=!!js process.env.GITHUB_TOKEN' --profile web
dsh-panel mcp add --name web --http --url https://example.com/mcp \
--header 'Authorization=!!js `Bearer ${process.env.MCP_TOKEN}`' --profile web
新建的 MCP 服务器写入目标 profile 的 cordis.patch.yml 受管块;已经写在文件里的每一条
@deepseek-ai/dsh-mcp-client(含手写行,有没有 id 都行)都能 enable / disable / remove,也能用
dsh-panel mcp raw 打印或 --set 替换这一条的原文。原文只能改这一条的内容,缩进和位置不能变
(受管块 insert 成员不能改成顶层条目),条目后面属于别的行的注释也不会被带走。网关在线时自动热加载。
同名的两条(serverName 重复)不能直接按名字操作:dsh-panel mcp list --ids 会打出每条的 entryId,
加上 --id <entryId> 就能精确操作其中一条,再用 raw --set 把其中一个 serverName 改掉即可。
文件里原本就有重名时面板不会拒绝保存,只拒绝新增重名。受管块由
# >>> dsh-skill-mcp-panel:mcp:begin / # <<< ...end 标记;块里不是 MCP 的行请勿手改语义。
更新插件
dsh-panel update # 升级全部已安装本插件的 profile
dsh-panel update --yes # 同上,不询问
dsh-panel update --profile web # 只更新 web
dsh-panel mcp update # 等价于 dsh-panel update
- 对比基准是每个 profile 自己
node_modules 里已装的版本,不是正在执行的那份 CLI 的版本。
- 更新保持该 profile 原本的安装渠道:tarball 装的仍走 tarball(换成对应版本的版本化 URL),npm 装的仍走 npm 并钉到该版本,
Git(或来源未知)装的仍用
github:Fishquito7/dsh-skill-mcp-panel#v<版本>。
- 已经是最新的 profile 会被跳过,不会被重装。
desktop 由 DSH 桌面应用独占,宿主的 dsh plugin --profile desktop 会直接报错;自动枚举时跳过并说明,
显式 --profile desktop 时以退出码 2 报错。
- 更新完成后:客户端 bundle 热更新(刷新页面即可),服务端改动需要重启对应 profile 的网关。
CLI 只扫描当前目录锚定的项目根与用户根;管理其他工作区的技能请加 --cwd <工作区路径>。
同名技能存在于多个作用域时,enable/disable/delete 需加 --global/--project/--workspace 指定操作哪一份。
add 不在此列:目标作用域内没有同名就照常添加,跨作用域同名只打印一条提示(v2.1.7)。
工作原理
技能部分
页面和 dsh-panel skill 命令的每次操作,最终都是对磁盘上技能文件(SKILL.md)的改动,DSH 自带的文件监听器立刻发现变化——所以启用/停用、增删、迁移都热生效,无需重启网关。
- 技能实体直接存放在其工作区的技能文件夹:全局 =
~/.dsh/skills,工作区 = <工作区>/.dsh/skills,没有隐藏存储或联接点——卸载插件后技能仍是普通文件,照常被 DSH 发现
- 停用 = 把
SKILL.md 改名为 SKILL.md.disabled,启用 = 改回来
- 改变所属位置 = 真实地把文件复制/移动到目标位置的文件夹(先校验、失败回滚)
- 随部署附带的技能(bundled)为只读,不可停用或删除
MCP 部分
负责维护 profile 的 cordis.patch.yml 里每一条 @deepseek-ai/dsh-mcp-client;真正连接和注册工具的是 DSH 官方插件 @deepseek-ai/dsh-mcp-client,由 DSH 的 HMR 自动加载。
OAuth 发现按「RFC 9728 路径形态 PRM → 根 PRM → RFC 8414 插入式 AS → 根 AS → OIDC → 旧行为兜底」的顺序尝试,带路径的端点共 8 个候选(只有 origin 时去重为 3 个);命中 PRM 后再解析 authorization_servers[],并用 PRM 声明的 issuer 精确校验 AS 元数据。授权 URL 与 token 请求都带 RFC 8707 的 resource(优先 PRM 声明的同源值,否则取 MCP 端点 URL),兼容 Ardot 的 invalid_target 校验。失败时错误会列出已尝试的 URL 与每条的原因(404 / 超时 / issuer 不匹配),不再是笼统的「没有暴露发现文档」。
开发
源码为 TypeScript,位于 src/;编译产物 lib/*.js 随仓库一起提交(保证 Git 直装可用)。
改完源码后运行 pnpm build:tsc 编译到 lib/ 并剥离浏览器束的多余模块标记。
发布时 npm pack 会通过 prepack 自动重新构建,无需手工编译。
卸载
dsh plugin --profile web remove dsh-skill-mcp-panel
更新日志
只列会改变使用方式的版本要点;完整改动见 Releases。
-
v2.3.7 —— 窄屏适配(#71,感谢 @2532316972 的截图反馈):窗口或面板宽度小于 820px 时自动切到简洁布局,不再出现三栏把主区挤成一列图标、内容右侧被裁掉、连布局开关都被推出屏幕外的情况。自动切换只影响显示:不覆盖你手动选的布局、不写浏览器里的偏好,拉宽后回到你选的那套;窄屏下那枚「三栏 / 简洁」开关直接不显示,避免点了又被宽度压回来。判据同时看视口宽度和面板自身宽度——面板挂在宿主主区里,窗口宽 ≠ 面板宽,左侧会话栏占掉一截时按面板自身宽度算。MCP 页在窄屏下也改单列(卡片、表单),工具栏允许换行
-
v2.3.6 —— MCP 页接管 cordis.patch.yml 里的全部 @deepseek-ai/dsh-mcp-client 行(#67 / PR #70):启停、表单编辑、删除、OAuth 不再区分受管块和 panel-mcp-*。表单继续管常规字段,并保留还没建模的键(例如 maxInstructionBytes)以及已有的 disabled;卡片上的 原文(CLI:dsh-panel mcp raw)按字节改这一条。已有配置不用迁移,非 MCP 的外来行仍按 ADR-0003 原样保留。合并后实测又补了几处:没有 id 的手写行也能启停(按这一行自己的缩进插 disabled,表单保存也不会给它补 id);!!js 的 disabled 由宿主求值,卡片标「由表达式控制」并禁用开关,不再给一个点了必然报错的开关;删掉受管块最后一行时 begin/end 标记会成对收掉、块外内容不再被卷进去;条目后面的注释不会被表单保存或删除吞掉;CRLF 文件不再被掺进 LF 行;重复 id / 重复 serverName 不再改错行——所有操作按 serverName + entryId 双键定位,CLI 新增 --id <entryId> 与 list --ids,重名文件可以用原文逐条改回来,面板只拒绝新增重名;原文编辑不许改缩进/位置(insert 成员不能变顶层条目)。OAuth 发现顺序(PR #72)改成 RFC 9728 路径形式优先、PRM 声明的 resource 同源才采用,PRM 驱动的 issuer 必须精确匹配;见 ADR-0007
-
v2.3.5 —— 修 禁用任一 MCP 工具后宿主卡死(#59 / #62)。根因在我们自己的监听器:宿主的 ScopedLayers.effect() 在限制生效后和撤销时都会同步广播 tools/change,而插件既监听这个事件、又在回调里 undo() / restrict(),等于自己触发自己——同步无限递归,爆栈抛出的 RangeError 又被空 catch 吞掉,进程不崩、只是主线程持续 100% 烧 CPU。用宿主真实的 @deepseek-ai/dsh-scope 复现:一次事件放大成 887 次 restrict / 1774 次 view / 递归 458 层。现在 tools/change 只由控制器统一监听并带重入守卫,每个插件的 sync() 只在禁用集合真的变化时才撤销重施(集合没变就只多一次 view),挂载途中的 agent 用 mounting 挡住;原本静默的 catch 全部接上 logger,这类故障以后至少留得下日志;顺便修了同一 issue 里「显示插件技能」复选框不记忆的问题(现在勾选状态记在浏览器里)
-
v2.3.4 —— 修「打开目录」只弹出「文档」的那个 bug(2.3.3 的回归)。Windows 的 explorer.exe 不认正斜杠:面板把技能根路径规范化成了 C:/Users/.../.dsh/skills,explorer 收到这种参数会当成无效目标、默默打开自己的默认位置,于是点哪个工作区都只看到「文档」。现在宿主先把路径换成本机分隔符再交给系统,提示条里显示的也是同一份路径,复制出来直接能粘进资源管理器;顺手修了两个边界:盘符根 不再被裁成 ,末尾多余的 会收敛掉。界面预览换成新版截图(三栏 / 简洁两种布局各一张)
链接
License
MIT