Back to marketplace

dsh-mcp-manager-ui

Integrations

Imzl-zl/dsh-mcp-manager-ui

MCP server management UI for DeepSeek Harness Web: floating panel for managing global and project-level MCP servers, with JSON import and profile-backed persistence.

  • cordis
  • deepseek-harness
  • dsh
  • dsh-plugin
  • mcp
GitHub Stars
14GitHub
Views
0DSH Plugin Hub
Forks
0GitHub
Open issues
0GitHub Issues
Manifest version
1.1.8dsh-mcp-manager-ui
Latest push
Sep 2, 2026GitHub
License
MITJavaScript
Plugin type
Host + ClientRuns in both Host and Web Client

Verification and compatibility

This section shows evidence collected by the catalog. Undeclared information is labeled as unknown.

Runtime verified
01Exact source: github · dsh-mcp-manager-ui@1.1.802Validated: Sep 3, 202603Verified Harness: 0.1.0-rc.7
Current-version compatibility
Verified on the catalog Harness version
Declared Harness range
Not declared
Declared platforms
Not declared
Profiles
web
Build approval
No requirement detected
Permissions
Not declared
External services
Not declared
Telemetry
Unknown
No known risk flags found

This is not a security endorsement. Review source, permissions, and configuration before installing.

View evidence and scope

Verification covers only the named source, version, and Harness environment. It does not guarantee future compatibility.

  • github:Imzl-zl/dsh-mcp-manager-ui#a2a729f3606d4e3de2bc102f33428511fed4f40f
  • The plugin completed a load check in an isolated environment.

README

View source

dsh-mcp-manager-ui

DeepSeek Harness Web 的 MCP 管理面板。它在 Web Host 中运行一份,通过右下角悬浮按钮(可拖拽)管理全局 MCP(Web profile)与各项目的项目级 MCP(.dsh/mcp.json)。

界面预览

全局管理面板

MCP 管理面板

