dshx
中文 | English
The missing companion CLI for DeepSeek Harness (dsh).
Manage MCP servers, skills, and agent memory with one command — dry-run connection checks before anything is written, secret-safe config output, and one-shot migration of your existing Claude Code / Codex setup (MCP servers, skills, and global memory). Ships with a SKILL.md so the dsh agent itself knows how to use it, and a /mcp command with an interactive card in dsh Web.

npm install -g @why913/dshx
dshx mcp add everything -- npx -y @modelcontextprotocol/server-everything
# 连接测试 everything … 通过(2133ms,发现 13 个工具: echo, get-env, …)
# 已写入 ~/.dsh/profiles/web/cordis.patch.yml(id: mcp-everything)
dshx mcp import --yes
# discovers every MCP server in ~/.claude.json, ./.mcp.json and
# ~/.codex/config.toml, connection-tests each one, writes the ones that workWhy
dsh's MCP client is solid (stdio + streamable-http, auto-reconnect, hot reload) — but the only way to configure it is hand-editing cordis.patch.yml. In our timed test, an experienced agent needed 6 min 24 s to add one server by hand (finding the file, learning the patch-layer semantics, dodging the []-placeholder YAML trap). Claude Code does the same job in one command. dshx closes that gap:
| hand-editing | dshx | |
|---|---|---|
| Add one server | ~6 min, YAML traps | one command |
| Broken server | discovered at boot, silently mounts zero tools | refused before writing (dry-run handshake + tools/list) |
| Secrets | pasted into YAML | $VAR → !!js process.env.VAR references |
| Migrating from Claude Code / Codex | re-type everything | dshx mcp import --yes, or one click |

