ZT Blog
dev

给 DeepSeek Harness(dsh)添加 MCP 服务器:从 opencode.json 迁移实战

#mcp#deepseek#dsh#opencode

给 DeepSeek Harness(dsh)添加 MCP 服务器:从 opencode.json 迁移实战

DeepSeek Harness(CLI 命令 dsh,npm 包 @deepseek-ai/dsh)是 DeepSeek 官方在 2026 年 8 月开源的 Agent 框架,定位对标 Claude Code。其设计哲学是 "一切皆插件"——基于 Cordis 框架,所有能力(包括 MCP 客户端)都是插件。

本文承接上一篇《OpenCode 中让 MiniMax 两个 MCP 共存:Token Plan + 完整版》,把其中那两个 MCP 服务迁移到 dsh,并记录完整的踩坑过程。

封面

一、为什么不能直接用 opencode.json 的写法

opencode 的 MCP 配置是 JSON-键值对

{
  "mcp": {
    "MiniMax": {
      "type": "local",
      "command": ["uvx", "--from", "minimax-mcp", "--with", "mcp>=1.6.0,<2.0.0", "minimax-mcp"],
      "environment": { "MINIMAX_API_KEY": "..." },
      "enabled": true
    }
  }
}

dsh 的配置是 YAML patch 列表,每条 MCP 服务器是一个独立的插件实例:

- id: mcp-minimax
  name: '@deepseek-ai/dsh-mcp-client'
  config:
    serverName: minimax
    transport: stdio
    command: uvx
    args: [...]
    env:
      MINIMAX_API_KEY: "..."

核心差异:

维度 opencode dsh
配置格式 JSON YAML
MCP 客户端 内嵌在 mcp.<name> 字段 独立插件 @deepseek-ai/dsh-mcp-client
配置文件 ~/.config/opencode/opencode.json $DSH_HOME/cordis.patch.yml
工具命名 <server-name> 直接暴露 mcp__<serverName>__<toolName>(Claude Code / Codex 同款)
热重载 需重启 监听 patch 文件,保存即重连

二、前置条件

  • Node.js ≥ 16——npx 直接用,无需全局安装
  • uvx 已安装(uv tool install uvastral-sh/uv
  • 操作系统:Windows / macOS / Linux 均可

$DSH_HOME 默认值:

  • WindowsC:\Users\<YOU>\.dsh\
  • macOS / Linux~/.dsh/

也可通过环境变量覆盖:$env:DSH_HOME = "D:\custom\path"

三、第一步:触发首次初始化

dsh 的 web / headless profile 在首次使用时自动从模板初始化。如果不想打开 Web UI,可以用 --dump-default-config 安全地创建目录并打印 Bundle 层:

npx -y @deepseek-ai/dsh --profile web --dump-default-config

执行后 C:\Users\zengt\.dsh\ 会出现:

.dsh/
├── profiles/
│   ├── web/
│   │   ├── cordis.yml              # base 模板(不可手改)
│   │   ├── cordis.patch.yml        # profile 级 patch(空)
│   │   ├── package.json
│   │   └── pnpm-workspace.yaml
│   └── node_modules/
│       └── @deepseek-ai/dsh-mcp-client/   # MCP 客户端插件已就绪
└── sessions/

此时 @deepseek-ai/dsh-mcp-client 已经下载完毕,无需 pnpm add

四、第二步:写 cordis.patch.yml

home 级C:\Users\zengt\.dsh\cordis.patch.yml)写入如下内容。这样所有 profile 都会共享这些 MCP 服务器。

# DSH home-level MCP overrides.
# Layer precedence (lowest to highest):
#   bundles -> profile cordis.patch.yml -> THIS file -> --patch CLI overlay
# Edit & save; HMR reconnects without process restart.

- insert:
    - id: mcp-minimax
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: minimax
        transport: stdio
        command: uvx
        args:
          - --from
          - minimax-mcp
          - --with
          - mcp>=1.6.0,<2.0.0
          - minimax-mcp
        env:
          MINIMAX_API_KEY: "<YOUR_KEY>"
          MINIMAX_API_HOST: "https://api.minimaxi.com"
          MINIMAX_MCP_BASE_PATH: 'C:\Users\zengt\MiniMax-mcp-output'
          MINIMAX_API_RESOURCE_MODE: "local"

    - id: mcp-minimax-search
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: minimax-search
        transport: stdio
        command: uvx
        args:
          - --from
          - minimax-coding-plan-mcp
          - --with
          - mcp>=1.6.0,<2.0.0
          - minimax-coding-plan-mcp
          - -y
        env:
          MINIMAX_API_KEY: "<YOUR_KEY>"
          MINIMAX_API_HOST: "https://api.minimaxi.com"

关键字段说明

  1. - insert:——dsh 的 patch 语法,必须insert / replace / remove 显式声明操作。直接写 - id: mcp-minimax 会被当作"查找已存在条目",从而报 patch: entry not found
  2. serverName——MCP 工具命名前缀,[A-Za-z0-9_-]{1,32},在同一 dsh 实例内必须唯一。模型最终看到的工具名是 mcp__minimax__text_to_image / mcp__minimax-search__web_search
  3. transport: stdio——dsh 当前支持 stdiostreamable-http 两种 transport。
  4. MINIMAX_MCP_BASE_PATH——完整版 MCP 必填,所有生成的多媒体文件会落盘到此目录(YAML 单引号保留 Windows 反斜杠)。
  5. MINIMAX_API_RESOURCE_MODE: local——生成的文件返回本地路径而非 URL。

