Repository avatar
Other Tools
v0.7.0
active

dsh-any-agent

io.github.xmtlzzz/dsh-any-agent

Drive DeepSeek Harness (DSH) as a long-lived, resumable coding agent from any MCP client.

Documentation

dsh-any-agent — a blue-haired chibi whale-girl eating white rice beside a coding laptop

Give any MCP client a coding buddy that sticks around.

Website · English · 简体中文

npm version node license dependencies tests

Website · Quick start · Features · Tools · Live view · Usage notes · Changelog

🧪 Offline suite green in CI on Linux, Windows and macOS · 👀 new live call-chain view (watch / dashboard, with token usage) · 🔌 MCP over stdio · 📦 Zero dependencies · 📜 MIT

An MCP server that drives DeepSeek Harness as a long-lived coding agent. Hand it a goal, let it write code and run scripts, steer it mid-flight, and collect the result when you are ready.

Claude Code · Claude Desktop · Codex · Cursor · Windsurf · Zed · Gemini CLI · Antigravity · opencode · VS Code — or any client that speaks MCP over stdio.

🎯 The problem this solves

You already have a coding agent in your editor. What you do not have is one that can:

  • hand a multi-hour job to another agent and walk away — then collect the result when it's done;
  • keep the job alive across restarts of your editor, your session, your machine's idle reap (the conversation always survives; for a turn that is still running to survive the client closing or reconnecting, turn on resident process mode, DSH_MCP_DAEMON=1);
  • cut in mid-flight when it's going the wrong way, without destroying the work;
  • run several of them at once and switch between them;
  • see what it is doing without asking the agent — which tools it called, which approvals went through, how many tokens it spent.

DSH (DeepSeek Harness) can do all of that — it has file/shell tools, skills, subagents and workflows — but it is a GUI-first application. Its own sdk profile exposes only 3 requests and can't resume, cancel, or answer approvals.

This server is the missing adapter: it makes DSH into a well-behaved background agent that any MCP client can hire, supervise and collect from.

Think of it as &job — like & in a shell, but for an agent.

✨ Why it's different

CapabilityWhat you get
🔄 A dead process never kills a conversationThe identity lives on disk. A crash, an idle reap, even restarting this whole server costs you nothing — the next call transparently resumes it, memory intact (verified across processes).
🗣️ You are never stuck waitingSteer a conversation mid-flight — interject to stop and redirect (~100 ms to reach idle, measured over four cancelled turns), or queue a remark for after the current turn. Interrupting never damages the conversation.
📡 Completion notification, not pollingLong tasks return instantly with a run_id; when the turn ends, a sentinel file is written atomically so a background task in your host wakes you. No polling, no occupied turn.
🪟 Many conversations at oncedsh_list(only_running) shows what's live; dsh_read gives cursor-based incremental output. Cycle between them like windows.
👀 You can see it yourselfdsh-any-agent watch (terminal) or dsh-any-agent dashboard (local read-only web page) lays dispatched work out as a call chain — client → conversation → run → tool calls / approvals — with per-run tokens and context-window use, updating while the run is going. Separate processes, never touch the write lock, and show work from every client together.
🧠 Reasoning hidden by defaultYou get "thought for N chars / M seconds" statistics instead of context-burning prose — and it is never logged, never written to disk.
🤫 Silent by defaultNot one byte on stderr, so nothing pollutes your host's logs. stdout carries the protocol and nothing else.
🧩 Capabilities composeDSH can mount its own MCP servers (IDA Pro, browsers, your internal tooling), so this is a bridge to a whole toolbox.
🛡️ Read-only that really is read-onlyread-only sandbox plus auto-denied approvals, for working on untrusted samples.
🔎 Caller review, not blind relayEvery result carries a review ticket (checklist + evidence + dsh_review entry). The caller verifies with its own tools, then records pass / partial / fail. Advisory by default — it never blocks the next dispatch, and a failed review is not silently fed back to DSH.

🧭 Positioning: a dispatcher, not a capability aggregator

This server dispatches; it does not re-implement what DSH already does. The caller gives a goal, and DSH decides which tools, skills, subagents or team to use. Nothing from DSH's tool surface is proxied, duplicated or preloaded.

Owned by DSH (this server stays out)Owned by this server (dispatch semantics)
File I/O, shell, search, editingSession identity and cross-crash resume
Skills, subagents, Agent Teams, task boardAsync dispatch, receipts, completion sentinels
Model routing, sandbox, approval policyIncremental read, interject, interrupt, live call-chain view
Memory/context/compactionApproval bridge, write-lock governance, queue, inbox
worktrees (the agent creates them with git)Setup, status, doctor, audit

