dsh-mcpserver-manager
在 DeepSeek Harness 的「设置 → MCP 管理」里管理 MCP 服务器:粘贴一段 JSON → 双重校验 → 写入 DSH 官方的 MCP 维护配置文件,立刻在列表里回显。
配置写在 cordis.patch.yml(DSH 原生 MCP 配置的同一个文件),不是插件私有的 JSON——卸载插件后配置依然在,与宿主原生 MCP 配置同源。
它能做什么
- 粘贴一段 JSON 就能接入:弹窗里贴配置,输入即时校验,通过后才写盘,写完列表立刻回显并高亮 1.5 秒
- 写入官方配置文件:
~/.dsh/profiles/web/cordis.patch.yml,按name: '@deepseek-ai/dsh-mcp-client'的官方条目格式插入,宿主patchReload: live自动热重载,不用重启 - 绝不破坏你的配置文件:受管段落 + 文本级行编辑 + 原子写 +
.bak备份 + 失败自动回滚,段外一个字节都不动 - 前后端同一套校验规则:前后端共用同一个
src/validate.js,前端判过的一定能过后端;JSON 语法错能报出「第 N 行第 M 列」 - env 脱敏:列表与回显里只出现
env的 key(GITHUB_TOKEN ••••),明文永不越过 Host 边界 - 停用/启用可逆:用 cordis 原生
disabled: true,停用后卡片置灰标「已停用」,再启用完整恢复(含env与!!js表达式) - 看得见别人写的 MCP:用户手写或其它插件写入的条目也会列出来,归到「非本插件管理 · 只读」分组,只展示不改写
- 零运行时依赖:
dependencies为空,构建期只用一个esbuild,客户端 bundle 约 37KB
界面
- 列表页:标题行写明配置会写到哪里;每张卡片显示状态点、serverName、传输方式 chip、命令行(过长单行截断、悬浮看全量)、环境 key 与超时
- 空态:图标 + 主副文案 + 「添加 MCP」按钮——目标文件通常一条 MCP 都没有,空态是默认路径而不是异常
- 添加弹窗:等宽 JSON 编辑器 + 「填入示例 ▾」(stdio / streamable-http 两套)+ 实时错误区 + 写入位置提示
- 三态齐备:加载中骨架屏、空态、失败可重试 banner,不会出现「不知道是没数据还是没加载完」
- 主题原生:全部色值走
--dsw-alias-*设计变量并带 fallback,卡片用设置页语言(settings-card-fill+settings-card-stroke),没有任何 JS 明暗判断
安装
方式一:插件页直接添加(推荐)
左侧导航点 插件
右上角点 添加插件
输入框粘贴本仓库地址:
https://github.com/liuleiyu121520/dsh-mcpserver-manager点 安装,完成后在 设置 → MCP 管理 里使用
方式二:从 DSH Hub 安装
在 DSH Hub 插件页 点安装,按页面给出的命令执行。
方式三:命令行
dsh plugin --profile <你的 profile> add github:liuleiyu121520/dsh-mcpserver-manager
需要固定版本时加 # 后缀:
dsh plugin --profile <你的 profile> add github:liuleiyu121520/dsh-mcpserver-manager#v0.1.0
卸载
dsh plugin --profile <你的 profile> remove dsh-mcpserver-manager
卸载后本页消失,但已写入的 MCP 条目仍然留在 cordis.patch.yml 里(它们是 DSH 原生配置,不属于插件)。
用法
- 打开 设置 → MCP 管理
- 点 添加 MCP,在编辑器里粘贴一段配置(或点「填入示例 ▾」选一套模板改)
- 错误区为空后点 确定 → 配置写入 → 列表刷新 → 新条目高亮
- 卡片上可直接 停用 / 启用;点 ⋯ 可 删除(二次确认)
示例配置:stdio(本地命令)
{
"serverName": "github",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "!!js process.env.GITHUB_TOKEN" }
}
示例配置:streamable-http(远程服务)
{
"serverName": "remote",
"transport": "streamable-http",
"url": "https://example.com/mcp",
"headers": { "Authorization": "Bearer your-token" }
}
env的值写成!!js process.env.X时会被原样写入(cordis 的私有 tag,运行时才求值),这样 token 不必以明文躺在配置文件里。普通字符串会被加上单引号。
字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
serverName |
✅ | 字母/数字/下划线/连字符,1–32 字符;是工具 namespace,全局唯一 |
transport |
✅ | stdio 或 streamable-http |
command |
stdio 必填 | 可执行文件 |
args |
— | 字符串数组 |
env |
— | 「字符串: 字符串」字典 |
cwd |
— | 工作目录 |
url |
http 必填 | http:// 或 https:// 且能被解析 |
headers |
— | 「字符串: 字符串」字典 |
toolCallTimeoutMs |
— | 1–600000 整数,默认 60000 |
maxInstructionBytes |
— | 正整数,默认 32768 |
failOnStartupError |
— | 布尔,默认 false |
不在白名单里的字段不会丢弃,而是给出黄色警告后原样写入——丢弃会静默吃掉未来官方新增的字段。
写入位置与安全
写入文件:
$DSH_HOME/profiles/<profile>/cordis.patch.yml,默认即~/.dsh/profiles/web/cordis.patch.yml受管段:本插件只维护下面这段注释之间的内容,段外内容一个字节都不改:
# >>> dsh-mcpserver-manager:managed:begin (由「设置 → MCP 管理」维护,请勿手改) - id: 'mcp-github' name: '@deepseek-ai/dsh-mcp-client' config: serverName: 'github' transport: 'stdio' command: 'npx' args: - '-y' - '@modelcontextprotocol/server-github' toolCallTimeoutMs: 60000 maxInstructionBytes: 32768 failOnStartupError: false # <<< dsh-mcpserver-manager:managed:end请勿手改这两个锚点之间、以及锚点本身。锚点缺失一半、顺序颠倒或重复出现时,页面会切成只读并提示手工修复;你把条目挪到段外它就会变成「非本插件管理 · 只读」。
写入流程:写前落
cordis.patch.yml.bak→ 写cordis.patch.yml.tmp→fsync→rename原子替换;任一步失败自动把.bak拷回并提示「文件已回滚」手工恢复:
cp ~/.dsh/profiles/web/cordis.patch.yml.bak ~/.dsh/profiles/web/cordis.patch.yml只有一份
.bak(覆盖式),所以误操作两次后要尽快恢复。并发与竞态:写操作在 Host 进程内串行化,且唯一性判断发生在落盘前的最后一次扫描上;文件在读后写前被外部改动会被 checksum 拦下并返回「配置文件已被外部修改,请刷新后重试」
env 值脱敏:
GET /servers与页面永不回显 env 明文——无论条目由谁写入(本插件、你手写、或其它工具生成的块/流/引号键/行尾注释等形态),env 子树的值一律显示为••••,只有 key 可见。唯一例外是!!js process.env.TOKEN这类引用表达式:它本身不是明文,会原样显示,宿主在启动 MCP 子进程时才求值。明文只存在于落盘文件里(MCP 服务自己要读),不进浏览器脱敏边界:对「非本插件管理 · 只读」的不可解析条目,页面会逐字展示原文(这样你才能看懂并手工处理它),而脱敏只作用于能识别的
env子树——若你把 token 写在 env 之外的位置(比如塞进某段自由文本里),它会随原文显示。请把秘密只放在env里
配置
插件自身的可选项写在 cordis.patch.yml 的条目 config 里:
| 参数 | 默认值 | 说明 |
|---|---|---|
profile |
web |
要维护的 profile(profiles/<profile>/) |
backup |
true |
写前是否落 .bak |
dshHome |
未设置 | 显式指定 DSH 根目录;优先级高于 $DSH_HOME |
路径解析优先级:显式 config.dshHome > $DSH_HOME > ~/.dsh。空字符串或纯空白的 $DSH_HOME 视为未设置(不会解析成当前工作目录)。
示例:
- id: dsh-mcpserver-manager
name: 'dsh-mcpserver-manager'
config:
profile: web
backup: true
权限与副作用
- 只读写 profile 下的
cordis.patch.yml(及它的.bak/.tmp) - 不发起任何外部网络请求;不读取
env的值(只解析 key),明文 token 不进日志、不进 HTTP 响应 - 不重启宿主;写盘后由宿主的
patchReload: live自行热重载
工作原理
┌──────── Client 半(esbuild → lib/client.js 单文件 CJS)────────┐
│ launchpad(slots.inject) → McpManager → Toolbar / EmptyState / │
│ ServerList → ServerCard → AddDialog / ConfirmDialog │
│ 共享 ../validate.js(同源校验)· api.js · styles.js(CSS) │
└──────────────────────────┬────────────────────────────────────┘
HTTP /api/dsh-mcpserver-manager/*
┌──────────────────────────┴──────── Host 半(Node ESM)─────────┐
│ index.js → routes.js(守卫 + 4 端点)→ patch-store.js │
│ ├→ paths.js(路径解析) │
│ ├→ serialize.js(→ YAML) │
│ └→ validate.js(同源校验) │
└────────────────────────────────────────────────────────────────┘
为什么不用 YAML 库:目标文件里可能有用户注释和 cordis 私有的 !!js tag,任何「解析 → 改对象 → 全量 dump」都会把 !!js process.env.TOKEN 静默写成字面量——那是不可逆的用户资产损坏。所以本插件自研了一个受管段感知的行编辑器:只在自己的锚点段内做字符串拼接式增删,段外字节不变(这一点由单测逐断言覆盖)。
为什么停用用 disabled: true:已核实 cordis 的 @deepseek-ai/cordis-plugin-loader 在启动前会检查 fiber.entry.disabled,原生键可用,比注释搬运更"官方"、也更可逆。
开发
npm install # 只装 esbuild(devDependency),运行时依赖为 0
npm run build # 生成 lib/index.js 与 lib/client.js
npm run check # 全流程跑进临时目录,只校验不落盘
npm test # node:test,不需要任何测试框架
lib/ 是构建产物,本仓库的 .gitignore 里忽略它——发布时由维护者在本地 npm run build 之后再把 lib/ 带上(DSH 直接读 lib/index.js 与 lib/client.js,仓库里没有产物就装不起来)。
本地试用:
npm run build
ln -s "$(pwd)" ~/.dsh/profiles/web/node_modules/dsh-mcpserver-manager
发版流程
# 1. 改 package.json 的 version,重新构建
npm run build && npm run check && npm test
# 2. 提交推送(lib/ 是构建产物,但**必须随仓库分发**——DSH 从 github:owner/repo
# 安装插件时不执行构建,仓库里没有 lib/ 就装不起来)
# 3. 在 GitHub 上基于该提交创建 Release,tag 用 vX.Y.Z
已知限制
- 编辑已存在的条目是 P1(后端
replaceEntry()已实现并单测,路由与 UI 未暴露) - 搜索与状态筛选是 P1(Toolbar 未渲染占位控件)
- 「N 个工具」「测试连接」依赖宿主是否暴露 MCP 运行时查询服务,未暴露则整条不展示
- v0.1.0 只做中文文案
- 只维护一个 profile(默认
web) - 解析能力边界:只认顶层序列项 + 2 空格键 + 更深的
config子块 + 流样式。块标量、锚点引用、多文档等形状会降级为「无法解析 · 只读」并展示原文,不报错、不写、不猜
许可
MIT — 见 LICENSE。
No comments yet. Be the first to write one.