项目作用域(.dsh/mcp.json

项目 MCP

截图是同一项目开两个会话时的真实状态:memory 标「已连接(本项目会话共享)」「2 个会话共用」,工具正常枚举——两个会话共用同一份连接,不再出现 serverName is already in use

连接详情与操作

MCP 连接详情

新增 MCP

新增 MCP

功能

  • 查看 MCP 状态、传输方式、连接参数和工具列表
  • 展开每个工具查看完整输入 JSON Schema:必填/可选参数、类型、枚举、默认值与原始 JSON
  • 按传输方式(HTTP/stdio)和连接状态筛选,支持按名称/命令/URL 搜索
  • 添加时一键套用常用预设模板(Filesystem、Memory、Sequential Thinking 等)
  • 从“内置 MCP”目录查看 Exa、Tavily、Firecrawl、Chrome DevTools 和 Playwright,勾选后按需追加;已有配置只识别并跳过,不会覆盖
  • 全局 + 项目双作用域:顶部标签页在「全局」与各项目之间切换;全局 MCP 一次注册所有项目可用,项目级 MCP 写入项目目录 .dsh/mcp.json 仅该项目会话可见
  • 项目级补充:在项目标签页添加/编辑/移除只写该项目 .dsh/mcp.json;项目可用「屏蔽」隐藏某个全局 MCP(写 exclude,新会话不再看到)
  • 全局注册共用的、项目级补充项目特有的:共用 MCP(Exa、GitHub、Chrome DevTools 等)全局注册一次,所有项目直接可用,无需每个项目重复配置
  • serverName 全局唯一(含所有项目),冲突在保存时提示被哪个作用域占用
  • 项目 MCP 由该项目的所有会话共享一份连接(复用官方 @deepseek-ai/dsh-mcp-client,支持惰性连接与自动重连):同一项目开多少个会话都能用,不会互相占用 serverName,也无需手动重连;会话中修改项目配置不会热更新,下一次会话生效(与主流一致,详见配置生效时机
  • 显示并复制已解密的 URL 凭据、args、env、headers 值(会话内临时可见)
  • 启用、禁用、重连、添加、编辑和移除 MCP
  • 跟随 DSH 深色/浅色主题,并适配窄屏和移动宽度
  • 支持 DSH rc.7+ 的完整 MCP 连接字段:commandargsenvcwdurlheaders、调用超时、启动失败策略和重连策略
  • 导入 Claude、Cursor、Cline、Roo 等使用的 mcpServers JSON,以及 VS Code 的 servers JSON
  • JSON 导入支持“合并(同名更新)”和“替换”,写入前提供预览
  • 结构化修改 Web profile 的 cordis.patch.yml,保留其他插件条目、注释和 !!js 环境变量表达式
  • Host Remote 与 Web 客户端均随插件生命周期加载和卸载
  • 非强制更新提示:面板打开时 Host 每天最多向 GitHub Releases 查询一次最新版本,有新版时在面板顶部显示可关闭的提示条;查询失败静默、绝不自动更新,可设环境变量 DSH_MCP_MANAGER_DISABLE_UPDATE_CHECK 关闭,除该查询外不发送任何数据

配置生效时机(重要)

两类作用域的生效机制不同,这是有意设计,与主流 Agent 客户端一致:

作用域存储位置修改后何时生效
全局Web profile 的 cordis.patch.ymlDSH 热加载,通常立即生效(含运行中的会话)
项目项目目录 .dsh/mcp.json下一次新建的连接生效;正在运行的会话不受影响(见下方共享连接的生效边界)

项目 MCP 在会话创建/恢复时按当时的 .dsh/mcp.json 装配到该会话,会话进行中不重读配置——会话里改配置不生效是预期行为,Claude Code、Codex 等客户端的项目级 MCP 同样要求新开会话。「屏蔽」全局 MCP 的可见性变更同理,只对之后的会话生效。

改完配置不需要点「重连」也不需要重启 Host(项目 MCP 的重连由 mcp-client 自己管),新开会话即可。

注意(共享连接下的生效边界):项目 MCP 是「该项目所有会话共用一份连接」的模型,连接由最先打开该项目会话时的配置建立。所以「新开一个会话」并不总等于「用上新配置」:

  • 该项目已经没有会话在跑 → 新会话会新建连接,立刻用上新配置。
  • 该项目还有会话在跑 → 新会话复用现有连接,沿用旧配置。此时面板会在该项目行标出 配置待生效,详情页给出说明;等该项目所有会话都结束后,下一个会话才会用新配置建连。

面板提示只是如实告知,不会静默;本插件不会在运行中替换连接——那会把工具从正在对话的会话脚下抽走。

项目 MCP 的共享连接模型

同一项目的多个会话共用一份项目 MCP 连接:

  • 每个 (项目目录, serverName) 在整个 dsh web 进程内只启动一份 mcp-client 实例,因此 serverName 只登记一次,并发会话不会撞名(并发建连与释放/重建均做了串行化:建连 promise 先入表、释放保留占位直到连接完全销毁)。
  • 该连接注册出的工具会投射进每个属于该项目的会话自己的工具层,所以每个会话都能看到并调用;其他项目的会话看不到(隔离保留,默认不可见、显式投射,不依赖“事后屏蔽”)。
  • 引用计数管理生命周期:该项目第一个会话建立连接,最后一个会话结束后释放;会话销毁与插件卸载都会等到连接真正关闭。
  • 引用所有权完全交给 cordis:每一份引用由一个 agentCtx.effect() 唯一持有。因此两个边界情形都不需要本插件另建一套存活性判定:会话在建连期间被销毁时(dsh-agent-loopraceAbort 会抛弃 setup 但不取消它),effect()assertActive() 当场抛出,引用当场归还;会话正常结束时由 cordis 跑 disposer,并因为它是异步 disposer 而被 Fiber._unload 等待。插件 HMR 卸载是另一个纤度的事实(插件代次),由它自己的令牌判定,不与会话存活性共用同一张表。
  • 为什么必须共享serverName 同时是进程内唯一的注册名和模型可见工具名 mcp__<serverName>__* 的前缀。「每会话各起一份」既会撞名,也不能靠“每会话换个名字”绕过——换名等于换工具名,会话恢复时的历史工具调用和 prompt 缓存都会失效。

与 MCP 规范的关系(按版本说清楚)

  • 规范 2026-07-28 修订版新增了 Statelessness 一节:服务器 MUST NOT 依赖同一连接上的先前请求建立上下文,SHOULD 准备好处理来自多个任务/线程/会话的请求,客户端 SHOULD NOT 把单个任务/会话当作 stdio 进程的生命周期边界。按这一版,共享连接 + 并发多路复用正是被鼓励的形态,而“每会话一个子进程”反倒是被劝阻的。
  • 但随 DSH 分发的 @modelcontextprotocol/sdk 目前协商的是 2025-11-25,那一版没有 Statelessness 一节,取而代之的是 Lifecycle Management(含 session control)。也就是说:按 2025-11-25 实现的服务器完全可以合理地维护连接级会话状态,这不算它的缺陷。
  • 传输层的并发安全是有保障的:一个 Client 实例的请求 id 单调唯一(SDK 的 _requestMessageId++)、响应按 id 路由,stdio 一次 write 写整帧,所以多个会话在同一连接上交织调用不会串线。DSH 是单进程多会话,因此不需要生态里那些代理方案的 shim/broker/socket 和请求 id 重映射。

不适合共享的服务器(重要,本宿主没有 per-session 逃生舱)

把会话身份隐式绑在连接/进程上的服务器(浏览器自动化、SSH 会话、编辑器缓冲区、按连接建索引等)在多会话共享时会串状态。

生态里的代理方案通常提供 shared / isolated / session-aware 三档开关(如 mcp-muxjasonwarta/mcp-muxpunt-labs/mcp-proxy)。本宿主给不了 isolated 这一档,原因就是上面那条:per-session 隔离必须 per-session 换 serverName,而那会连带换掉工具名。所以请如实理解:

  • 这类服务器在项目作用域下不被支持。把它挪到全局作用域也没用(那只是从“本项目所有会话共享”变成“所有项目所有会话共享”,隔离更差);在项目里另起一个 serverName 同样无效(serverName 区分的是服务器,不是会话)。
  • 可行的做法:让该服务器改用 streamable-http 并自己按请求参数分区状态,或者用一个外部代理(上面那几个项目)在 DSH 之外做隔离。
  • 规范给出的正解是 session-aware:状态跨请求时用请求里显式传的标识符引用(2026-07-28 的 “State that needs to span multiple requests MUST be referenced by an explicit identifier the client passes on each request”,实践上就是 _meta 里带会话 id,mcp-mux 的 _meta.muxSessionId 就是这么做的)。当前 dsh-mcp-client 不注入任何 per-request _meta,所以共享连接对服务端是匿名的;等上游支持按调用注入会话标识后,这一档才能补上。

生态里的同类问题与同方向实践:Claude Code 每会话各起进程导致的内存压力(claude-code#28860,Anthropic 侧的 shared-daemon 提案,已关为 duplicate)、Serena 在多客户端打开同一项目时的重复实例与并发写问题(serena#1235,Serena 自己给多 agent 场景的建议是改用 HTTP/SSE)。DSH 是单进程多会话,能直接在进程内共享,不需要 daemon 或代理。

已知限制(重要,请阅读)

  • 首轮就绪时序:项目 MCP 默认异步建连,新会话的首轮对话可能还未就绪,第二轮起可用。若服务器配置了 failOnStartupError: true,会等待连接确认后才继续创建会话(与 mcp-client 全局行为一致)。
  • 屏蔽不释放命名:「屏蔽」全局 MCP 只隐藏其工具,该 serverName 的全局实例仍在运行并占用命名,项目内不能通过同名服务器接管;如需接管请先在全局禁用/移除该服务器。
  • 全局与项目不能同名serverName 在整个进程内唯一,项目级不能与全局或其他项目用同一个名字;保存时会提示被哪个作用域占用。这一校验在保存路径上,手工编辑 .dsh/mcp.json(或项目不在工作区注册表里)能绕过它;那时第二份连接会启动失败,面板会在该项目行标 serverName 被占用 并列出和哪些项目撞了(该会话拿不到这个 MCP 的工具,fail-closed)。
  • 共享连接与配置粘性:见上「配置生效时机」的注意——运行中连接沿用首会话配置,全部会话结束后新连接才用新配置;期间面板标 配置待生效
  • 不支持 per-session 隔离:见上「不适合共享的服务器」。
  • 关掉最后一个会话后立刻重开会稍等:新连接要等旧连接完全销毁才建(避免撞名),这段等待取决于 MCP 服务端退出的快慢。上界约 9 秒:MCP SDK 的 stdio 关闭本身最多等 2s(stdin 关掉)+ 2s(SIGTERM)再 SIGKILL,mcp-client 对关闭确认又有 5 秒上限。同一会话的多个 server 是并行释放的,不累加。实测正常服务器远低于这个上界(Windows、SDK 1.30.0):transport.close()@modelcontextprotocol/server-memory 35ms、mcp-deepwiki 43ms、fast-context-mcp 34ms、serena 167ms;整个 dsh web 进程的优雅退出(同时拆 4 个 stdio + 2 个 HTTP 连接)约 0.5s。慢的前提是服务器不理 stdin EOF,见下一条。
  • Windows:忽略 stdin EOF 的 stdio 服务器会漏孙进程。stdio 服务器在 Windows 上通常是一条进程链(npx 解析成 npx.cmd,于是 dsh → cmd.exe → node),而 MCP SDK 的 StdioClientTransport.close() 只对直接子进程发 SIGTERM/SIGKILL(sdk/dist/esm/client/stdio.jsclose()),没有 job object,孙进程不在射程内。实测常见服务器(memory、deepwiki、fast-context、serena、chrome-devtools)都在 stdin EOF 时自行退出,因此 DSH 正常退出与被强杀都不残留进程;但这份干净来自服务器行为,不是 transport 的保证——故意忽略 stdin EOF 的服务器会让 close() 吃满 4 秒(2s + 2s)并留下一个孤儿孙进程。遇到这类服务器请让它自己处理退出,或改用 streamable-http
  • DSH 的退出宽限是 5 秒:官方启动器在 SIGINT/SIGTERM 后只给整棵插件树 5 秒(dsh/lib/profile-boot-*.jsPROCESS_SHUTDOWN_TIMEOUT_MS),超时就 process.exit()。正常情形绰绰有余(实测 ~0.5s),但若同时有多个“退得慢”的服务器,退出可能在 teardown 完成前被强行截止。
  • 插件热重载会清空运行中会话的项目工具:HMR/卸载时会撤回所有投射并释放连接(否则会留下指向已销毁连接的僵尸工具)。已在运行的会话要重新拿到项目 MCP 工具需新开会话。

兼容性

项目已验证版本
DeepSeek Harness0.1.0-rc.7 及以上(已验证至 0.1.0-rc.8
Node.jsDSH 自带/支持的运行时
平台Windows;Linux/macOS 使用同一 DSH Web 契约

内置 MCP

插件安装和 Web Host 启动都不会自动写入任何 MCP。打开管理面板后,点击顶部工具栏中位于“导入 JSON”和“添加 MCP”之间的“内置 MCP”,可以查看目录、勾选未配置项并一次安装。

MCP默认配置无密钥使用范围本地要求
Exahttps://mcp.exa.ai/mcp匿名限额;可另配 API Key 提升额度
Tavilyhttps://mcp.tavily.com/mcp/ + X-Tavily-Access-Mode: keyless限额 Search / Extract;免费账号可提供更高额度
Firecrawlhttps://mcp.firecrawl.dev/v2/mcp限额 Search / Scrape / Parse;完整工具需要登录或 API Key
Chrome DevToolsnpx -y chrome-devtools-mcp@latest本地工具,无 API 额度Node.js、Chrome
Playwrightnpx -y @playwright/mcp@latest本地工具,无 API 额度Node.js 20+、可用浏览器

目录会按 serverName、官方 HTTP 主机名和官方 npm 包识别当前有效配置,包括来自其他 bundle、Agent preset 或 mcp-remote 桥接的同类项。已存在项会显示其配置名称并禁用勾选;Host 在真正写入前还会在文件锁内再次判重,只追加当时仍缺失的所选项,不更新、不替换用户配置。用户主动移除某项后,只有再次勾选安装才会恢复。

DSH 宿主 API 通过 peerDependencies^0.1.0-rc.7 声明,自动兼容 0.1.0-rc.70.2.0 之前的所有版本(含后续 RC 与 0.1.x 正式版)。开发与测试环境跟随同一范围,升级 DSH 后用 pnpm update && npm test 验证即可,无需改版本号。0.2.0 属于新的兼容边界,需要重新验证后再放宽。

安装

使用 DSH 插件命令安装。不要把 mcp-manager-ui 再手工插入 Web profile 的 cordis.patch.yml

# 正式使用固定 release tag。
dsh plugin --profile web add github:Imzl-zl/dsh-mcp-manager-ui#v1.1.8

安装、升级、卸载和本地开发流程见 安装与升级

安装后重启 dsh web。插件命令会同时完成两件事:

  1. 把包加入 Web profile 的 dependencies
  2. dsh-mcp-manager-ui 加入 dsh.profile.bundles

仓库自己的 cordis.patch.yml 已经声明唯一的 Host 条目:

- insert:
    - id: mcp-manager-ui
      name: dsh-mcp-manager-ui

不要在以下位置重复这段条目:

  • ~/.dsh/profiles/web/cordis.patch.yml
  • 任意 Agent preset 的 agent.cordis.yml
  • 额外的 --patch 文件

本插件也不需要全局安装 @deepseek-ai/dsh-tool-cordis。需要临时开发 Cordis 插件时,直接新建“创造模式”会话。

卸载:

dsh plugin --profile web remove dsh-mcp-manager-ui

JSON 兼容范围

DSH rc.7 原生支持两种 MCP transport:

  • stdiocommandargsenvcwd
  • streamable-httpurlheaders

导入器会识别 httpstreamable-httpstreamableHttp 等常见别名,并把 ${TOKEN}${env:TOKEN} 转成 DSH 的 !!js process.env.TOKEN 表达式。DSH 当前不支持的 SSE、WebSocket、OAuth、headersHelperenvFile 等字段会明确报错或提示,不会静默生成不可用配置。

其他 Agent 的 directTools 可以是 truefalse 或缺失。DSH 没有间接工具模式并始终把 MCP 工具注册为 mcp__<server>__<tool>,因此导入器采用保守映射:true 转成 disabled: falsefalse 转成 disabled: true,缺失时不干预现有启停状态;同时存在显式 disabled 时以后者为准。预览会逐项提示这些转换。

“替换”只替换当前 Web profile 的 cordis.patch.yml 中由 @deepseek-ai/dsh-mcp-client 声明的条目,不会删除其他 bundle 或 Agent preset 自带的 MCP。

完整格式、两种导入模式、启停映射和密钥处理见 JSON 导入

文档

开发流程

  1. 在“创造模式”中用 cordis_inspectcordis_definecordis_run 做临时验证。
  2. 将确认后的实现写入本仓库。临时动态插件不会自动生成源码文件,也不会在 DSH 重启后恢复。
  3. 停止临时动态版本,避免它与仓库版本同时注册 UI 或 Remote。
  4. 使用本地路径执行 dsh plugin --profile web add ...,验证正式 bundle。
  5. 运行测试并启动 Web 做真实操作验证。
npm test
dsh --profile web --dump-config
dsh web

包结构

  • package.json:声明 dsh.bundle 和 Web dsh.client
  • cordis.patch.yml:插入唯一的 Host 插件实例
  • lib/index.jsmcpManager Host Remote
  • lib/mcp-registry.js:loader 中 MCP 条目的枚举与工具归属推断
  • lib/workspace-runtime.js:项目配置读写状态、按 (项目, serverName) 引用计数的共享 mcp-client 连接,以及把其工具投射进每个会话作用域
  • lib/workspace-config.js:项目级 .dsh/mcp.json 的读写与转换
  • lib/mcp-config.js:JSON 规范化与 YAML patch 结构化读写
  • lib/mcp-observability.js:连接状态判定与 mcp-client 日志格式化
  • lib/client.js:响应式 Web UI、Remote 客户端和生命周期清理
  • lib/typert.js:Remote 契约描述

lib/ 是预构建产物,GitHub、tarball 和 npm 安装均不需要执行构建脚本。

连接状态语义

@deepseek-ai/dsh-mcp-client 不对外暴露连接成功/失败事件。面板因此用两条官方事实拼出状态:已注册的工具数cordis fiber 的状态代号。日志只用来填失败原因的文案,不参与判定。

  • 已连接(connected):只有该 server 的工具已注册(mcp__<server>__* 数量 > 0)才判定为已连接。插件 fiber 处于 ACTIVE 只说明 mcp-client 在跑,不能证明握手成功——failOnStartupError: false(默认)时连接失败也会让 fiber 保持 ACTIVE。
  • 连接失败(failed):fiber 已 ACTIVE(mcp-client 的 apply 要等首次连接与 tools/list 结束才让 fiber ACTIVE)却没有任何工具,或者 fiber 本身处于失败态。具体原因取自 mcp-client 最近的日志(通过 ctx.logger.exporter 订阅并按 mcp-client(<serverName>) 过滤),例如 connection attempt failed: ECONNREFUSEDgiving up after 10 consecutive failed reconnect attempts;拿不到日志时就如实写“未注册任何工具”。
  • 连接中(loading):fiber 尚未 ACTIVE(还在跑 apply)。不猜测成功也不猜测失败。
  • 已停止(stopped):没有 fiber。全局意为条目未加载;项目语境里意为「尚无会话持有这份共享连接」,面板显示为「待会话挂载」。

全局与项目行走的是同一个判定函数mcp-observability.deriveMcpPhase)与同一个取值域,只有文案不同(项目行的 connected 写作「已连接(本项目会话共享)」、stopped 写作「待会话挂载」)。面板还会在项目行标出 配置待生效(配置改过但仍在复用旧连接)与 N 个会话共用。两类失败分开告知,不混为一谈:

  • 挂载失败mountFailed):本插件在会话 setup 阶段就挂不上(配置里的 ${VAR} 求值为空、failOnStartupError: true 下启动失败、工具注册被拒等)。
  • 连接失败status === 'failed'):mcp-client 那边的事。两者由 Host 分开标记,客户端不再用「lastError 存在」反推挂载失败。

项目 MCP 的工具注册在它自己的共享作用域层里,全局工具视图看不到,所以面板按该作用域枚举(不是走全局 tools.schemas())。

只读诊断接口(排障用)

面板每行只回答得了「这个项目的这个 server 怎么了」。进程级的问题(一共有几条共享连接、有没有引用卡住不归零)由一个只读 RPC 回答:

mcpManager/projectConnections → { connections: [{ wsPath, serverName, state, refs, sessions, toolCount, fiberState, configStale, configError, duplicateOwners }] }
  • stateready(已就绪)/ connecting(建连中,还没有连接态可读)/ disposing(释放中,占位未清)。卡在后两种状态不走才是最需要排障的形态,所以它们也如实出现在列表里。
  • refssessions两个独立事实:前者是引用计数,后者是真实持有它的存活会话数。健康时二者相等;refs > sessions 就是漏了引用(会话已销毁但引用没归还),后果是连接永不释放、配置永远刷不新。接口不把两者合成一个“健康”布尔,判读留给使用者。
  • configStalenull 表示无从判定(还没建连,或配置里已经没有这个 server),不伪造 false;读配置失败时原因在 configError
  • 全程只读:不改引用计数、不碰 fiber、不触发建连或释放。面板目前不接线,它是给排障留的接口。

面板在详情页和编辑表单中默认掩码敏感值(URL 凭据、args、env、headers),点击眼睛图标后经 Host 的 reveal 接口读取有效运行值并在会话内临时显示;编辑时若未实际修改输入,保存仍保留原配置引用,不会把环境变量密钥写回 profile。该读取只对当前 Web profile 管理的 server 开放。

设计约束

dsh-mcp-manager-ui 是 Web Host 单实例插件。固定的 Remote namespace 和 UI slot id 是有意设计;重复加载属于配置错误,插件会明确失败,而不是静默忽略。多个 MCP server 则由 @deepseek-ai/dsh-mcp-client 的不同 serverName 实例管理。

相关链接

License

MIT

Comments

0
Newest first