@sduwall/dsh-wall-mcp-manager

toolsnpmMIT

A plugin for managing MCP servers in DeepSeek Harness.

下载量 / Downloads
688

Install

dsh plugin --profile web add @sduwall/dsh-wall-mcp-manager

Sandbox test

This plugin has not been tested yet. Submit a test →

README

@sduwall/dsh-wall-mcp-manager

DSH(DeepSeek Harness)插件:集中管理 MCP 服务配置,并展示每个服务提供的工具、

调用参数与返回契约。

  • 插件类型:Cordis Bundle(npm 包)
  • 依赖:@deepseek-ai/cordis、@deepseek-ai/cordis-plugin-loader、

@deepseek-ai/dsh-mcp-client、@deepseek-ai/dsh-settings、@deepseek-ai/dsh-tools

(peer,由 DSH 运行时提供)、@deepseek-ai/schemastery

  • 运行环境:Node.js ≥ 20(DSH 内置 Node ≥ 22)
  • 硬依赖服务:loader、tools(inject: ['loader', 'tools'])
  • 可选服务:settings(运行时改配置并热生效;缺失时按组合层配置工作)、

webServer(设置界面的配置桥;缺失时仍照常挂载 MCP,只是没有界面)

DSH 版本兼容

本插件 0.2.0+ 仅支持 DSH ≥ 0.1.2-alpha(对应 @deepseek-ai/dsh-settings ≥ 0.1.2-alpha.2)。

  • 旧版 DSH(≤ 0.1.1-rc.2)不兼容:该版本线移除/重构了设置面板 API,旧插件一加载即报

does not provide an export named 'installSettingsSection'。

  • 已验证的版本范围:0.1.2-alpha.2 起全部已发布版本(含 0.1.5-rc.2、0.1.6-alpha.2 等)设置 API 表面一致

(均为注入的 ctx.settings.installSection(owner, ns, schema, entry, hooks))。

  • 实机验证通过的版本(package.json 的 dsh.compatibility.verifiedDshVersions,以下均为真机加载验证:npx 实装 DSH、实际启动并确认 web 可访问、本插件成功加载且能正常拉起/管理 MCP server):
  • 0.1.2-alpha.2(兼容下限;0.1.2-alpha.3/.4/.5 与其 API 完全一致,已通过静态核对覆盖)
  • 0.1.5-rc.2(latest 稳定线)
  • 0.1.6-alpha.2(alpha 线)

以上版本已在本机经 npx 实机加载本插件验证可正常运行。

  • 校验方式:发布前运行 node scripts/verify-dsh-versions.mjs 逐个核对已发布版本接口,

任一不符即非零退出。若 DSH 未来再改接口,脚本先报警,届时再升插件大版本。

安装后亦可用 npm run verify:dsh 复检当前 DSH 版本是否仍兼容。

如需在旧版 DSH 上运行,请锁定 @sduwall/dsh-wall-mcp-manager@0.1.0。

它做什么

本插件自己不实现 MCP 协议,只做两件事:

1. 对账:把配置里的服务清单收敛成一组 @deepseek-ai/dsh-mcp-client 实例

(一个实例 = 一个 MCP 服务)——新增则挂载、改了则更新、删了则卸载、没变则一动不动;

2. 呈现:通过回环配置桥把「配置 + 挂载状态 + 工具契约」交给设置界面。

为什么是对账而不是「全拆重建」:重建会让所有 MCP 重连一次。用户改一个服务的

备注文字,不该把另外五个的 stdio 子进程全杀掉重启。判定依据是实例配置指纹

(toMcpConfig 的输出序列化),因此只改 description 这类非 mcp-client 字段不触发重连。

工具命名与 serverName

MCP 工具在 DSH 里的公开名是 mcp__<serverName>__<原始工具名>。因此:

  • servers 用字典而非数组,键即 serverName,让「重名」在数据结构上就不可能

(serverName 在所有活动 mcp-client 实例之间必须唯一,重复会在挂载阶段抛错);

  • serverName 受 [A-Za-z0-9_-]{1,32} 约束(与 mcp-client 内部常量一致),

非法名称在插件侧就被拦下并在界面显示原因,而不是只留一行日志;

  • 改名等于换一个服务:工具名会整体变化,模型侧的引用也随之改变。

对账时先卸载不再需要的 serverName、再挂载新的,避免改名瞬间在唯一性检查上撞车。

界面按 mcp__<serverName>__ 前缀归组工具(最长前缀优先,正确区分 fs 与 fs_ro),

不反解析工具名——publicToolName 在需要字符替换或截断时会追加哈希,其源码明确

"the public name is never parsed to recover it",但 serverName 段不会被改写。

配置

配置命名空间为 dsh-wall-mcp-manager(settings 命名空间只允许小写 kebab-case,

不带 npm scope),形如:

dsh-wall-mcp-manager:
  servers:
    filesystem:
      enabled: true
      transport: stdio
      description: 本地文件访问
      command: npx
      args:
        - -y
        - '@modelcontextprotocol/server-filesystem'
        - D:/workspace
      env:
        LANG: zh_CN
      secretEnv:             # 凭据类环境变量;没有凭据就整段省略,不要写成 {}
        API_TOKEN: xxx
      cwd: ''
      toolCallTimeoutMs: 60000
      failOnStartupError: false
    weather:
      transport: streamable-http
      url: https://mcp.example.com/mcp
      headers:
        X-Trace: 'on'
      secretHeaders:         # 凭据类请求头
        Authorization: Bearer xxx

