dsh-skills-mcp-panel
Verifieddsh-skills-mcp-panel · v0.2.0 · MIT · Web UI
Skills & MCP panel for the DeepSeek Harness Web GUI: list, search, inspect and hot-toggle skills and MCP servers from Settings.
Install
dsh plugin add dsh-skills-mcp-panel Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-skills-mcp-panel
English | 中文
本项目由 DeepSeek Flash 生成。 这里每一个文件——插件的两个半边、测试、以及本文档——都由运行在 DeepSeek Harness 中的该模型编写,需求与评审来自项目所有者。
DeepSeek Harness Web GUI 的「技能与 MCP」面板。 在设置页的「Agent 预设」下新增一个分页,列出本部署已组合的每一个技能和每一个 MCP 服务器,并带有搜索框、每行状态灯、状态选择器,以及展开详情的箭头。
两类条目都是不重启进程即可改变:
| 选择器写入了什么 | 何时生效 | |
|---|---|---|
| 技能 | 该技能 SKILL.md 前言里的 disable-model-invocation 与 user-invocable——这两个位拼出的四种状态 |
技能提供方监听扫描根,下一次 agent/pre-step 会重新发布目录——同一个会话内即可见 |
| MCP 服务器 | 往 profile 自己的 cordis.patch.yml 写入一条按 id 定向的 disabled: true |
patchReload: live 的 profile 会通过配置 HMR 重新应用补丁,就地重载该服务器的连接——同一个会话内即可见 |
除此之外什么都不动:面板从不删除技能、从不修改服务器的配置,也从不会重新格式化你的 patch 文件——详见写入内容。
安装
本包开箱即跑:lib/ 就是真源,没有构建步骤,git 安装也不需要 allowBuilds 放行。
# 从 npm 安装
dsh plugin --profile web add dsh-skills-mcp-panel
# 从 GitHub 安装(想跟着仓库走就用这条)
dsh plugin --profile web add github:KyattoCat/dsh-skills-mcp-panel
# 开发时从本地目录安装
dsh plugin --profile web add link:/absolute/path/to/dsh-skills-mcp-panel
无论走哪条路,装下来的包只含 lib/、两份 README、cordis.patch.yml 和许可证;tests/ 与 CONTRIBUTING.md 留在仓库里。
之后需要重启一次 dsh web 进程——安装新的 bundle 层是实时 patch 重载唯一覆盖不了的操作,因为 dsh.profile.bundles 是启动时读取的。此后的每一次改变都是热的。
卸载:
dsh plugin --profile web remove dsh-skills-mcp-panel
使用
打开设置 → Agent 预设 → 技能/MCP(入口就排在 preset 页下面)。页面包含:
- 一个搜索框,按名称、描述和路径过滤;
- 两个分页并带实时计数——技能与 MCP 服务器;
- 分组:条目来自哪个目录就归到哪一组(见下),每组有自己的标题、路径和计数——标题本身就是一个开关,可以收起或展开该组;
- 每行一张卡片:标题、来源、彩色状态灯、状态选择器,以及一个与选择器同款样式的详情开关(
+/−); - 每张卡片的详情面板:技能显示描述 / 何时使用 / 来源根 / 路径 / 正文行数 / 模型可调用 / 用户可调用;MCP 服务器显示条目 id / 传输方式 / 命令或 URL / 连接状态 / 工具数 / 工具名;
- 没有常驻页脚:只有当一次改动落在"下次启动
dsh才生效"的 profile 上时,页面才会加一行提示。
分组
条目按它所属的目录分组,因此"你为某个项目写的技能"和"你为所有项目装的技能"一眼可分。
当前项目置顶。 GUI 当前会话所在的目录对应的那一组会被钉在列表最前面,默认展开,并带一个当前项目的标记;其余各组默认收起。点击组标题即可收起或展开该组,你的选择会一直保留到重新打开这个页面——一次开关操作带来的刷新不会把它推翻。没有打开任何会话时,回退到 dsh web 进程自己所在的项目;会话位于本部署从未登记过的工作区时同样回退,因此永远恰好有一个项目组置顶,而不是一个都没有。被钉住的那个项目若本身没有任何技能或服务器,它在页面上没有分组可渲染,此时改为展开第一个有条目的分组。
技能。 每个被扫描的根对应一个组:
| 分组 | 根 |
|---|---|
| 全局 | $DSH_HOME/skills 与 $DSH_AGENTS_HOME/skills——不论项目都归你的技能 |
| 每个项目一组 | <项目>/.dsh/skills 与 <项目>/.agents/skills,覆盖本部署知道的所有项目:Web GUI 打开过的每个工作区,加上 harness 进程自己的工作目录 |
| 每个配置根一组 | skillRoots 的每一项,以它自己的目录名命名 |
组标题会带上组名、完整路径和条目数。
MCP 服务器。 服务器本身没有目录,因此面板取它的运行目录:cwd 落在上述某个项目内的 stdio 服务器归入该项目组,其余服务器——HTTP 服务器,以及继承 harness 进程目录的 stdio 服务器——都归全局。
技能状态
技能的两个前言键彼此独立,因此共有四种状态,选择器把它们全部列出:
| 选择 | disable-model-invocation |
user-invocable |
谁能加载它 |
|---|---|---|---|
| 已启用 | 不存在(默认) | 不存在(默认) | 模型会自行路由到它,你也可以敲 /名字 |
| 仅手动 | true |
不存在 | 只有你——模型在目录里看不到它,也无法用 skill 工具加载 |
| 仅模型 | 不存在 | false |
只有模型——它不会出现在 / 菜单里,/名字 也调不到 |
| 已停用 | true |
false |
两个界面都够不到;只剩受信的 ctx.skills.get() 调用方 |
MCP 状态
MCP 行只有一个启用位,因此它的选择器只有已启用和已停用。连接已稳定并处于 active 的服务器不再挂第二个标签;只有连接尚未稳定(loading 或 failed)时,选择器旁边才会出现一个标签——因为那是实时事实而不是设置。详情面板里的长工具列表同理:一行一个名字,超出卡片高度后列表内部滚动。
状态灯含义:绿色 = 模型够得到(技能)或服务器已连接(MCP),琥珀色 = 仅手动(技能)或仍在连接中(MCP),红色 = 解析失败(技能)或连接失败(MCP),灰色 = 已停用。
写入内容
技能。 只动那两个调用相关的键。true 是提供方自己的默认值,因此需要它的状态会删除该键而不是写一个冗余值:已启用两个键都不写,仅手动写 disable-model-invocation: true,仅模型写 user-invocable: false,已停用两个都写。所以一个技能离开默认态再回到默认态会恢复成原始字节,而无关的键永远不会被移动。
MCP 服务器。 面板在 profile 的 cordis.patch.yml 末尾独占一个带标记的块:
# >>> dsh-skills-mcp-panel (managed — edit these rows from the panel)
- id: "mcp-playwright"
disabled: true
# <<< dsh-skills-mcp-panel
两道栅栏之外的所有内容都逐字节保留——包括注释、格式和你自己的条目。每次改变都会整体重写这个块,因此它始终只列出当前被面板停用的服务器;把最后一个也重新启用时,块会被移除,文件回到最初的字节。写入走临时文件加 rename,所以 patch watcher 永远不会看到写了一半的文档。
配置
所有字段都是可选的;默认值就是原版 dsh web 需要的。
| 字段 | 默认值 | 含义 |
|---|---|---|
patchPath |
自动探测 | MCP 选择写入的 patch 文件。探测会选 $DSH_HOME/profiles 下 dsh.profile.bundles 里列了本插件的那个 profile,并优先选 patchReload: live 的。如果你保留多个 profile,请显式指定。 |
dshHome |
$DSH_HOME 或 ~/.dsh |
Harness 主目录;会扫描其 skills 子目录,并在其 profiles 目录下查找。 |
agentsHome |
$DSH_AGENTS_HOME 或 ~/.agents |
共享 agent 根目录;会扫描其 skills 子目录。 |
projectRoot |
process.cwd() |
harness 进程自己所在的项目;它的 .dsh/skills 与 .agents/skills 会和本部署知道的每个工作区一起被扫描。 |
skillRoots |
[] |
额外的技能根,每个自成一个分组。 |
maxSkillProjects |
64 |
单次扫描覆盖的工作区上限;先按路径排序再截断。 |
- id: skills-mcp-panel
name: dsh-skills-mcp-panel
config:
projectRoot: /home/me/work
skillRoots:
- /home/me/team-skills
实现方式
一个包里的两个半边,都是纯 JavaScript,无依赖:
- Host 半(
lib/index.js)在ctx.webServer上注册两条 exact 路由,并在组合的connection信任闸之后应答(浏览器会话 cookie 加 Host/Origin 校验)。GET /skills-mcp/state扫描技能根、遍历 Loader 找@deepseek-ai/dsh-mcp-client行;浏览器会把当前会话所在目录作为?cwd=传上来,置顶哪一组由它决定。POST /skills-mcp/toggle执行上面描述的写入,并回同一份完整快照(body 里同样带cwd),因此一次改动不会在光标底下把页面重新排序。 - 浏览器半(
lib/client.js)直接以客户端模块系统的 lazy-CJS 工厂形式书写(window.__ModuleLoader__.load({ id, factory })),因此不需要打包器,也不需要共享构建 preset。它注册一个settings.section贡献,并且只从模块表取react。
已知限制
- 面板的改动不等于编辑 preset。 由 preset 的
agent.cordis.yml提供的技能或 MCP 行,仍然是在创建会话时组合的;面板的 MCP 选择作用于 profile 的 patch 层,对所有会话生效。要隐藏整个由 preset 提供的行,仍然得改 preset,并由新会话拾取。 - 启动即冻结的 profile。
headless、sdk、acp默认是patchReload: startup;面板的 MCP 写入会落盘,但要到下次启动才应用,改动之后页面会立即说明这一点。技能在所有 profile 上都是热的,因为技能提供方监听的是文件而不是配置。 - 技能是从磁盘读的,不是从注册表读的。 面板复刻了文件系统提供方的发现规则(顶层
SKILL.md目录包和扁平的<name>.md,跳过点号目录),因为它要改写的是真正控制可见性的那个文件。由其他提供方提供的技能会出现在模型目录里,但没有文件可供本面板改动。 - 没有导入、新建和删除。 这页只改变已经存在的东西,既不新增也不移除技能或服务器。
- MCP 没有项目级配置源。 DSH 的 MCP 服务器来自 profile、harness 主目录和内置组合包——没有"每个项目一份 MCP 文件"可读——所以只有当某个服务器自己的
cwd指向项目内部时,它才会落进项目组。其余的一律归全局,这是事实,不是缺口。
开发
node tests/smoke.mjs # 在临时目录里用桩 Cordis 上下文驱动两条路由
冒烟测试断言两件最关键的事:四种技能状态都能往返(含回到默认态时逐字节还原原始文件),以及 patch 文件在托管块之外保持字节不变。
打包规则与发版步骤写在 CONTRIBUTING.md——那些是维护者的事,不是给使用者的说明。
许可证
MIT