Dispatch on demand: dsh_send passes a goal, not a tool manifest, and the caller does not need to know DSH's tool list. The DSH agent picks what it needs — including capabilities added after this server was built (for example the experimental Agent Teams). This server does not grow one MCP tool per DSH feature.

Team mode (opt-in since 0.5.0): off by default. Run dsh-any-agent team on and reconnect; the profile switches to dsh-team (the experimental Agent Teams packages), and dsh_send then adds a short "form a team if this needs parallel or multi-role work" line; simple tasks are not forced into a team. Turn it off with dsh-any-agent team off, or for one call with dsh_send(team=false). The team profile disables the legacy subagent tools and replaces them with nine team tools; teammates share one workspace.

Only dispatch semantics become tools: start, send, read, get, review, interject, interrupt, approve, queue, inbox, status, release, takeover, doctor. No file I/O, no shell, no editor, no task board.

When to add something outside DSH: only when you need a guarantee rather than a behavior — for example, several top-level sessions editing the same repository need outer worktree isolation. For collaboration, prefer letting DSH run the team itself (mount the experimental team packages into the profile; teammates share one workspace).

Quick start

Requires Node.js ≥ 22.15 and DSH installed with a working model route and credentials. See Requirements for details.

npm install -g dsh-any-agent
dsh-any-agent-setup
dsh-any-agent-setup --yes

The second command previews the changes; --yes applies them. Reconnect your client's MCP server, then ask your agent to start a DSH conversation in your project, dispatch a task, and collect the result. The dispatch workflow shows the tool sequence.

Only using Cursor or VS Code? Add it in one click instead (runs npx -y dsh-any-agent@latest; DSH still has to be installed):

Add to Cursor Install in VS Code

Want to see it first? Watch the 54-second promo — dispatch, interject, the sentinel landing and the caller's review, with the terminal replayed from a real run:

▶ Play the dsh-any-agent promo (54 seconds)

Before your first task: read the Usage Notes, especially the model-config and external-MCP permission boundaries. External MCP tools bypass DSH's sandbox. For a fresh machine, follow Setup.

Permissions: this server only dispatches; permissions follow DSH and your configuration. The default is workspace-write (writes limited to the workspace; out-of-workspace requests are auto-allowed and listed in the result). For personal use on your own machine most people just turn on full access: dsh-any-agent-setup --yes --permission danger-full-access, after which DSH asks for nothing and everything is allowed. Anyone who can call these tools can run commands on your machine (including a caller agent that got prompt-injected); use read-only for untrusted content. See Permissions & privacy.

Full contents

🧰 Tools

ToolWhat it does
dsh_startCreate a conversation: workspace, permission tier, approval policy, reasoning effort (default max), model
dsh_sendHand it a task. Defaults to wait=false: returns a run_id + sentinel_file immediately, the turn runs in the background and the sentinel file notifies you when it's done (recommended). wait=true blocks until the turn ends; hitting timeout_ms (default 0 = wait forever) only downgrades to background — the turn is never cancelled and the result is never lost
dsh_listAll conversations with live state: running / idle / detached, elapsed time, current tool, output so far. only_running=true for active ones
dsh_readCursor-based incremental read of what a running conversation is producing right now. The cursor is a revision number: an entry whose text grew since your last read reappears — match it by seq and replace its text, don't append again
dsh_getConversation details; with run_id, the final result of that dispatch (the "collect the result later" entry point) plus its review ticket. Run records live in memory (last 20) — use the sentinel file after a restart
dsh_reviewCaller review. Submit pass / partial / fail for a completed run, with notes and optional findings. The verdict is written to <state dir>/reviews/<conversation_id>/<run_id>.review.json and back into the in-memory run and sentinel. Advisory by default: it never blocks the next dsh_send
dsh_interjectInterject while it's busy: interject = stop and redirect, queue = wait for the current turn, then speak. This round writes a sentinel too, and the reply carries its sentinel_file. In both modes, and on an idle conversation, it blocks until the new turn ends; to queue without blocking, use dsh_send(queue=true)
dsh_takeoverPreempt the write lock. The only way out when another DSH process holds it (DSH has no steal API — preempting means killing the holder). It classifies the holder first: our own orphaned child → taken automatically; another live instance → needs force; GUI/unknown → never killed, reported instead
dsh_interruptStop the current turn without sending anything new (the conversation stays healthy); to stop and redirect, use dsh_interject
dsh_releaseHand the conversation back (frees the write lock so you can open it in your own DSH GUI). Still resumable; forget=true writes a tombstone — by-id lookups are refused until you delete $DSH_HOME/.dsh-any-agent-forgotten/<id>.json
dsh_approval_decideAdjudicate a pending approval (only when on_approval=ask)
dsh_statusLightweight status check (single conversation), including the write-lock holder lock_holder
dsh_inboxCross-conversation approval inbox: every pending approval with reason, input and age; decide with dsh_approval_decide
dsh_queueInspect/modify the session queue: list / remove / update / promote (created by dsh_send(queue=true), memory only)
dsh_doctorRead-only health check: DSH version, profile, state/dir writability, orphan locks, sessions and RSS, effective config