字段 · 默认值 · 说明

enabled · false · 是否启动该 MCP;默认不启动,勾选界面「启动」才挂载(保留配置)

transport · stdio · stdio(本地子进程)或 streamable-http(远程 HTTP)

description · '' · 备注,仅本界面展示,不传给 mcp-client

command · '' · stdio 必填:启动命令

args · [] · stdio:命令参数,逐项填写(不会按空格拆分)

env · {} · stdio:环境变量(明文,可在界面查看)

secretEnv · 无 · stdio:凭据类环境变量,role('secret'),永不出网

cwd · '' · stdio:工作目录,留空继承 DSH 进程

url · '' · streamable-http 必填:服务地址

headers · {} · streamable-http:请求头(明文,可在界面查看)

secretHeaders · 无 · streamable-http:凭据类请求头,role('secret'),永不出网

toolCallTimeoutMs · 60000 · 工具调用超时(毫秒,≥1000)

failOnStartupError · false · 启动失败是否让该实例整体失败

secretEnv / secretHeaders 在挂载时被合并进 env / headers(同名以凭据为准)——

对 mcp-client 而言它们和普通环境变量、普通请求头没有区别,拆成两个字段只是因为

明文项要能在界面查看编辑,而凭据项必须只写不读,两者需要不同的 role。

两个凭据字段没有默认值(显式 .default(undefined)):界面判断「已配置 / 未配置」

依据的是脱敏视图里的 set 标记,而它按「值是否为 undefined」判定;schemastery 会给

每个 dict 无条件补上 {} 默认值,若不压回 undefined,从没填过的凭据槽位也会显示

「已配置」。因此手写 yaml 时不要写 secretEnv: {},没有凭据就整段省略。

为什么不照抄 mcp-client 的 Schema

mcp-client 自身的 Config 是 z.union([stdio, streamable-http]),但 settings 的

redactSecrets 只穿透 object / dict / array 三种容器,遇到 union 会原样返回整棵子树

(其源码注释明确 "a secret buried inside a union branch is not reachable and must not be

modeled that way")。若按 union 建模,凭据就会随 describe 原文出网。

因此这里用扁平 object + transport 判别字段,挂载时再由 toMcpConfig 按分支重新

组装出合法的实例配置——只输出该分支该有的字段,多带一个别分支字段会被 union 直接拒绝。

半成品不影响其他服务

「填了一半的服务」在配置界面里是常态。校验失败(缺 command、名称非法、

transport 不支持)只让该项被跳过并在界面标出原因,不会让整份配置或其他服务失效。

单个服务挂载失败(命令不存在、远端不可达)同样只记为该项的失败,绝不外溢成插件整体卸载。

设置界面

插件注册了 settings.section 一级页面「MCP 服务」。界面由浏览器端 bundle

(lib/client.js)渲染,通过宿主自建的回环配置桥读写:

路由(均为 POST) · 用途

/api/dsh-wall-mcp-manager/describe · 脱敏配置视图(含 revision 与 schema)

/api/dsh-wall-mcp-manager/mutate · 按 { ops, expectedRevision } 精确改字段

/api/dsh-wall-mcp-manager/servers · 每个服务的挂载状态(fiber 相位 / 跳过原因 / 失败原因)

/api/dsh-wall-mcp-manager/tools · 按服务归组的工具清单,含参数与拆包后的返回 schema

  • 只接受本机回环来源(127.0.0.1 / ::1 / ::ffff:127.*)。该桥能写入 MCP 的

启动命令与凭据,权限等价于「在宿主上执行任意进程」;DSH 的 webserver 允许绑定

0.0.0.0,那种部署下不做来源校验等于把远程命令执行接口暴露到整个网段。

  • 凭据永不出网:describe 走 redactSecrets: true,整字典标 secret 后连键名都不返回,

界面只能显示「已配置 / 未配置」。凭据框留空保存表示不修改,这是既支持修改又不

回传原文的唯一语义;要清除请点「清除凭据」。

  • 删除需二次确认:点「删除」只置位待确认,界面就地展开确认条,说明将断开的连接、

移除的工具数,以及凭据会被一并清除且无法恢复;再点「确认删除」才真正写入 unset。

凭据从不回显,误删一次只能重新去拿密钥,这类不可逆操作不能一键完成。

  • 写入按字段 set / unset(不整体替换),带 revision 乐观锁。整体替换会把浏览器

从未见过的 secretEnv / secretHeaders 一并抹掉;乐观锁则保证手改 yaml 与界面并发

修改不会互相覆盖。校验失败与修订冲突返回 ok:false,界面保留草稿供修正。

  • /servers 与 /tools 读的是运行时事实,不依赖 settings 服务,headless 之外的

只读展示也可用。未提供数据源钩子时这两条路由干脆不注册(宁可 404 也不回假数据)。

  • 工具卡片展示调用参数与返回契约。mcp-client 给每个工具的返回 schema 套了固定包装

{ content, structuredContent },界面会拆包:声明了 outputSchema 的工具展示其真实

结构

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::1,最后更新 2026-09-19

Links

GitHub · Homepage · npm

Same category