One real import run on a machine with 12 servers across Claude Code and Codex configs: 10 migrated, 2 correctly rejected (one endpoint returning 403, one server crashing on startup) — before either could pollute the config. Note that npx is what downloads and runs the package; dshx checks that the result speaks MCP, and writes only when it does.
Install
npm install -g @why913/dshx # CLIOptional — mount it as a dsh plugin too, so the agent gets mcp_add / mcp_list / mcp_remove / mcp_test / mcp_import as native tools, plus the /mcp command and its card:
dsh plugin --profile web add @why913/dshxRecommended — install the skill so the agent reaches for dshx on its own:
dshx skill add ./skills/dshx # records the source, so `skill update` works laterdsh hot-watches the skills directory; no restart needed. In our test, the agent picked the skill up live, called mcp_list / mcp_test / mcp_import itself, and finished the whole task in 16 seconds.
Commands
dshx mcp add <name> -- <command> [args...] add a local stdio server
dshx mcp add --transport http <name> <url> add a remote streamable-http server
dshx mcp list list managed servers
dshx mcp rm <name> remove a server
dshx mcp test <name> dry-run handshake + tool listing
dshx mcp import [--yes] migrate servers from Claude Code / Codex
dshx skill list list skills with validity checks
dshx skill add <owner/repo[/subdir] | path> install a SKILL.md package (source + commit recorded)
dshx skill rm <name> remove a dshx-installed skill
dshx skill update <name> re-fetch from the recorded source
dshx skill import [--yes] migrate skills from ~/.claude/skills
dshx memory import [--yes] migrate ~/.claude/CLAUDE.md + ~/.codex/AGENTS.md
into $DSH_HOME/AGENTS.md (idempotent marker blocks)Notes: the skills directory is hot-watched by dsh, so skill add/import take effect immediately. Project-level CLAUDE.md needs no migration — dsh reads it natively. Imports preview by default and require --yes to write; a $VAR value becomes a reference, but a source config holding a literal token migrates that literal.
Shared flags:
| Flag | Meaning |
|---|---|
--profile <name> | target profile (default web) |
--global | write to $DSH_HOME/cordis.patch.yml (all profiles) |
--env KEY=$VAR | env var for stdio servers; $VAR form is stored as a !!js process.env.VAR reference — no secret lands in the file |
--header 'K: V' | header for http servers (values support $VAR too) |
--timeout <ms> | connection-test timeout (default 30000) |
--no-test | skip the dry-run connection test |
--force | overwrite an existing server of the same name |
--agents | install skills into ~/.agents/skills instead |
Design guarantees
- Dry-run before write.
addandimportperform a real MCP handshake plustools/list; an unreachable server is refused, not written. Same on every path — CLI, agent tool, card button. - Idempotent. Re-adding an existing
serverNamefails loudly (--forceto replace).rmonly ever touches rows dshx manages. - Comment-preserving YAML edits. Your
cordis.patch.ymlcomments survive every edit; removing the last server restores the pristine[]placeholder. - No secrets in files.
$VAR-form env/header values are written as!!js process.env.VARreferences, dsh's own idiom. - Never restarts anything. Changes apply on the next dsh reload; dshx tells you instead of killing your sessions.
- Skills checked before install. A missing
name/description, a non-kebab-case name, or the olddisableModelInvocationcamelCase key is refused — better than failing silently inside dsh.
As a dsh plugin
Mounted via dsh plugin --profile web add @why913/dshx, the agent gets five native tools (mcp_list, mcp_add, mcp_remove, mcp_test, mcp_import) with the same guarantees — so "connect me to the GitHub MCP server" is something the agent can just do, test included. Configure the target profile on the plugin row:
- id: dshx
name: '@why913/dshx'
config:
profile: webIn dsh Web
The same plugin adds /mcp, whose result renders as an interactive card:
/mcp
MCP 服务器 · 9/10 连通 [全部重测]
✓ codex 2 tools · 322ms [重测]
✓ playwright 24 tools · 7942ms [重测]
✗ node_repl 连接失败 · 60ms [重测]
MCP error -32000: Connection closed
/mcp import
可迁移 2 个 · 已管理 10 个 [全部迁移]
+ openai-docs claude-user · streamable-http · https://… [迁移]
+ obsidian claude-user · stdio · node …\main.js [迁移]
= codex 已管理| Form | What it does |
|---|---|
/mcp | check every configured server, one row each |
/mcp <server> | recheck one server and list its tool names |
/mcp import | list what is importable, already filtered against what dshx manages |
/mcp import <server> / /mcp import all | migrate, connection-tested first |
/mcp help | the forms above |
Buttons replay the command, so a recheck or an import lands as a fresh card — the command log is append-only. Without the client half installed, the same command still renders as plain text.
Limitations
- Slash commands are Web-only. The shipped
headlessCLI forwards its whole positional input to the model, sodsh --profile headless "/mcp"reaches the model, not the command registry. In a terminal, usedshx mcp …. - dsh's live connection state is not exposed to third-party plugins, so
/mcpopens its own diagnostic connection and reports that — it cannot show dsh's runtime connection or reconnect state. - No OAuth-authenticated MCP servers until dsh exposes an API for it.
- Config edits need a dsh reload; dshx never restarts anything for you.
- Editing one field of an existing server means re-adding it with
--force.
Roadmap
/dshx migrate: skills and global memory alongside MCP servers in one card- Skill/memory management as model-facing plugin tools (
skill_add,memory_import, …) - OAuth-authenticated MCP servers (see dsh-mcp-manager for a Web-UI approach)
Compatibility
DeepSeek Harness is in developer preview and its internals change fast. dshx only touches documented surfaces (patch files, the @deepseek-ai/dsh-mcp-client config schema, ctx.commands, and the conversation.chat.commandview slot) and is tested against @deepseek-ai/dsh 0.1.0-rc.6. @deepseek-ai/dsh-tools is a peer dependency, supplied by the host. Node ≥ 22.19.
An unofficial community project, not affiliated with DeepSeek.
License
[MIT](LICENSE)