dsh-mcp-servers-panel
Verifieddsh-mcp-servers-panel · v0.2.3 · MIT · Web UI
DSH MCP 服务器管理插件:在设置中提供 MCP Servers 管理面板,负责把服务器配置写入当前 Profile 的 cordis.patch.yml 并启停官方 @deepseek-ai/dsh-mcp-client 条目;连接、重连、工具注册与 teardown 由官方 client 负责。
Install
dsh plugin add dsh-mcp-servers-panel Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-mcp-servers-panel
DSH(DeepSeek Harness)的 MCP 服务器管理面板插件。
自 0.2.0 起,面板的定位是 管理平面(management plane):它只负责把官方 @deepseek-ai/dsh-mcp-client 的 Loader 行写入当前 profile 的 cordis.patch.yml、切换这些行的启停状态,并从 Loader 读取实时状态。
MCP 的 stdio / Streamable HTTP 传输、进程启动、协议握手、自动重连、工具注册、资源与清理,全部由官方客户端负责。面板不再自行启动 MCP 进程,也不再实现或转发 MCP JSON-RPC。
[!WARNING] 0.2.0 是 breaking change。 面板不再读取旧版
~/.dsh/mcp.json与<workspace>/.dsh/mcp.json,也不会自动合并它们;旧版面板管理的 MCP 不会随升级继续运行。面板检测到旧文件时会显示提示,可在「查看并导入」中逐条选择导入:默认写入禁用行,导入后是当前 profile 的全局配置(不再按工作区区分),同名条目会被标记并默认不可选。旧文件不会被修改或删除。
0.2.0 的职责边界
| 能力 | 本面板 | 官方 MCP 客户端 |
|---|---|---|
| 配置存储 | 写入活动 profile 的 cordis.patch.yml |
读取并校验 Loader 行 |
| 启停 | 切换面板行的 disabled |
根据 Loader 状态启动或停止连接 |
| 传输与进程 | 不参与 | stdio / Streamable HTTP、进程生命周期 |
| 协议 | 不参与 | 握手、工具发现、工具调用、资源读取 |
| 工具注册 | 只读展示 | 以 mcp__<serverName>__<tool> 注册到 DSH 工具运行时 |
| 重连与清理 | 不参与 | 重连策略、资源提供者、连接与进程清理 |
面板是这些官方条目的管理器,而不是 MCP 客户端本身。工具名称、服务器指令、重连行为等以官方客户端为准。
存储模型
活动 profile
面板通过 DSH 启动器注入的 profileContext 服务定位活动 profile。该服务包含 name、dir、patchPath 等位置信息;面板只读写该 profile 的 cordis.patch.yml(即 patchPath)。
0.2.0 只管理当前活动 profile:
- 用户级 / 工作区级的作用域概念已移除,界面没有作用域选择器。
- 旧版的
~/.dsh/mcp.json与<workspace>/.dsh/mcp.json不再被读取或写入;面板只在启动/刷新时为升级提示只读这些文件。 - 如需沿用旧配置:在升级横幅中点「查看并导入」,逐条勾选要迁移的服务器;也可以取消后在编辑器的 JSON 模式中手动粘贴。导入后写入当前 profile 的
cordis.patch.yml,默认禁用。
面板行
面板把每个服务器写成 profile patch 中的一条普通 Loader insert 行:
- 行 id:
dsh-mcp-panel-<serverName> - 模块名:
@deepseek-ai/dsh-mcp-client - 服务器配置:该行的
config - 开关:服务器关闭时写入行级
disabled: true,启用时移除该字段
示例(面板只为非默认值写字段;未写出的字段由官方客户端使用默认值):
- insert:
- id: dsh-mcp-panel-fast-context
name: "@deepseek-ai/dsh-mcp-client"
config:
serverName: fast-context
transport: stdio
command: npx
args:
- -y
- '@sammysnake/fast-context-mcp'
- id: dsh-mcp-panel-docs
name: "@deepseek-ai/dsh-mcp-client"
# 服务器关闭时由面板写入
disabled: true
config:
serverName: docs
transport: streamable-http
url: http://127.0.0.1:3000/mcp
env、cwd、headers、toolCallTimeoutMs、reconnect、failOnStartupError、maxInstructionBytes 等字段只在显式设置且不同于默认值时出现;默认值见下表。
面板使用 yaml 包的 Document API 读写 patch,只增加、更新或删除 id 以 dsh-mcp-panel- 开头的 insert 行;无关条目的注释与 !!js 标签会被保留。
配置字段
以下字段对应官方客户端的 Config;disabled 是 Loader 行字段,不属于 config。
| 字段 | 适用传输 | 默认值 | 说明 |
|---|---|---|---|
serverName |
必填 | — | 服务器命名空间,^[A-Za-z0-9_-]{1,32}$,加载作用域内唯一 |
transport |
必填 | — | stdio 或 streamable-http |
command |
stdio | 必填 | 可执行文件 |
args |
stdio | [] |
参数数组 |
env |
stdio | {} |
在清理凭据类变量和 DSH_* 变量后的环境之上合并 |
cwd |
stdio | '' |
工作目录 |
url |
streamable-http | 必填 | MCP 端点 URL |
headers |
streamable-http | {} |
额外请求头 |
toolCallTimeoutMs |
两者 | 60000 |
单次 tools/call 或资源请求的超时 |
reconnect.enabled |
两者 | true |
连接丢失后自动重连 |
reconnect.initialDelayMs |
两者 | 500 |
首次重连延迟;连续失败后逐次翻倍 |
reconnect.maxDelayMs |
两者 | 30000 |
退避上限,也是重置尝试预算的连接稳定时长 |
reconnect.maxAttempts |
两者 | 10 |
单次中断的连续失败上限;耗尽后移除工具并停止重连,直到重载配置或重启 |
failOnStartupError |
两者 | false |
初始连接或工具同步失败时,是否让插件激活失败 |
maxInstructionBytes |
两者 | 32768 |
服务器指令的 UTF-8 字节上限(含归属行),超限拒绝连接 |
disabled |
两者 | false |
Loader 行字段;关闭服务器时由面板写入 |
来源与只读行
列表中的每一行来自以下两种来源之一:
- 本插件:id 以
dsh-mcp-panel-开头、由本插件写入的行(下文称「面板行」),来源徽标显示「本插件」。 - 第三方导入:其他所有
@deepseek-ai/dsh-mcp-client行,来源徽标统一显示「第三方导入」。包括 Magpie 的magpie-mcp-*、其他 DSH 插件清单写入的条目,以及各类第三方导入工具。面板不按 id 前缀区分具体导入器,新增一种导入工具无需改代码。
面板行可以新增、编辑、删除和启停。第三方导入行只读:不能编辑或删除,只能通过开关启停;启停由 DSH 插件管理器(pluginManager.setPluginEnabled)处理。若插件管理器不可用或该行不可控,开关会被禁用并显示原因。
面板不会修改或删除其他来源的行。删除面板行时,只移除对应的一条 insert 行。
重名规则
serverName 是官方客户端的命名空间,在加载作用域内必须唯一;面板行属于 profile 级全局条目,因此面板按全局唯一校验。官方客户端在加载时拒绝重复项(后加载的一条会报错并失败)。
面板在保存前会同时检查自己的行和 Loader 中所有(含已禁用)的 @deepseek-ai/dsh-mcp-client 行;发现重名时拒绝写入并给出错误。非面板行不能被同名的面板配置接管;停用只写入行级 disabled,不会释放 serverName,要释放名字需移除对应行。
需要改名时,在 JSON 模式中给服务器一个不同的名字,面板会以 replaceName 提交替换。新名称必须唯一:把 A 改名为已存在的另一条面板服务器 B 会被拒绝,不会覆盖 B。
生效路径
面板的所有写操作在管理器内串行执行。
- HMR 可用时:在
hmr.runExclusive事务中先原子写入 patch 文件,再调用hmr.reconcileProfile(requiredIds)让 Loader 立即应用。新增、编辑、启用传入该面板行的 id;禁用、删除传入空列表。若 reconcile 失败,面板恢复写入前的文件内容并再次 reconcile,然后向界面抛出错误,避免留下半应用状态。 - 没有 HMR 时:只写入 patch 文件,并返回
application: 'restart-required';界面显示警告横幅,需要重启 DSH 才能加载新配置。 - 保存、删除、启停的结果都会带上
application:applied表示无需重启(可能已经生效),restart-required表示需要重启。第三方导入条目的启停若被更高优先级的配置覆盖,仍返回applied,但会附带提示,实际状态可能未改变。
常规 DSH Desktop / Web 的 profile 启动会提供 HMR;headless、SDK、ACP 等组合默认禁用 HMR,在配置中关闭 HMR 时同样会退回到重启生效。
profileContext 要求
面板依赖 DSH 启动器提供的 profileContext:
- 存在时:界面在副标题与每行徽标中显示当前 Profile 名称,可以读写该 profile 的 patch。
- 不存在时:面板会显示缺少 profile 的提示,
canManage为 false,保存、删除等写入 profile patch 的操作不可用;第三方导入行的启停仍取决于 DSH 是否提供插件管理器。面板不会回退到用户级或工作区级配置文件。
profileContext 由 DSH 宿主提供,不是插件配置项。没有 profile 上下文说明当前启动方式不提供可管理的 profile,此时面板只能展示提示,不能写入 profile patch。
功能特性
设置页无缝集成
- 启动后自动在设置左侧菜单添加「MCP 服务器」选项卡。
- 遵循 DSH 原生界面规范,纯 SVG 图标,无 Emoji,界面文案为中文。
- 头部显示标题与副标题(副标题带 Profile 名称或缺少 profile 的提示)、来源筛选、新增按钮和刷新按钮;按钮顺序保持 0.1.7 的「筛选 → 新增 → 刷新」。
服务器列表
- 显示服务器名称、状态点、Profile 徽标、来源徽标(本插件 / 第三方导入)和传输徽标(stdio / streamable-http)。
- 头部可按来源筛选:全部来源 / 本插件 / 第三方导入。
- 面板行按 patch 文件顺序排列在前,第三方导入行按名称排列在后。
- 状态副文本按以下顺序取第一个匹配项:已禁用;加载失败;正在加载...;已启用,{n} 个工具可用;已启用,暂无可用工具。
- 有警告时显示提示标记。
展开详情
- 展开后可查看错误信息、警告、实际生效的配置(transport、command、args、cwd、url、toolCallTimeoutMs、reconnect 等)、对应的 patch 行 id、profile 的 patch 路径,以及官方客户端已发现的工具(名称与描述)。
新增与编辑
- 提供「表单」与「JSON」双模式,编辑时目标是当前 profile 的 patch;编辑器顶部显示当前 Profile,并在其下方以只读方式显示目标 patch 路径。
- 表单模式:输入名称(编辑时禁用)、传输方式、stdio 的命令 / 参数 / 环境变量,或 streamable-http 的 URL / 请求头。
- JSON 模式:兼容完整
{mcpServers: {...}}、{servers: {...}}、顶层数组、裸映射,以及单个官方@deepseek-ai/dsh-mcp-client配置对象,可一次粘贴批量导入;未知字段会被忽略;enabled: false映射为一条禁用行。 cwd、toolCallTimeoutMs、reconnect、failOnStartupError、maxInstructionBytes等进阶字段在 JSON 模式中编辑。- 编辑时保留服务器名称,除非 JSON 中指定了不同的名称;后者以
replaceName提交,且新名称必须唯一(不能覆盖另一条面板服务器)。
启停与删除
- 面板行可直接启停,写入或移除该行的
disabled。 - 非面板行只读,但可以在可控时通过开关交由 DSH 插件管理器启停。
- 删除面板行只移除对应的 insert 行,不影响其他来源的行。
- 面板行可直接启停,写入或移除该行的
重启提示
- 当 DSH 返回
restart-required时,界面显示警告横幅,并保留到下一次刷新。
- 当 DSH 返回
安全
- 管理接口只接受回环地址(
127.0.0.0/8、::1、IPv4 映射的::ffff:127.x),其他来源拒绝。 - HTTP 请求体上限为 256 KB,响应使用
Cache-Control: no-store。 - 前端通过 loopback RPC 调用管理接口;宿主也在同一前缀提供 loopback HTTP 端点(
list/legacy/import/save/delete/toggle/reload)。 - HTTP 入口仍受 DSH Web 服务自身的访问控制(未携带会话凭据的请求会被拒绝);仍不要在不可信的多用户环境暴露管理接口。
- 管理接口只接受回环地址(
安装
npm:
dsh plugin --profile <name> add dsh-mcp-servers-panel
GitHub:
dsh plugin --profile <name> add github:whiteS18/dsh-mcp-servers-panel
本地检出:
dsh plugin --profile <name> add /绝对路径/dsh-mcp-servers-panel
[!IMPORTANT] DSH 在进程启动时组合插件。先启动再安装插件时,必须完全退出并重新打开 DSH(不是刷新页面)。
启动 DSH Desktop 后,进入「设置」即可在侧边栏看到「MCP 服务器」面板。
要求 Node.js >= 20。运行时依赖 yaml@^2.9.1。
0.2.0 的行为变化
- 面板由「自己启动并托管 MCP 进程」改为「管理 profile patch 中的官方客户端行」。
- 移除自建的 stdio / HTTP 传输、MCP 握手、工具注册与注销、重连和进程树清理。
- 移除 Windows
spawn包装与 PATH 探测补全;stdio 命令的解析和启动由官方客户端及其 MCP SDK 负责。 - 移除用户 / 工作区作用域,只管理当前活动 profile。
- 不再读取或写入旧版
mcp.json;只读检测旧文件,并在界面上提供逐条选择的迁移导入(legacy/import接口)。不会自动合并,导入默认禁用且为 profile 全局配置。 - 新增对 profile patch 的 YAML Document 读写、对非面板行的只读展示、重名校验,以及重启提示横幅。
从 0.1.x 迁移
- 升级并完全重启 DSH。旧面板管理的 MCP 不会继续运行,这是 0.2.0 的预期行为。
- 打开 设置 → MCP 服务器。如果存在旧配置,顶部会出现「检测到旧版 MCP 配置」横幅。
- 点「查看并导入」,勾选要迁移的服务器;不会自动合并。与既有面板行、第三方导入行或其他旧配置重名的条目会标记「重名」且默认不可选,需要先删除/改名,或手动在 JSON 模式中改名导入。
- 「导入后立即启用」默认关闭:导入先写成
disabled: true的行,确认无误后在列表中逐条启用。导入后是当前 profile 的全局配置,不再保留原工作区区别。 - 旧文件(
~/.dsh/mcp.json、<workspace>/.dsh/mcp.json)不会被修改或删除;确认迁移完成后可自行备份/清理。 - 也可以直接取消,在「新增 MCP」的 JSON 模式中手动粘贴配置。
迁移是逐条、可取消的,不会把多个工作区的配置自动合并到一起。
修复与变更记录
0.2.3
- 修复热更新(HMR / 客户端重新同步)后设置里不再出现「MCP 服务器」:客户端半之前注册语言命名空间后没有返回销毁函数,插件被替换时旧注册会残留在页面里,下一次加载就会抛
locale namespace "settings.dsh-mcp-servers-panel" already has locale "zh",整个客户端半挂载失败。现在语言注册跟随 effect 生命周期释放;同时兼容已经残留旧注册的页面,遇到同一命名空间被占用时沿用现有字典继续挂载,无需强制刷新页面。 - 新增回归测试:卸载后重新 apply 不丢语言字典、旧版本泄漏命名空间时仍能挂载。
0.2.2
- 来源标签简化:来源徽标由「面板 / Magpie / 插件清单」改为「本插件 / 第三方导入」。本插件写入的行显示「本插件」;其余所有
@deepseek-ai/dsh-mcp-client行(Magpie、其他插件清单、第三方导入工具)统一显示「第三方导入」,不再识别magpie-mcp-*前缀,新增导入工具无需改代码。 - 来源筛选同步为「全部来源 / 本插件 / 第三方导入」;只读提示、错误信息与 README 一并更新。
0.2.1
- 新增旧版配置选择性迁移:面板只读检测
~/.dsh/mcp.json、<workspace>/.dsh/mcp.json(以及旧版mcp-servers.json回退);检测到非空配置时显示升级横幅,点「查看并导入」逐条勾选。legacy接口返回完整配置,import接口按选择写入 profile patch。 - 迁移不自动合并:默认不勾选重名项(与既有面板行、第三方导入行或其他旧配置重名);导入默认
disabled: true,可勾选「导入后立即启用」;旧文件只读,从不修改或删除。 - 新增测试:旧配置检测、摘要脱敏、选择性导入、默认禁用、重名拒绝、旧文件保持不变。
0.2.0
- 重构为管理平面:面板不再启动 MCP 进程、不再实现 MCP 协议,只读写活动 profile 的
cordis.patch.yml并读取 Loader 状态;传输、重连、工具注册、资源和清理交给官方@deepseek-ai/dsh-mcp-client。 - 新增
patch-store.js:通过yaml的 Document API 维护 id 为dsh-mcp-panel-<serverName>的 insert 行,保留无关条目的注释和!!js标签。 - 新的存储格式:每个服务器对应一条官方客户端 Loader 行,配置字段为
serverName、transport、command、args、env、cwd、url、headers、toolCallTimeoutMs、reconnect、failOnStartupError、maxInstructionBytes;关闭时写入行级disabled: true。 - 新增 profile 依赖:通过
profileContext定位活动 profile 的 patch;缺失时显示提示并禁用管理操作。界面新增 Profile 徽标。 - 新增 HMR 生效路径:在
hmr.runExclusive事务中写入并reconcileProfile,失败时恢复旧文本;没有 HMR 时返回restart-required并显示警告横幅。 - 新增非面板行处理:识别 Magpie(
magpie-mcp-*)与其他插件清单写入的@deepseek-ai/dsh-mcp-client行,只读展示,启停交由pluginManager.setPluginEnabled。 - 新增重名校验:保存前同时检查面板行和所有
@deepseek-ai/dsh-mcp-client行(含已禁用行);serverName规则为^[A-Za-z0-9_-]{1,32}$;重命名到另一条面板服务器会拒绝,避免覆盖。 - 新增旧版配置迁移:只读检测
~/.dsh/mcp.json、<workspace>/.dsh/mcp.json(以及旧版mcp-servers.json回退),列表响应带legacyConfigs摘要;legacy返回完整配置,import按用户勾选逐条写入 profile patch。默认禁用、不覆盖既有行、不修改旧文件、不自动合并。 - 移除:自建进程与传输、MCP JSON-RPC 握手与工具桥接、工具注册与注销、自建重连和进程树清理、Windows spawn 包装与 PATH 探测、用户 / 工作区作用域、旧版
mcp.json读写。 - 测试更新:
npm test覆盖 patch 文档读写、管理器操作与重名、Loader 行来源与外来行、安全校验,以及client.js的静态检查。
以下 0.1.x 条目是历史版本的行为记录;其中进程托管、用户 / 工作区作用域、
mcp.json存储等能力已在 0.2.0 移除。
0.1.7
- 面板条目优先展示:当同一服务器既在面板配置中、又出现在 DSH 插件清单中时,列表以面板条目为主(徽标「面板」),并使用面板配置的命令/参数/URL、作用域和开关状态。同名的插件清单条目改为在展开详情的「同名插件清单条目」中显示,不再顶替该行的来源徽标。
- 开关、状态、工具数仍代表实际运行状态;若面板开关与插件清单条目的状态不一致,会显示提示。
- 更新回归测试:同名同参数时行来源仍为「面板」,插件清单条目作为附注存在。
0.1.6
- 更正术语:不再把插件清单中的 MCP 条目称为「DSH 原生」。
@deepseek-ai/dsh-mcp-client是 DSH 自带插件,但具体条目可能由 Magpie 等第三方工具写入。列表现在按来源显示「面板」/「Magpie」/「插件清单」,展开详情会显示清单条目 id。 - 修正删除语义:删除面板配置只移除
mcp.json条目,不再顺带停用同名插件清单条目。删除后列表会显示该插件清单条目(如 Magpie)及其真实启停状态,需要停用时用手动开关。删除面板配置时若有同名插件清单条目,确认框会提示这一点。 - 新增/更新回归测试:来源识别(
magpie-mcp-*识别为 Magpie)、删除面板配置后插件条目保持启用、同名不同参数时面板接管。
0.1.5
- 修复更新 DSH 时
graceful deadline exceeded报错:插件的 reconcile 与宿主关闭存在竞态。关闭时若有一次 reconcile 还在读取配置/握手,它会在dispose()之后继续创建 MCP 子进程,这些子进程让事件循环无法退出,桌面端只能等到超时后用 SIGTERM 杀掉宿主。现在:- 所有 reconcile 串行执行,并在每次
await之后检查disposed/代次,关闭后不再创建或重启任何 runner; dispose()会等待进行中的 reconcile 结束,并异步等待所有子进程退出;- Windows 下改用
taskkill /T /F终止整棵进程树(只杀cmd.exe包装进程会遗留真正的 MCP 服务进程),所有等待定时器都unref,无法阻止宿主退出; - 插件返回的 disposer 现在是异步的,Cordis 会在宿主关闭时等待清理完成。
- 所有 reconcile 串行执行,并在每次
- 修复禁用 MCP 后工具仍被注入上下文:
- 刚启动/切换工作区时并发触发的多次 reconcile 不再可能用旧配置把已禁用服务器重新拉起;
- 工具注册前会再次检查 runner 是否已被停止,停止时同步注销全部工具;
- 自动接管插件清单中的同名
dsh-mcp-client条目,面板禁用会同时禁用该条目,工具因此真正从上下文中移除。
- 新增
native-mcp.js(插件清单 MCP 条目桥接)与两组回归测试:test/mcp-lifecycle.test.js覆盖关闭/竞态/进程回收,test/native-mcp.test.js覆盖清单条目接管与启停同步。
目录结构
dsh-mcp-servers-panel/
├── cordis.patch.yml # Cordis 自挂载补丁
├── package.json # 包描述、依赖与平台声明
├── index.js # 宿主进程端(profile patch 管理、McpManager、RPC/HTTP)
├── native-mcp.js # 宿主进程端(Loader 行读取、来源识别、非面板行启停)
├── patch-store.js # 宿主进程端(yaml Document 读写与 insert 行增删改)
├── client.js # 前端渲染端(设置页 UI、列表、表单/JSON 编辑器)
├── test/ # 单元测试(patch、manager、native-mcp、security、client 静态检查)
├── README.md # 插件说明文档
└── LICENSE # MIT 开源许可证
测试
从插件目录运行:
cd dsh-mcp-servers-panel
npm test
测试覆盖:
- patch 文档的读取、插入、更新与删除,以及无关条目的注释和
!!js标签保留; - 管理器的新增、编辑、启停、删除、重名拒绝,以及写失败时的恢复;
- Loader 行的来源识别、非面板行只读与启停、官方客户端行重复名称;
- 旧版
mcp.json的只读检测、摘要脱敏、选择性导入、默认禁用、重名拒绝,以及旧文件保持不变; - 回环访问等安全校验;
client.js的静态检查:语法、旧作用域标识已移除、新的 API 字段与迁移流程存在。
已知限制
- 面板需要
profileContext;没有该上下文时只能显示提示,不能管理配置。 - 没有 HMR 的宿主中,修改需要重启 DSH 才能生效;面板会显示重启提示。
- 面板只管理当前活动 profile,不修改其他 profile;旧版
mcp.json只读用于升级提示和选择性导入,从不修改或删除。 - 非面板行只能启停,不能编辑或删除;这些条目应在写入它们的工具或 DSH 插件设置中维护。
serverName必须在面板管理的 profile 内唯一(^[A-Za-z0-9_-]{1,32}$);官方客户端会拒绝重复项。- 面板不包装命令、不探测或补全 PATH;stdio 启动失败由官方客户端报告,面板只展示 Loader 状态与错误。
许可证
MIT License