Every tool declares all four MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). Six only read state and are marked read-only: dsh_list, dsh_read, dsh_get, dsh_status, dsh_inbox, dsh_doctor. Tool titles, descriptions and the server instructions come in English and Chinese: they follow your system language, or set DSH_MCP_LANG=en|zh. Tool results follow the same setting; a few less common error messages are still Chinese only.

👀 Live view: see what the dispatched work is doing

The tools above are for the calling agent. You, sitting at the client, usually see one dsh_send and nothing else. Since 0.6.1 the server writes each conversation's activity to a local event stream, so you can watch the call chain without going through the agent:

claude-code  pid 8328
└─ e896d16a  temperature module  running · total 278k/17k · context 3%
   ├─ ✓ run-…-1  [send] · done · 1m24s · 8 tools · 69k↑ 4.1k↓ tok
   └─ ▶ run-…-2  [queue] · running · 156k↑ 12k↓ tok
         ✓ read   temp.js                  13ms
         ✓ edit   temp.js                  18ms
         ◆ approval auto-allow ⚠ danger-full-access  node --test
         ▶ pwsh   node --test
dsh-any-agent watch
dsh-any-agent dashboard --open
dsh-any-agent dashboard: conversations grouped by client on the left, the run timeline with a timing bar per tool call in the middle, overview, token usage, tools and files on the right (sample data)
  • watch: the call chain in your terminal, redrawn in place like top (no scrolling spam). watch <conversation id prefix> follows one conversation; --once prints once and exits.
  • dashboard: a local read-only web page; --open launches it in your default browser. Left: conversations grouped by client. Middle: the run timeline, with a timing bar per tool call and approvals/escalations highlighted. Right: overview, tool breakdown, files touched, approvals, and token usage (input / output / cache hit, context window, per-turn chart).
  • English / 中文: follows your system language by default; pass --lang en|zh to choose, or switch from the page's top-right corner.
  • Doesn't disturb running work: both are separate processes that only read event files — they never touch the write lock or DSH. Use them to watch progress, and don't open MCP-driven conversations in the DSH GUI (that takes the write lock; see usage notes 3.10).
  • Safety and privacy: the dashboard binds 127.0.0.1 only, its URL carries a one-time token, it checks the Host header, and it has no control that changes state. Command, path and prompt previews are redacted first; reasoning is never written at any level. DSH_MCP_LIVE=meta keeps only tool names and status; off disables it.

Details in Usage · side view; event format and design trade-offs in the live view design.

📚 Documentation map

DocumentContents
SetupRequirements, profile bootstrap, 10 MCP clients
UsageDispatch/collect, sentinels, many conversations, live view, reasoning
Usage notesPitfalls: model routing, cost, concurrency and the write lock, permissions, privacy, troubleshooting
Live view designEvent stream format, watch / dashboard, token source, privacy trade-offs
Team modeDefault behavior, how to disable (per client), verify and revert
Permissions & privacyTiers, approvals, GUI visibility, logging
Write lockFour holder classes, takeover, GUI contention
Environment variablesEvery switch and its default
TestsOffline/integration suites and coverage
Known limitationsLimits, profile naming
CompatibilityDSH version matrix, team-package requirements, upgrade checks
ArchitectureWhy ACP, sdk/plugin comparison
Release processRelease checklist

🧑‍🤝‍🧑 Team mode (opt-in)

Off by default. Run dsh-any-agent team on and reconnect; the profile switches to dsh-team (the experimental Agent Teams packages) and dsh_send adds a short "form a team when this needs parallel or multi-role work" line. Simple tasks are not forced into a team. Turn it off with dsh-any-agent team off, or for one call with dsh_send(team=false).

Full steps (commands, verification, rollback) are in Team mode.

📇 Listed on

dsh-any-agent MCP server on Glama M8ven Score DSH Any Agent MCP on mcprush

🙏 Acknowledgements

dsh-any-agent started from Moer2831/Deepseek-DSH-MCP (MIT), where this server was called dsh-mcp. The ACP client, the session hub, write-lock handling, sentinel files, the original design draft and the first test suites come from that project; client setup and restore, team mode, the live view, caller review and the release pipeline were added here. The original copyright notice is kept in LICENSE.

Star History

Star History Chart
NPM
dsh-any-agent
Install Command
npm install dsh-any-agent