Back to home

wuhobin

dsh-mcp-manage

dsh plugin: Settings > MCP 服务 management page for DeepSeek Harness (DSH). List/add/edit/delete MCP servers registered in cordis.patch.yml and run a real MCP initialize connection handshake per server.

Stars
0
Language
JavaScript
Created
Aug 17, 2026
Updated
Aug 17, 2026

Introduction

dsh-mcp-manage

Label: dsh plugin · A DeepSeek Harness (DSH) static market plugin that adds an MCP 服务 management page in Settings.

Manage the MCP servers DSH registers through cordis.patch.yml: list / add / edit / delete each MCP service, and — most importantly — run a real MCP initialize connection handshake against every server so you can see at a glance whether each MCP service connects normally.

dsh-plugins mcp mit


Install — one command, restart-persistent

This is a static DSH plugin (the same shape as a plugin-market package): once installed it is a dependency of the profile and is loaded by the bundle layer on every boot — it survives restarts, no pasting code, no cordis_define.

From anywhere a package is installed into a DSH profile:

# link straight from this repo
pnpm add github:wuhobin/dsh-mcp-manage   # or an npm registry entry when published

# restart the dsh process (e.g. `dsh web`), then open Settings → MCP 服务

The package carries a dsh.bundle.patch (static/cordis.patch.yml) that inserts

- insert:
    - id: dsh-mcp-manage
      name: 'dsh-mcp-manage'

so DSH's bundle layer turns it into an active loader entry automatically. You only need to add the dependency once; the UI page and routes appear after a restart.

You can also install it through the built-in 插件市场 (plugin market, dshmarket) once this package is registered in a market registry — the market's pnpm add path is the same mechanism underneath.


TL;DR (what you get)

  • A Settings → MCP 服务 section (settings.section → id mcp-manage, order 30).
  • One card per configured MCP server: id / serverName / transport / command-or-url / headers-or-env plus a status badge (已连接 / 可达 / 异常 / 未知).
  • 检测 (probe one) / 重新检测全部 run a real MCP initialize handshake, not a ping:
    • streamable-http → a real authenticated POST initialize session via the official MCP SDK.
    • stdio → actually spawns the server command and completes an MCP initialize over stdio.
  • 添加服务 / 编辑 / 删除 persist changes straight back to cordis.patch.yml (insert:-blocks for @deepseek-ai/dsh-mcp-client).

Architecture — this is a real static plugin

The dynamic plugin the author originally shipped used the dynamic-runner sandbox APIs (harness.handle / host.call), which only exist inside dsh-cordis-*-runner. A static plugin runs in the normal (non-sandbox) plane and gets no harness/host.call; it uses the real services instead. This package is ported to that model:

layerfiledoes what
Host (Node)static/index.jsESM Cordis plugin exporting name + apply(ctx, config). ctx.inject(['webServer'], …) mounts HTTP routes /dsh-mcp/list, /dsh-mcp/check, /dsh-mcp/save. Uses real node:fs / node:os / node:child_process.
Client (browser)static/client.jsA window.__ModuleLoader__.load({ id, factory }) bundle (only external is react). Registers settings.section via ctx.slots.inject(...) and fetch()es the host routes.
Bundle patchstatic/cordis.patch.ymldsh.bundle.patch → inserts { id: dsh-mcp-manage, name: 'dsh-mcp-manage' } so the loader activates it.
Probeprobe/dsh-mcp-probe.cjsStandalone real-MCP initialize handshake (official @modelcontextprotocol/sdk), self-terminating.

Host routes

routemethodpurpose
/dsh-mcp/listGETlist servers + the auto-detected profile patch path
/dsh-mcp/checkGETrun a real handshake per server → {id, status, message} each
/dsh-mcp/savePOSTpersist the edited server list back to cordis.patch.yml (same-origin only)

Auto-detection (no hardcoded paths)

The host derives the runtime layout the same way the dynamic host did, but with real Node:

  • DSH_HOME (an existing .dsh directory) or HOME<profilesRoot>.
  • profilesRoot = <DSH_HOME>/profiles (or <HOME>/.dsh/profiles).
  • SDK_ROOT = <profilesRoot>/node_modules.
  • PATCH_PATH = first <profilesRoot>/<p>/cordis.patch.yml whose content references @deepseek-ai/dsh-mcp-client; PATCH_DIR = its directory, where dsh-mcp-probe.cjs is written.

Package manifest (what makes it market-installable)

package.json satisfies the exact contracts the plugin market and the client loader validate:

  • main./static/index.js (host entry artifact).
  • exports["./client"]./static/client.js (browser bundle).
  • dsh.client.platform: "web" (+ optional inject).
  • dsh.bundle.patchstatic/cordis.patch.yml.
  • peerDependencies["@deepseek-ai/cordis"]^4.0.1.

These match what dshmarket itself ships and what dsh-client-modules requires (parseDshClient needs platform:"web", clientExportOf needs exports["./client"]).


Layout

dsh-mcp-manage/
├── package.json              # static-plugin manifest (dsh.bundle.patch + dsh.client.platform + main/exports)
├── README.md
├── LICENSE                   # MIT
├── static/
│   ├── index.js              # Host: ESM Cordis plugin → webServer routes list/check/save
│   ├── client.js             # Client: __ModuleLoader__.load bundle → Settings → MCP 服务 UI
│   └── cordis.patch.yml      # dsh.bundle.patch → inserts { id, name } into the composed entry list
├── probe/
│   └── dsh-mcp-probe.cjs     # Standalone real-MCP-initialize handshake
└── test-parse.js             # YAML-subset parse test used during development

plugin/ (the original dynamic harness.handle/host.call version) is kept in the git history for reference; the installable package uses static/. If you want the dynamic, session-only variant instead, see the plugin/*.js source in an earlier commit.


Standalone probe (no DSH needed)

# stdio server
node probe/dsh-mcp-probe.cjs "{\"sdkRoot\":\"/abs/path/node_modules\",\"transport\":\"stdio\",\"command\":\"/abs/path/server\",\"args\":[\"stdio\"],\"env\":{\"TOKEN\":\"...\"},\"timeoutMs\":15000}"

# streamable-http server
node probe/dsh-mcp-probe.cjs "{\"sdkRoot\":\"/abs/path/node_modules\",\"transport\":\"streamable-http\",\"url\":\"https://host/mcp\",\"headers\":{\"Authorization\":\"Bearer ...\"},\"timeoutMs\":15000}"

probe is also wired as an npm script: npm run probe -- "<spec-json>".

Credentials (e.g. Authorization: Bearer …, GITHUB_PERSONAL_ACCESS_TOKEN) go only into the probe process; the result reports only ok / name / ver / error, never the headers.


Acknowledgements

  • DeepSeek Harness for the static-plugin load path (dsh.bundle.patch / dsh.client / webServer / client-modules).
  • Model Context Protocol SDK (@modelcontextprotocol/sdk) for the real initialize handshake.
  • Cordis for the plugin/event/service framework.