前言¶
DSH 的理念是「一切皆插件」,MCP 服务器通常也是以插件形式挂进 profile 的。实际用下来有一个反复出现的问题:profile 里同时挂着多个 MCP 服务器,某次对话某条 mcp__<server>__* 工具调不通了,你很难快速判断是「这台服务器压根没连上」还是「连上之后又死了」。@deepseek-ai/dsh-mcp-client 本身不暴露连接状态,fiber 是 active 的不代表服务器活着,工具注册表里还有条目也不代表现在还通。
dsh-mcp-status 解决的就是这件事:在 Web GUI 侧边栏把每个 MCP 服务器的连接状态、工具数量、配置摊开,并支持原地开关和重连。下面介绍这个插件。
这是什么¶
sonettogu14/dsh-mcp-status(维护者 SonettoGu14,分类客户端)是 DeepSeek Harness Web GUI 的 MCP 服务器面板插件。它把宿主侧 ctx.loader.entries() 里所有 @deepseek-ai/dsh-mcp-client 行列出来——profile 补丁、home 补丁、插件包插入的行都算,与宿主实际在跑的一致——并给出连接状态、工具数量和配置,允许在不离开面板的前提下停用 / 启用、重试连接、单行测试连接。
它回答三个问题:有哪些 MCP 服务器;连上了没有;没连上的话为什么。
核心功能¶
1、侧边栏列出每个 MCP 服务器,显示状态、工具数、传输与地址、来源补丁文件、超时与重连策略。状态由四份证据组合推导,不是客户端自报。
2、原地开关与重连。开关只改写当前 profile 补丁文件里那一行的 disabled 字段:按缩进定位到 - id: <行 id> 之后的块,插入或删除一行 disabled: true,其余字节原样保留,采用临时文件 + rename 的原子写(保留原权限位)。写完后补丁监听会热重载该行,重载即重新连接;8 秒内没连上会主动 hmr.reconcileProfile([id]) 再排一次。不在可编辑补丁文件里的行(例如插件包插入的 drawio)显示「内置」,开关不可用。
3、「测试连接」用宿主同一份配置真做一次 JSON-RPC 握手(stdio 起进程 / http 打请求),把 ECONNREFUSED、HTTP 401、超时原文贴回行内。
4、状态同时喂给 system prompt 摘要与调用闸门:有服务器不可用时注册一个最多 6 行的健康摘要;mcp__<server>__* 的调用在派发前被判为不可用则拒绝,并给出可读原因。
面板提供四个同源路由:
GET /api/dsh-mcp-status/servers # 快照:状态、工具数、传输与地址、来源补丁文件
POST /api/dsh-mcp-status/toggle # { id, enabled }:改补丁行,等热重载,返回新状态
POST /api/dsh-mcp-status/retry # { id }:重新挂载这一行
POST /api/dsh-mcp-status/test # { id }:真握手,返回协议版本 / serverInfo / 工具数 / 失败原因
写操作要求同源证明,所有路由只接受回环来源。
状态判定¶
状态由 lib/status.js 的 resolveStatus() 纯函数推导,每条结论都带依据,优先级从上到下:
| 顺序 | 条件 | 状态 | reason |
|---|---|---|---|
| 1 | 行被停用 | 已停用 | disabled |
| 2 | loader fiber 失败 | 加载失败 | fiber-failed |
| 3 | fiber 未就绪 | 启动中 | pending |
| 4 | 最近一次测试失败,且端口不通或结论在 probeAssertMs 内 |
不可达 | probe-failed |
| 5 | streamable-http 端口拒绝连接 / 超时 | 不可达 | tcp-refused |
| 6 | 最近一次测试成功 | 已连接 | connected |
| 7 | 工具表读不到 | 已连接 | unknown-tools(不下结论) |
| 8 | 有工具 / 没有工具 | 已连接 / 未连接 | connected / no-tools |
第 8 条的 no-tools 刻意与 unreachable 分开:工具数为 0 可能只是这行挂在 per-agent 作用域、全局注册表看不见,当断线处理会误报也会误拦。每行 DTO 带 reason 与 evidence(fiber / tools / tcp / probe 四个信号及时间戳),展开区把依据压成一行。
调用闸门只有证据够新才拦:快照超过 2 × refreshMs 视为过期,pending、no-tools、disabled 一律放行。idle 不等于断线。
安装与启用¶
README 给出四种装法,任选其一:
# 1. npm(推荐):预构建包,不需要构建授权
dsh plugin --profile <profile> add dsh-mcp-status
# 2. 本地目录(开发用)
dsh plugin --profile <profile> add link:<本目录>
# 3. 从 GitHub
dsh plugin --profile <profile> add github:SonettoGu14/dsh-mcp-status
第四种是在 Harness 里用插件管理器的 install_bundle 指向本目录或本仓库。npm 包页:https://www.npmjs.com/package/dsh-mcp-status
安装后在侧边栏点「MCP 服务器」即可。卸载:
dsh plugin --profile <profile> remove dsh-mcp-status
配置与典型用法¶
配置写在本插件自己的 patch 行里:
- id: mcp-status
name: dsh-mcp-status
config:
refreshMs: 10000 # 后台状态刷新周期(≥ 2000)
startupWaitMs: 8000 # 开关/重试后等待连接就绪的上限
probeAssertMs: 60000 # 测试失败后,结论压过实时探活的窗口
probeTimeoutMs: 15000 # 「测试连接」整次超时
promptHealth: true # 是否把不可用写进 system prompt
guardUnreachable: true # 是否拦截发往不可用服务器的工具调用
典型操作流程:侧边栏点开某行,看状态徽标;是黄的(启动中)或红的(未连接 / 不可达 / 加载失败)时点「测试连接」,行内会贴回握手失败原文;确认是配置或进程问题后直接点开关或重试,不必再去翻补丁文件。系统 prompt 侧不用额外操作,不可用摘要会在健康时自动注销。
面板只读配置,不做新增与编辑;新增和编辑仍在「设置 → 插件 → MCP 服务器配置」。
兼容与依赖¶
- 需要宿主组合了
webServer(Web GUI)与@deepseek-ai/dsh-mcp-client(MCP 客户端),两者都是 DSH 自带包。 - 包版本 0.2.0,许可证 MIT,要求 Node
>=22.19.0。 - 不依赖任何第三方运行时包:宿主半体只用 Node 内置模块,探测模块自己实现 MCP 握手。官方
@deepseek-ai/*包一个都没有 import,因此package.json里没有dependencies/peerDependencies。
适用场景与注意¶
适合用 DSH Web GUI、profile 里挂多个 MCP 服务器的人,用来替代「翻补丁文件 + 猜连接状态」的做法。需要留意:
- 插件以当前 dsh 进程的权限运行,安装前应先检查源码与许可证(MIT)。
- 停用 / 启用会改写当前 profile 补丁文件里那一行;如果该行由其它工具生成(例如同步 MCP 配置的第三方工具),那个工具下次整表重写时可能覆盖本次开关结果。
- stdio 行没有端口可探,存活只能靠工具数 + 手动「测试连接」的结论。
- 闸门可能误拦,尽管只拦「快照新鲜 + 端口确实拒绝连接 / 刚测过失败」;不放心就设
guardUnreachable: false,退回纯看板行为。 - 状态是推导的,不是客户端自报的。真要让状态像 opencode 那样由客户端层拥有,需要上游
dsh-mcp-client提供接口。
结尾¶
dsh-mcp-status 的价值在于把「MCP 服务器到底通不通」变成一个面板上可见、可操作的事实,而不是靠 fiber 状态和工具注册表间接猜。信息密度集中在状态判定表与四份证据上,开关写补丁的方式也刻意保守,不破坏原文件。
目录页:https://www.skillhub.cn/plugins/SonettoGu14/dsh-mcp-status
GitHub:https://github.com/SonettoGu14/dsh-mcp-status