packages/dsh-task-board
zhu1090093659/dsh-web侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
在设置中提供 MCP Servers 管理面板:新增、编辑、启停、删除服务器,支持用户/项目作用域与表单/JSON 双模式编辑,并自动把发现的 MCP 工具注册为对话原生工具。
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 的全局配置(不再按工作区区分),同名条目会被标记并默认不可选。旧文件不会被修改或删除。
| 能力 | 本面板 | 官方 MCP 客户端 |
|---|---|---|
| 配置存储 | 写入活动 profile 的 cordis.patch.yml | 读取并校验 Loader 行 |
| 启停 | 切换面板行的 disabled | 根据 Loader 状态启动或停止连接 |
| 传输与进程 | 不参与 | stdio / Streamable HTTP、进程生命周期 |
| 协议 | 不参与 | 握手、工具发现、工具调用、资源读取 |
| 工具注册 | 只读展示 | 以 mcp__<serverName>__<tool> 注册到 DSH 工具运行时 |
| 重连与清理 | 不参与 | 重连策略、资源提供者、连接与进程清理 |
面板是这些官方条目的管理器,而不是 MCP 客户端本身。工具名称、服务器指令、重连行为等以官方客户端为准。
面板通过 DSH 启动器注入的 profileContext 服务定位活动 profile。该服务包含 name、dir、patchPath 等位置信息;面板只读写该 profile 的 cordis.patch.yml(即 patchPath)。
0.2.0 只管理当前活动 profile:
~/.dsh/mcp.json 与 <workspace>/.dsh/mcp.json 不再被读取或写入;面板只在启动/刷新时为升级提示只读这些文件。cordis.patch.yml,默认禁用。面板把每个服务器写成 profile patch 中的一条普通 Loader insert 行:
dsh-mcp-panel-<serverName>@deepseek-ai/dsh-mcp-clientconfigdisabled: 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 行字段;关闭服务器时由面板写入 |
列表中的每一行来自以下两种来源之一:
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.runExclusive 事务中先原子写入 patch 文件,再调用 hmr.reconcileProfile(requiredIds) 让 Loader 立即应用。新增、编辑、启用传入该面板行的 id;禁用、删除传入空列表。若 reconcile 失败,面板恢复写入前的文件内容并再次 reconcile,然后向界面抛出错误,避免留下半应用状态。application: 'restart-required';界面显示警告横幅,需要重启 DSH 才能加载新配置。application:applied 表示无需重启(可能已经生效),restart-required 表示需要重启。第三方导入条目的启停若被更高优先级的配置覆盖,仍返回 applied,但会附带提示,实际状态可能未改变。常规 DSH Desktop / Web 的 profile 启动会提供 HMR;headless、SDK、ACP 等组合默认禁用 HMR,在配置中关闭 HMR 时同样会退回到重启生效。
面板依赖 DSH 启动器提供的 profileContext:
canManage 为 false,保存、删除等写入 profile patch 的操作不可用;第三方导入行的启停仍取决于 DSH 是否提供插件管理器。面板不会回退到用户级或工作区级配置文件。profileContext 由 DSH 宿主提供,不是插件配置项。没有 profile 上下文说明当前启动方式不提供可管理的 profile,此时面板只能展示提示,不能写入 profile patch。
设置页无缝集成
服务器列表
展开详情
新增与编辑
{mcpServers: {...}}、{servers: {...}}、顶层数组、裸映射,以及单个官方 @deepseek-ai/dsh-mcp-client 配置对象,可一次粘贴批量导入;未知字段会被忽略;enabled: false 映射为一条禁用行。cwd、toolCallTimeoutMs、reconnect、failOnStartupError、maxInstructionBytes 等进阶字段在 JSON 模式中编辑。replaceName 提交,且新名称必须唯一(不能覆盖另一条面板服务器)。启停与删除
disabled。重启提示
restart-required 时,界面显示警告横幅,并保留到下一次刷新。安全
127.0.0.0/8、::1、IPv4 映射的 ::ffff:127.x),其他来源拒绝。Cache-Control: no-store。list / legacy / import / save / delete / toggle / reload)。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。
spawn 包装与 PATH 探测补全;stdio 命令的解析和启动由官方客户端及其 MCP SDK 负责。mcp.json;只读检测旧文件,并在界面上提供逐条选择的迁移导入(legacy / import 接口)。不会自动合并,导入默认禁用且为 profile 全局配置。disabled: true 的行,确认无误后在列表中逐条启用。导入后是当前 profile 的全局配置,不再保留原工作区区别。~/.dsh/mcp.json、<workspace>/.dsh/mcp.json)不会被修改或删除;确认迁移完成后可自行备份/清理。迁移是逐条、可取消的,不会把多个工作区的配置自动合并到一起。
locale namespace "settings.dsh-mcp-servers-panel" already has locale "zh",整个客户端半挂载失败。现在语言注册跟随 effect 生命周期释放;同时兼容已经残留旧注册的页面,遇到同一命名空间被占用时沿用现有字典继续挂载,无需强制刷新页面。@deepseek-ai/dsh-mcp-client 行(Magpie、其他插件清单、第三方导入工具)统一显示「第三方导入」,不再识别 magpie-mcp-* 前缀,新增导入工具无需改代码。~/.dsh/mcp.json、<workspace>/.dsh/mcp.json(以及旧版 mcp-servers.json 回退);检测到非空配置时显示升级横幅,点「查看并导入」逐条勾选。legacy 接口返回完整配置,import 接口按选择写入 profile patch。disabled: true,可勾选「导入后立即启用」;旧文件只读,从不修改或删除。cordis.patch.yml 并读取 Loader 状态;传输、重连、工具注册、资源和清理交给官方 @deepseek-ai/dsh-mcp-client。patch-store.js:通过 yaml 的 Document API 维护 id 为 dsh-mcp-panel-<serverName> 的 insert 行,保留无关条目的注释和 !!js 标签。serverName、transport、command、args、env、cwd、url、headers、toolCallTimeoutMs、reconnect、failOnStartupError、maxInstructionBytes;关闭时写入行级 disabled: true。profileContext 定位活动 profile 的 patch;缺失时显示提示并禁用管理操作。界面新增 Profile 徽标。hmr.runExclusive 事务中写入并 reconcileProfile,失败时恢复旧文本;没有 HMR 时返回 restart-required 并显示警告横幅。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 读写。npm test 覆盖 patch 文档读写、管理器操作与重名、Loader 行来源与外来行、安全校验,以及 client.js 的静态检查。以下 0.1.x 条目是历史版本的行为记录;其中进程托管、用户 / 工作区作用域、
mcp.json存储等能力已在 0.2.0 移除。
@deepseek-ai/dsh-mcp-client 是 DSH 自带插件,但具体条目可能由 Magpie 等第三方工具写入。列表现在按来源显示「面板」/「Magpie」/「插件清单」,展开详情会显示清单条目 id。mcp.json 条目,不再顺带停用同名插件清单条目。删除后列表会显示该插件清单条目(如 Magpie)及其真实启停状态,需要停用时用手动开关。删除面板配置时若有同名插件清单条目,确认框会提示这一点。magpie-mcp-* 识别为 Magpie)、删除面板配置后插件条目保持启用、同名不同参数时面板接管。graceful deadline exceeded 报错:插件的 reconcile 与宿主关闭存在竞态。关闭时若有一次 reconcile 还在读取配置/握手,它会在 dispose() 之后继续创建 MCP 子进程,这些子进程让事件循环无法退出,桌面端只能等到超时后用 SIGTERM 杀掉宿主。现在:
await 之后检查 disposed/代次,关闭后不再创建或重启任何 runner;dispose() 会等待进行中的 reconcile 结束,并异步等待所有子进程退出;taskkill /T /F 终止整棵进程树(只杀 cmd.exe 包装进程会遗留真正的 MCP 服务进程),所有等待定时器都 unref,无法阻止宿主退出;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
测试覆盖:
!!js 标签保留;mcp.json 的只读检测、摘要脱敏、选择性导入、默认禁用、重名拒绝,以及旧文件保持不变;client.js 的静态检查:语法、旧作用域标识已移除、新的 API 字段与迁移流程存在。profileContext;没有该上下文时只能显示提示,不能管理配置。mcp.json 只读用于升级提示和选择性导入,从不修改或删除。serverName 必须在面板管理的 profile 内唯一(^[A-Za-z0-9_-]{1,32}$);官方客户端会拒绝重复项。MIT License