dsh_list_workspaces
List workspaces
List the DeepSeek Harness workspaces (project folders): id, path, title and session_count. The id is the workspace_id for dsh_send. Read-only. Needs the dsh-baton-host plugin.
无需输入参数。

io.github.JialaoLiu由 GitHub 账号 JialaoLiu 发布v0.6.7更新于 Nov 7, 2026
Hand tasks from Claude Code, Codex or any MCP client to a local DeepSeek Harness.
把 Claude Code、Codex 或任意 MCP 客户端的编码任务交给本机 DeepSeek Harness 会话执行,并带回结果、改动文件与测试证据。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
参照 仓库 中的启动说明。
English | 中文 · macOS · Windows (guide) · no Node.js needed · zero dependencies · MIT
Quick start · See it run · Token usage · How it works · Tools · Security
Hand coding tasks from Claude Code or Codex to DeepSeek Harness on your machine: the task runs live in the Harness window, the main agent hears back once when it ends, and it gets back the files that changed, the project's own test results and a requirement-by-requirement checklist.
Needs DeepSeek Harness ≥ 0.2.1-rc.2 (signed in) and Claude Code or Codex. Quick start →
dsh-baton is an MCP server plus a small Harness plugin. The main agent hands the task to an ordinary Harness session with one tool call: you watch it run in the Harness window and can step in at any time, and the main agent hears back once, without polling. Unlike a plain MCP bridge to another model, the work stays visible and the proof comes back with the result.
Beyond the savings, the split itself helps:
Nothing else to install: no Node.js, no terminal. The steps are the same on macOS and Windows.
1. Add the plugin in Harness: Plugins (插件) → Add plugin (添加插件) → type dsh-baton-host → Install (安装) → Enable now (立即启用).
Switched on, the plugin registers dsh-baton for Claude Code and Codex, for every folder, and Harness says so.
2. Open a new chat: in Claude Code or Codex, start a new conversation; one that was already open does not see dsh-baton yet. The steps are the same for both. Codex asks you to approve MCP tool calls: approve the dsh-baton tools (for unattended codex exec runs, add --approve-for-me).
3. Check: ask it to call dsh_list_workspaces. It lists your Harness workspaces.
That is all. To hand a task over, end the request with "hand it to DeepSeek Harness". Updating, removing, Windows, and registering by hand when the plugin could not, are further down.
New in 0.6.4: Codex waits in one dsh_wait call of up to 59 minutes, so a run that ends within the hour costs one model request to wait for, and the result arrives as an ordinary reply in the chat. Update the plugin in Harness and new Codex conversations have it, with no restart.
One Codex chat starts a task, queues a follow-up, steers the running turn, and waits for all three:
Codex and Claude take turns in the same Harness session, and every message is labelled with who sent it:
In short: hand over the big, long jobs. With Claude Code the total cost, DeepSeek included, is 51–78% lower. With Codex on GPT-6 Astra it is 47–81% lower, and the long kanban app takes 16.01% of a ChatGPT Plus 5-hour window instead of 86.01%.
Hand it over: big jobs, long jobs, and whenever the 5-hour window is running low. The main agent's own spend drops 61–87% on every task and model.
Do it yourself: small tasks on Codex with GPT-6.1 Sol. Sol is cheap enough that DeepSeek's part of a hand-over, two thirds or more, outweighs what Sol saves.
The three test tasks, asked the way a user would (the requests are in Chinese, linked as written):
Cost per task at list price, DeepSeek V4.1 Flash 1017 at its peak price. Opus 5.5 runs in Claude Code, GPT-6.1 Sol and GPT-6 Astra in Codex, all at high effort:
dsh_wait call does it, also in one model request for every 59 minutes of a run.Pick a model: just say which one, for example "hand it to DeepSeek, V4 Pro". dsh_send passes it as model (and reasoning_effort), matched against the models Harness lists, so a model Harness adds later works as soon as it shows up. It applies to that session only; Harness's default model stays as it was. dsh_list_models shows what is available.
Nothing to configure for waiting; if you want to know what each client does while a run is going:
wait_command that dsh_send returns) and is woken once, when the run ends.dsh_wait. The plugin sets Codex's tool timeout for dsh-baton to an hour (tool_timeout_sec = 3600), so one call waits up to 59 minutes: a run that ends within the hour costs one model request to wait for, however long it takes, and the result comes back as an ordinary tool result in the chat. A longer run returns timed_out with just the state after 59 minutes, and Codex calls again without summarising. Pressing Stop ends the wait at once. Codex gets no wait_command: its terminal cannot wake it, so it would poll. Conversations still open since before the plugin set the timeout, or an entry you wrote yourself without it, wait 270 s per call, just under Codex's default 300 s timeout; after Codex restarts, they get the long wait too. Measured in the Codex desktop app: a 28-minute run took one dsh_wait call; a 62-minute run took two, the first returning timed_out after 59 minutes and the second getting the result 3 minutes later, with no dsh_status calls in between./goal sets one) makes Harness start the next round by itself as soon as a turn ends. A run sent to such a session is waited for until the goal stops, not just its first round, so "hand this to Harness" brings back the finished work; sessions without a goal are waited for as before. When the user asks for progress reports ("report every 10 minutes"), the client passes until turn: it then reports at the end of a round once 10 minutes (every_minutes) have passed since the last report, with every round since. To follow a session without sending anything, the client calls dsh_wait with session_id (Claude Code runs the follow_command in the background instead). Each report carries every round since the last one (the latest round's closing reply (up to 1500 characters), each earlier round as a one-line digest with its tools; any round in full through dsh_read_session), the files changed in the folder since the last report (since the run began, for the first), and the goal's state (phase, rounds started of the most allowed, whether Harness goes on with it), plus a cursor that the next call passes back, so switching between the two modes neither repeats nor misses a round. Both return at once when Harness asks a question (answer it with dsh_answer and session_id) or waits for an approval, when the goal is complete, paused, blocked or cleared, or when Harness will not go on with it by itself (for example after a Harness restart, until you press Continue). In Codex one call follows a goal for at most 15 minutes and Codex then calls again, so a message you type reaches it within that time; such an in-between return carries only the goal's state and how many rounds wait, not their texts.dsh_send with mode steer) and waits on the new run; Harness does both in the same turn. Sent with Queue, Codex sees it only when the run ends; you can also press Stop first and then send it. In Claude Code, just send it: it waits in the background, so it steers the change in right away. A steered run ends together with the original and they share one result; a queued run comes back on its own. Runs working in the same folder at the same time are named in each other's results, so neither removes the other's files.You don't have to take DeepSeek's word for it. Before the run, dsh-baton notes what is in the folder. When the run ends it shows exactly which files were created, changed or deleted, and runs your project's own tests itself (npm test, or whatever the client passes as checks). A second, read-only DeepSeek agent then goes through the request point by point and ends with a table: requirement, met or not, and where (file:line). Claude or GPT reads just that, sends anything missing back in one message, and opens code only where a row says "not met". Unless cwd names another folder, the new Harness session starts in the folder you're already working in.
When you want the exact parameters and return fields of each tool:
The server's MCP instructions are a single sentence (from 0.6.2); how to hand over, wait and answer is in each tool's own description. Codex repeats the instructions in front of every tool's description, so the catalogue it shows the model is now about 9.7k characters instead of 24.3k.
When you want to drive Harness from a terminal or a script instead of a chat:
(From source, use node src/cli.mjs ….)
When the defaults don't fit, for example a different socket path or forcing headless mode:
When the Harness plugin is not reachable:
When the plugin is unreachable, the MCP server falls back to Harness's dsh headless command. It can only continue sessions it created itself (headless does not load agent presets such as standard), and new sessions do not appear in the desktop sidebar. By default patches/account.patch.yml reuses the desktop app's DeepSeek account sign-in, so no API key is needed.
When you update or remove the plugin, come from dsh-bridge, run from source, or the plugin could not register:
What the plugin does when switched on. It copies the MCP server to ~/.dsh-baton/app, a folder that stays put, and writes the MCP entry into ~/.claude.json and ~/.codex/config.toml directly, keeping a *.bak-dsh-baton backup of each. It starts the server with your own Node.js ≥ 22.15 when it finds one on PATH; without one, with Harness's own runtime (the Harness app in Node mode, ELECTRON_RUN_AS_NODE=1), so nothing else has to be installed. Either way it works with only the desktop apps installed, when claude / codex are not on your PATH. It runs again on each Harness start: an entry that is already right, or a working one that starts another copy of dsh-baton, is left alone. In Harness, the source switches to the npm registry ("Official npm registry", 「npm 官方源」) by itself once you type the package name, and no restart is needed. To turn the registration off, set DSH_BATON_AUTO_REGISTER=0 for Harness.
Codex's tool timeout. Codex stops an MCP tool call after 300 s unless the server's table sets tool_timeout_sec. The plugin writes tool_timeout_sec = 3600 into [mcp_servers.dsh-baton] in ~/.codex/config.toml, and adds it to an existing table that has none; a value you set yourself is kept. The server reads it and makes each dsh_wait that long less 60 s. Codex reads the setting when it starts or loads a conversation and keeps it: new conversations get it without restarting Codex, and so does every conversation after Codex restarts, while conversations open since before keep Codex's 300 s limit. dsh-baton tells them apart by when the conversation and the Codex process started (~/.dsh-baton/codex-timeout.json holds when the value was written) and keeps their waits at 270 s.
Codex's cwd for dsh-baton. Codex starts an MCP server in the conversation's folder unless its table sets cwd; when that folder is gone (a project on an external drive that is not connected) the server cannot start and the conversation has no dsh_* tools. The plugin writes cwd (your home folder) into [mcp_servers.dsh-baton] and adds it to an existing table that has none; a cwd you set yourself is kept.
Updating to 0.6.4. Nothing to restart, approve or set. Updating the plugin in Harness installs the new server into ~/.dsh-baton/app and writes the timeout; Codex's running server picks up the new version on its next call (it checks for a newer copy before each tools/list and tools/call). New conversations wait up to 59 minutes per call; conversations already open go on as before, with 270 s waits. The Stop hook that plugins 0.6.0 to 0.6.3 added is taken out by itself, together with its trust record; there is nothing to untick in Codex. New conversations, and every conversation after Codex restarts, have no hook at all.
Updates need no restart. When a newer version lands in ~/.dsh-baton/app (a plugin update, setup, or a copy by hand), or the same version with changed files, conversations already open in Claude Code and Codex use it from their next dsh_* call: the server checks the folder before each call, starts the new version, and says once in that call's result that it was updated. A dsh_wait that is already running finishes on the old version first. If the new version cannot start, the old one keeps working and the result says why. A tool added in the new version shows up in Claude Code at once (the server tells the client its tool list changed); Codex keeps the tool list a conversation started with, so it does not show a new tool until the next conversation; it can still use one at once through dsh_call, which the update note points to, and the tools it already has run the new code right away.
npx -y dsh-baton-mcp setup does the same registration from a terminal: for a plugin that could not register, or when you come from an MCP directory. With the plugin already registered it changes nothing, then points you to the plugin if it is missing.
Updating. Harness has an update button only for its official plugins, so: Plugins → dsh-baton-host → Uninstall, then Add plugin → dsh-baton-host. For a while after a release, Harness installs the previous version instead (pnpm's minimumReleaseAge holds back versions published very recently, and the install dialog says so); to get the new one at once, type the version: [email protected]. A pre-release must always be added by its exact version, such as [email protected]: Harness resolves @next to a broken placeholder. Plain dsh-baton-host gets the latest release. Switched on again, the plugin brings the MCP server to the same version; new conversations use it. If the two are ever out of step, the first tool call that notices it says so, naming the older side and the newer side's exact version to install.
Removing. Harness → Plugins → dsh-baton-host → Uninstall, then npx -y dsh-baton-mcp setup --remove. The plugin cannot tell an uninstall from Harness quitting, so it leaves the MCP entries to that command, which also deletes ~/.dsh-baton/app; run records stay in ~/.dsh-baton until you delete it.
Upgrading from dsh-bridge. In Harness, uninstall dsh-bridge-host, then add dsh-baton-host; switched on, it replaces the old dsh-bridge MCP entries. DSH_BRIDGE_* variables and an existing ~/.dsh-bridge folder keep working for now. Tools get a new prefix (mcp__dsh-baton__…), so update any permission allowlists. A plugin added before 0.4 by pasting ~/.dsh-baton/app/host still works but does not update with npm: uninstall it and add dsh-baton-host by name.
From source (to follow main or change the code): git clone https://github.com/JialaoLiu/dsh-baton.git, then in Harness add the plugin from the clone's host folder (Add plugin → paste the path). Switched on, it registers the clone itself, so changes run before they are published. Update with git pull. node dsh-baton/src/cli.mjs setup registers the clone from a terminal.
Windows. Same steps; WINDOWS.md covers what differs (a named pipe instead of a socket file) and fixes for a Plugins page that cannot install or uninstall.
When you register by hand, or want dsh-baton in the Claude desktop chat app (not Claude Code):
The plugin and setup write the same entries these commands would (with the absolute path of node, since desktop apps start MCP servers without your shell's PATH):
-s user matters: without it, Claude Code registers the server for the current project folder only.
Claude desktop app (optional): for the chat app's connectors (separate from Claude Code), add this to ~/Library/Application Support/Claude/claude_desktop_config.json and restart the app. It shows as a "Local dev" connector.
Any process running as your OS user can drive Harness through the socket, the same as running dsh directly. dsh-baton relies on Harness 0.2.1-series internal service APIs, so a major Harness update may need a follow-up release.
MIT
来源:README.md,提交 182e6d2
List workspaces
List the DeepSeek Harness workspaces (project folders): id, path, title and session_count. The id is the workspace_id for dsh_send. Read-only. Needs the dsh-baton-host plugin.
无需输入参数。
List sessions
List Harness sessions, newest first, to find a session_id: session_id, title, cwd, updated_at, running, bridge_runs and model (what its next request uses). No paging: raise limit or narrow it with cwd or query. Read-only. Sessions not in dsh_list_sessions are archived or gone: do not look for them on disk.
cwdlimitqueryinclude_subagentsList models
List the models this DeepSeek Harness can run (provider, model id, name, reasoning efforts) and its default model, to check a name before passing model to dsh_send. Read-only. Needs the dsh-baton-host plugin.
无需输入参数。
Read session
Read the last messages of a session (role, time, text and the names of tools called; no tool output), to catch up before continuing it. Read-only.
lastsession_id必填Send message
Hand a task to DeepSeek Harness, or continue a session: returns at once with a run_id and a hint saying how to wait for the reply; follow it. Call it directly, no need to list anything first. A new session starts in the folder this conversation works in, or in cwd or workspace_id (a new cwd is registered first); session_id continues that session. message arrives verbatim as a user message, and Harness does not see this conversation: make it self-contained, in the user's language, the full, explicit spec, asking for tests that cover each requirement. Keep a leading /goal (it sets a Harness goal) and the user's goal text as written. For a large task, write a concrete spec (goal, modules and the files each owns, level of detail, acceptance criteria) rather than passing the user's words on, and send independent parts to separate sessions in parallel, each owning its own files; review once all are back, then send specific fixes. With checks, a short request for a sub-agent review table is appended. To a busy session, mode followup queues the message and steer adds it to the running turn; left out, the Harness setting decides.
cwdmodemodelchecksmessage必填session_idworkspace_idreasoning_effortRun status
Look once at a run or a session without waiting. A run (run_id): state (queued/running/waiting/completed/failed/cancelled/interrupted), recent events, final reply, waiting_on (sub-agents or jobs still working) and human_interjections. A session (session_id): busy, activity, its Harness goal (phase, rounds, whether Harness goes on with it), needs_input, and its last turn's reply. Never a way to wait: use dsh_wait.
tailrun_idsession_idWait for run
Wait for a run sent with dsh_send to end and return its state and final reply. Prefer the background wait_command from dsh_send if your client wakes you when a background command exits: it costs no turns. It also returns at once when the run stops to ask a person: needs_input answer (question and relay; reply with dsh_answer) or harness-ui (an approval only a person can give in Harness). Each call waits up to 50 s; if it returns timed_out: true, call dsh_wait again with the same run_id, without a progress note. A run in a session with a Harness goal is followed on until the goal stops, with every round since the run began. With session_id instead of run_id it follows that session's Harness goal without sending anything: until "goal" (default) returns once when the goal stops, until "turn" at the end of a round once every_minutes have passed since the last report; it also returns when Harness needs a person or will not go on by itself. Pass back the cursor each result gives.
untilcursorrun_idsession_idevery_minutesAnswer question
Answer the questions a waiting run asked (needs_input: answer in dsh_wait or dsh_status). If the user told you to decide such questions yourself, choose by your own judgment of the question and the task, call dsh_answer with reason (one sentence on why), then tell the user what you chose and why. Otherwise relay the questions to the user and start with your own advice, on a line of its own: "My recommendation: <option>, because <reason>", the reason drawn from your own judgment of the question and the task (harness_suggested only shows what the asking model would pick; it is never a reason by itself, and your pick may differ). Then list the options, wait for the user's decision and submit it with dsh_answer. Never answer on the user's behalf without that permission. Per question: option numbers or labels in choose, text for a free answer, or skip. The answer reaches Harness once every question has one (submitted: true; remaining lists the rest); then wait for the run again. If the user already answered in Harness, it reports that there is no open question.
reasonrun_idanswers必填session_idCancel session work
Stop everything a session is doing (turn, sub-agents, background jobs, reminders), by run_id or session_id; the session is kept. Messages queued in it are dropped too (removed_from_queue); to drop one queued message use dsh_update_queued. Safe to repeat. Returns still_busy, activity and removed_from_queue.
run_idsession_idCall dsh-baton tool by name
Call a dsh-baton tool by name, for a tool this conversation does not list (dsh-baton was updated without a restart; the update note names it). Runs the same code as a direct call, so it can do whatever that tool does. An unknown name changes nothing and returns the tools with their input schemas.
name必填arguments已满足适用检查项的 90%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
dsh-baton workspacesdsh-baton new-workspace ~/code/new-app --title "New App"dsh-baton sessions --cwd ~/code/new-appdsh-baton send --cwd ~/code/new-app "Scaffold the project"dsh-baton send --session session-xxxx "Next: add tests"dsh-baton send --session session-xxxx --mode steer "Interjection: don't change the API"dsh-baton wait hrun-xxxx # blocks, prints the final reply; exit 0 on successdsh-baton status hrun-xxxxdsh-baton follow session-xxxx --until turn --every 10m # a session's Harness goal: blocks until the next reportdsh-baton activity session-xxxx # running turn, sub-agents, background jobsdsh-baton cancel --session session-xxxxdsh-baton rename session-xxxx "New title"dsh-baton rename-workspace ~/code/new-app "New name"claude mcp add dsh-baton -s user -- "$(command -v node)" "$PWD/dsh-baton/src/server.mjs"codex mcp add dsh-baton -- "$(command -v node)" "$PWD/dsh-baton/src/server.mjs"{ "mcpServers": { "dsh-baton": { "command": "/absolute/path/to/node", "args": ["/absolute/path/to/dsh-baton/src/server.mjs"] } } }