dsh-mcp-kimicodeandmgr
MCP engine and manager for DeepSeek Harness. Fork of yangfch3/dsh-mcp-mgr, MCP layer rebuilt on kimi-code's architecture: self-contained engine, three config layers, transport-driven status.
- Stars
- 1
- Language
- TypeScript
- Created
- Aug 21, 2026
- Updated
- Aug 21, 2026
Introduction
dsh-mcp-kimicodeandmgr
English | 中文 · Usage guide
An MCP engine and manager for DeepSeek Harness. It owns the whole MCP path itself — config resolution, transports, connection lifecycle, OAuth, and tool registration — and exposes it as a Settings tab.
About this fork
This is a fork of yangfch3/dsh-mcp-mgr, which provided the original plugin: workspace mcp.json discovery, hot sync, and the web settings UI.
The MCP layer has since been rewritten to follow the architecture used by Kimi Code (Moonshot AI). Two deliberate departures from the original:
- A self-contained MCP engine. The upstream plugin delegated to
@deepseek-ai/dsh-mcp-client; this fork builds directly on@modelcontextprotocol/sdkand owns transports, the connection lifecycle, and OAuth itself. That is what makes honest per-server status and real transport events possible. - Project-level configuration, as Kimi Code does it. Three config layers instead of one, with the two project layers gated on explicit workspace trust.
Kimi Code's source is not redistributed here — it was studied as a reference. Credit for the MCP layering, the status model, and the tool-naming scheme belongs to that project.
Features
- Three transports:
stdio, Streamable HTTP (http), and legacysse - Three config layers:
~/.dsh/mcp.json<<git root>/.mcp.json<<cwd>/.dsh/mcp.json, with the two project layers gated on workspace trust - Honest status:
pending/connected/failed/disabled/needs-auth/removed, driven by real transport events rather than inferred from the tool registry - OAuth: dynamic client registration, PKCE, a loopback callback server, and a CSRF state check; tokens live in
~/.dsh/credentials/mcp.json - Per-call recovery: a tool call that hits a dropped connection probes liveness, retries once, then reconnects and re-sends
- Hot reload: file watching publishes a
{ upsert, remove }diff, so editing one server never churns the others - Tools are registered as
mcp__<serverName>__<tool>

Scope
The plugin is two packages with different reach:
| Package | Role | Platform |
|---|---|---|
dsh-mcp-mgr | the MCP engine: config, transports, connections, OAuth, tool registration | no platform declared |
dsh-mcp-mgr-ui | the Settings tab | web only |
So the Settings tab exists only in a web profile. The engine declares no
platform and only injects tools, so it should also run in a non-web profile —
registering mcp__* tools with mcp.json edited by hand — but that is read from
the code, not tested: everything here has only been exercised in the web
profile.
Install
This fork is not published to npm. The names dsh-mcp-mgr and dsh-mcp-mgr-ui on npm belong to the upstream project and still resolve to its 0.1.x line, so plugin add dsh-mcp-mgr@latest would install upstream, not this. Install from source:
git clone https://github.com/danhcng3822f/dsh-mcp-kimicodeandmgr
cd dsh-mcp-kimicodeandmgr
pnpm install
DSH_REPO=<deepseek-harness checkout> pnpm build
node scripts/install.mjs # --profile <name>, default: web
scripts/install.mjs links both packages into $DSH_HOME/profiles/node_modules and adds the loader rows to that profile's cordis.patch.yml. It requires the profile to exist already, and it is idempotent.
Then restart the Web GUI and open Settings → MCP servers:
npx @deepseek-ai/dsh web
Uninstall:
node scripts/uninstall.mjs # --profile <name>, default: web
See USAGE.md for the full guide.
Configuration
Layers, lowest precedence first. A later layer overrides an earlier entry of the same name.
| Layer | Shown in the UI as | Path | Writable from the UI |
|---|---|---|---|
| user | Global | ~/.dsh/mcp.json (or $DSH_HOME/mcp.json) | yes |
| project root | Project | <git root>/.mcp.json | no |
| project local | Local | <cwd>/.dsh/mcp.json | no |
The project-root layer exists only when the workspace is inside a git repository; outside one it is skipped entirely. Both project layers load only after you trust the workspace in the Settings tab, because a project-level stdio entry runs a command supplied by the repository. Until then they are neither loaded nor watched. Trust decisions live in ~/.dsh/trusted-workspaces.json.
{
"mcpServers": {
"unity": { "url": "http://localhost:8090/mcp" },
"filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"] },
"legacy": { "transport": "sse", "url": "https://example.com/sse" },
"paid": { "url": "https://api.example.com/mcp", "auth": "oauth" },
"token": { "url": "https://api.example.com/mcp", "bearerTokenEnvVar": "MY_MCP_TOKEN" }
}
}
- A missing
transportis inferred:commandmeansstdio,urlmeanshttp. A legacy SSE endpoint therefore needs an explicit"transport": "sse"— see USAGE.md - Per-entry
enabled,startupTimeoutMs,toolTimeoutMs,enabledTools,disabledTools - Remote entries take
headers,bearerTokenEnvVar(the variable name, never the token), andauth: "oauth" - With no
cwdan stdio child runs in the workspace directory. A relativecwdin a project layer resolves against the file that declared it, so a repository can ship a working entry without knowing where it was cloned; in the user layer it is passed through as written and resolves against the workspace - Secrets never reach the browser: the Remote surface projects
envtoenvKeysandheaderstoheaderKeys - The parent environment is scrubbed before an stdio child starts (anything matching
KEY|PASSWORD|SECRET|TOKEN, plusDSH_*); an entry's ownenvis merged on top, so a deliberately configured credential survives
Development
pnpm install
DSH_REPO=<deepseek-harness checkout> pnpm build # tsc host → gen.mjs → tsc ui → tsdown
DSH_REPO=<deepseek-harness checkout> pnpm verify
verify.mjs runs the engine against real MCP servers, never mocks:
| Fixture | What it covers |
|---|---|
fixture-mcp-server.mjs | stdio: tool discovery, env passthrough, structured results, isError, a crashing child for a genuine disconnect |
fixture-mcp-http-server.mjs | Streamable HTTP: bearer injection, 401 routing |
fixture-mcp-sse-server.mjs | legacy SSE: the two-endpoint handshake, and a killed server the transport never reports |
fixture-oauth-server.mjs | dynamic registration, PKCE, the state check, the token exchange, and the authorized reconnect |
The tool bridge is checked against both a hand-rolled registry and the real harness ToolRuntime, including a full ctx.tools.execute() dispatch. The last sections mount the built package into a real Cordis context, so the plugin wiring, the mcpMgr service key, teardown, and the reload semantics are covered too.
After changing src/types.ts (wire fields), rerun gen.mjs and rebuild the UI bundle — the strict client codec silently strips unknown fields.
Local source install verification:
node scripts/install.mjs
node scripts/uninstall.mjs
License
MIT.
packages/vendor-typert-protocol is a vendored copy of the Typert protocol source from DeepSeek Harness, also MIT.