五、第三步:验证合并结果

npx -y @deepseek-ai/dsh --profile web --dump-config | Select-String -Pattern "mcp-minimax|dsh-mcp-client" -Context 0,12

应输出两条完整的 MCP 条目,包含 serverNametransportcommandargsenv 等字段。

六、第四步:启动 dsh

npx @deepseek-ai/dsh web

默认监听 http://127.0.0.1:3080。打开浏览器后新建会话,模型会自动把新发现的工具列出来:

mcp__minimax__text_to_audio
mcp__minimax__list_voices
mcp__minimax__voice_clone
mcp__minimax__music_generation
mcp__minimax__generate_video
mcp__minimax__text_to_image
mcp__minimax__search_understand_image
mcp__minimax__play_audio
mcp__minimax__voice_design
mcp__minimax__list_mcp_resources
mcp__minimax-search__web_search
mcp__minimax-search__understand_image

试着调用一下,确认 Python 隔离环境被正确激活:

调用 mcp__minimax__text_to_image,prompt 写 "a cute cat"

七、三层 patch 优先级

dsh 的配置像叠千层糕,按从低到高的优先级合并:

┌─────────────────────────────────────────────┐
│  --patch C:\temp\extra.yml   (CLI overlay)  │  ← 临时调试最高
├─────────────────────────────────────────────┤
│  $DSH_HOME/cordis.patch.yml   (home 级)     │  ← 机器所有 profile 共享
├─────────────────────────────────────────────┤
│  $DSH_HOME/profiles/<name>/cordis.patch.yml │  ← per-profile 用户覆盖
├─────────────────────────────────────────────┤
│  profile manifest 的 bundles[]  (内置)      │  ← 默认 base
└─────────────────────────────────────────────┘

后层覆盖前层同名条目。mcp-minimax 写在 home 级会让所有 profile 都启用它;如果只想要 web profile 用,写在 profiles/web/cordis.patch.yml 即可。

八、进阶用法

8.1 HMR 热重载

cordis.patch.yml 被监听。编辑保存即触发重连,无需重启 dsh:

  1. serverName——新工具名立即生效
  2. env.MINIMAX_API_KEY——下次调用即用新 Key
  3. 删一条 MCP 整块——该服务所有工具从模型面板消失

8.2 临时覆盖(不落盘)

npx @deepseek-ai/dsh web --patch C:\Users\zengt\Desktop\debug-mcp.yml

debug-mcp.yml 写法与上面相同,常用于临时换一组 Key 测试。

8.3 禁用 MCP 服务

直接删掉对应 - id: 块。下次 dump 即可看到该条目消失,没有 enabled: false 这种开关。

8.4 查 MCP 客户端源码

dsh 把 MCP 客户端实现放在 C:\Users\zengt\.dsh\profiles\node_modules\@deepseek-ai\dsh-mcp-client,想看重新连接策略、超时、命名规范化逻辑的话直接读。

九、故障排查

现象 原因 处理
patch: entry "mcp-minimax" not found 用了 - id: mcp-x 而不是 - insert: 最外层必须包裹 - insert:
编辑后无反应 缩进错了 YAML 用 2 空格缩进,参考上面例子
MINIMAX_MCP_BASE_PATH 报"目录不存在" 目录未创建 New-Item -ItemType Directory -Path "C:\Users\zengt\MiniMax-mcp-output"
spawn uvx ENOENT PATH 找不到 uvx command 里写绝对路径,如 "C:\Users\zengt\.local\bin\uvx.exe"
ModuleNotFoundError: No module named 'mcp.server.fastmcp' mcp>=2.0 破坏 API 保留 --with "mcp>=1.6.0,<2.0.0"(上文已加)
工具名变成 mcp__minimax__t_e_x_t__t_o__a_u_d_i_o 之类怪名 serverName 含非法字符 严格满足 [A-Za-z0-9_-]{1,32}
dsh 启动但 Web UI 打不开 首次启动在拉前端构建产物 等待 1-2 分钟,或确认 5080 / 443 端口未被占用

十、写在最后

dsh 仍是 Developer Preview(npm 版本 0.1.0-rc.x),官方明确说会有破坏性变更。本文的插件 ID 和 YAML schema 是基于 0.1.0-rc.6 编写,等正式版发布时建议先看 CHANGELOG 再升级。

密钥管理跟 opencode 那篇一样——不要在仓库里提交明文 Key。建议:

  • cordis.patch.yml 加进 .gitignore.dsh/ 目录整体忽略最简单)
  • 或者把 MINIMAX_API_KEY 放到用户级环境变量,YAML 里写 !!js process.env.MINIMAX_API_KEY(Cordis 支持 !!js 表达式)

下一篇会写《在 dsh 中开发自己的 Cordis 插件》——从 apply(ctx: Context) 开始,把上面的 MCP 客户端当模板。

相关链接