dsh-any-agent
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dsh-any-agentfix the flaky login test and let me know when it's done"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
🐳 dsh-any-agent
English | 中文
Give any MCP client a coding buddy that sticks around — powered by DeepSeek Harness.
An MCP server that drives DSH as a long-lived agent runtime instead of wrapping a CLI. Open a conversation, hand it a goal, and it writes code, runs scripts and spawns its own subagents — while you watch, cut in, and collect the result whenever you like. 🛠️
Works with any MCP client. Nothing here is Codex- or Claude-specific: the server speaks plain MCP over stdio and uses only the four standard methods (initialize, ping, tools/list, tools/call). Verified against a bare-bones client that negotiates an older protocol version and never sends notifications/initialized.
🧪 528 checks green · 🔌 MCP over stdio · 📦 Zero dependencies · 📜 MIT
📌 Read the Usage Notes first — the practical gotchas that will actually bite you: the ACP model-config trap, external MCP tools bypassing DSH's sandbox, the workspace/GUI registry cache, cost control, and a troubleshooting table.
🆕 Setting this up on a fresh machine? USAGE-MIGRATION.md is a step-by-step walkthrough for a new environment: prerequisites, how to find your
DSH_BIN, ready-to-paste config blocks for Claude Code / Codex / Cursor / Windsurf / Cline / Zed, and a first-turn checklist.
📑 Contents
Start here — The problem · Why it's different · Why not just wrap the DSH CLI · Requirements · Setup
Using it — Tools · Dispatch then collect · Completion notifications · Many conversations
Behavior — Permissions · Reasoning · Logging and privacy · Workspace & GUI
Hard parts — The write lock
Reference — Environment · Tests · Known limitations · Why the old profile name?
any MCP client ← Claude Code, Codex, Cursor, Zed, Cline, your own…
(Claude/Codex/Cursor/…) │
│ MCP over stdio │ newline-delimited JSON-RPC (ACP)
▼ ▼
┌─────────────┐ ┌──────────────────────────┐
│dsh-any-agent│───────────▶│ dsh --profile dsh-mcp │ one long-lived process
└─────────────┘ │ │ per conversation
└────────────┬─────────────┘
│
┌────────────┴─────────────┐
│ DSH's own tools │ files, shell, search,
│ │ skills, subagents, workflows
├──────────────────────────┤
│ dsh-mcp-client │ → your other MCP servers
└──────────────────────────┘Related MCP server: dsh-mcp
🎯 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;
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.
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
🔄 A dead process never kills a conversation | The 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 waiting | Steer a conversation mid-flight — interject to stop and redirect (converges in well under a second), or queue a remark for after the current turn. Interrupting never damages the conversation. |
📡 Completion notification, not polling | Long tasks return instantly with a |
🪟 Many conversations at once |
|
🧠 Reasoning hidden by default | You get "thought for N chars / M seconds" statistics instead of context-burning prose — and it is never logged, never written to disk. |
🤫 Silent by default | Not one byte on stderr, so nothing pollutes your host's logs. stdout carries the protocol and nothing else. |
🧩 Capabilities compose | DSH 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-only |
|
⚡ Why not just wrap the DSH CLI?
DSH ships two programmable entry points. The sdk profile looks simpler, but its JSON-RPC surface has only 3 requests and 4 notifications — and it lacks three things that make an agent unusable in practice. The acp profile (Agent Client Protocol) has all of them. Everything below was verified against DSH 0.1.7-rc.2.
Capability |
|
|
Resume a conversation | ❌ no resume method; reusing an id fails with | ✅ |
Interrupt a running turn | ❌ only by killing the process | ✅ |
Answer approval prompts | ❌ no channel; operations silently fail closed | ✅ |
List / close sessions | ❌ none | ✅ |
Gray vs. black streaming | one message, split by a | ✅ two separate channels: |
Per-session MCP servers | ❌ process-wide | ✅ |
Core idea: the process is a cache, not the identity. A conversation's identity is DSH's sessionId, persisted under ~/.dsh/sessions. If the process is reaped, crashes, or the whole MCP server restarts, the next call transparently session/resumes it — history intact.
📦 Requirements
Node.js ≥ 20
DSH installed (
@deepseek-ai/dsh), with working credentials under~/.dshA DSH profile named
dsh-mcpderived from the shippedacptemplate, containing your own provider/model config (this repo deliberately ships no provider config)
🚀 Setup
1. Create the DSH profile
dsh dsh-mcp --from-default-profile acpThen edit ~/.dsh/profiles/dsh-mcp/cordis.patch.yml and declare your provider and model. Start from profile-example/cordis.patch.yml.
⚠️ Two traps here (both learned the hard way):
ACP's model does NOT come from
agent-default-model. It comes from thedsh-acpplugin's ownconfig.provider/config.model, which theacpbundle hard-codes todeepseek-official. If you don't override theacprow, your sessions silently run on the wrong provider and fail withno API key for provider route "deepseek-official".ACP's
reasoning_effortdefaults to empty (= "Provider default"), not to the maximum. This server explicitly sets it tomaxon everysession/newand after everysession/resume— because a fresh process does not remember the previous choice.
2. Register the server
From npm (recommended)
npm install -g dsh-any-agentThe dsh-any-agent executable is then on your PATH and every client below can just call it by name.
From a local checkout
Clone or unzip anywhere, then point command at node and args at <checkout>/server/bin/dsh-any-agent.mjs (shown below).
Claude Code
# global install
claude mcp add dsh-any-agent -- dsh-any-agent
# local checkout
claude mcp add dsh-any-agent -- node /absolute/path/to/server/bin/dsh-any-agent.mjsCodex (~/.codex/config.toml)
[mcp_servers.dsh-any-agent]
command = "dsh-any-agent" # or "node" + path to bin/dsh-any-agent.mjs
args = []
startup_timeout_sec = 20.0
tool_timeout_sec = 120.0Generic MCP client — anything that speaks MCP over stdio:
{
"mcpServers": {
"dsh-any-agent": {
"command": "dsh-any-agent",
"args": []
}
}
}For a local checkout, use "command": "node" and "args": ["/absolute/path/to/server/bin/dsh-any-agent.mjs"].
Cursor, Windsurf, Zed, Cline, Continue and most other clients use this same
mcpServersshape — if it can launch a stdio MCP server, it works.
3. Try it
node test/smoke.mjs # no LLM calls, verifies the whole plumbing🧰 Tools
Tool | What it does |
| Create a conversation: workspace, permission tier, approval policy, reasoning effort (default |
| Hand it a task. Defaults to |
| All conversations with live state: |
| Cursor-based incremental read of what a running conversation is producing right now |
| Conversation details; with |
| Interject while it's busy: |
| Preempt 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 |
| Stop the current turn (the conversation stays healthy) |
| Hand the conversation back (frees the write lock so you can open it in your own DSH GUI). Still resumable |
| Adjudicate a pending approval (only when |
| Lightweight status check |
📋 Workflow: dispatch, then collect later
MCP tool calls block, so dsh_send does not block by default (wait=false): you get a receipt immediately, the turn runs in the background, and the sentinel file wakes you when it finishes. The whole flow:
1. dsh_send(conversation_id, prompt, wait=false)
→ { run_id: "run-abc123", background: true } # returns without waiting for the turn
2. …keep working on something else (verified: another conversation ran a full turn meanwhile)…
3. dsh_status(conversation_id)
→ state=running | elapsed=3.5s | current_tool=pwsh | out=37 chars
4. dsh_read(conversation_id, cursor) # peek mid-flight
→ [me] … | [turn] turn 1 started | [tool] pwsh [in_progress]
5. dsh_get(conversation_id, run_id) # collect the result
→ status=done, elapsed=20.3s, full answerrun_id is the receipt. Run records are memory-only, last 20 kept; under many concurrent runs old ones get evicted and dsh_get will fail — which is exactly why the completion sentinel below carries the result.
🔔 Workflow: get notified on completion (no polling)
Besides run_id, wait=false returns a sentinel_file path plus sentinel_file_posix (the Bash/MSYS form — use it directly; hand-converting Windows paths is the easiest way to make your waiter hang until timeout). When the turn ends — whether it succeeds, fails, or is cancelled — the service atomically writes a small file there containing the final status and result. So you neither poll nor block: wait for that file with a background task in your own host (Claude/Codex); the moment it appears, you're woken up.
# sent="$sentinel_file_posix" ← copy it straight from the tool result
sent='/d/AI_MCP/DSH_MCP/.state/runs/<conversation_id>/<run_id>.json'
dl=$(( $(date +%s) + 2100 )) # 35min safety net so a dead service can't hang you forever
until [ -f "$sent" ]; do
[ "$(date +%s)" -ge "$dl" ] && { echo "TIMEOUT"; exit 1; }
sleep 2
done
echo "DONE" # task exits → host wakes you → read the file to collectOnce the file exists: read it for status and result (preferred — immune to the in-memory window and survives restarts), or use dsh_get(conversation_id, run_id).
Design notes (especially under concurrent multi-session use):
No cross-talk: the path is
.state/runs/<conversation_id>/<run_id>.json, namespaced by conversation.run_idsequence numbers are counted per-conversation, so two conversations can mint the samerun_idin the same millisecond — the per-conversation directory keeps them apart. Arm one background task per run; each completes and wakes you independently.Latch semantics: the file is written once and kept. Even if the run finishes before you arm the waiter,
[ -f ]is immediately true — you never miss it.Atomic write:
tmp+rename; if the file exists its contents are complete. Each run uses its own tmp, so concurrent writes never clobber.No server push: the service only answers requests over MCP (it sends no notifications). "Completion notification" is entirely the sentinel file plus your background waiter.
★ No reasoning in the sentinel: the payload carries the answer and thinking statistics only —
result.thinkingis stripped and replaced bythinking_omitted: true, because the project's rule is that reasoning never reaches disk.dsh_get(run_id)still returns it from memory while the record lives.DSH_MCP_SENTINEL_INCLUDE_REASONING=1breaks that guarantee deliberately.
Cleanup: sentinels contain full answers, so the service prunes sentinels older than 7 days at startup (tune with
DSH_MCP_SENTINEL_TTL_MS;0disables). Your waiter should stillrmthe file after consuming it.
🧵 Workflow: many conversations at once
1. dsh_list(only_running=true)
- … | id=448b239b… | 🔵 running (9s, tool=pwsh, 40 chars this turn) | cwd=…
- … | id=33427096… | 🔵 running (8s, 34 chars this turn) | cwd=…
2. dsh_read(id, cursor=0) # read the first, note the cursor
3. dsh_read(id2, cursor=0) # switch to the second
4. dsh_read(id, cursor=<prev>) # switch back, deltas only🤫 Reasoning: hidden by default
DSH separates reasoning from answer text at the model layer, and ACP exposes them as two independent streams. This server hides reasoning by default and returns only statistics (characters, duration, chunk count) — so the caller knows thinking happened without paying for it in context.
reasoning=hide(default) — content dropped, stats returnedreasoning=marker/summary/full— progressively more content (mind your context budget)dsh_read(include_reasoning=true)— live reasoning of the currently running turn only; it is never buffered and is discarded when the turn ends
🔐 Permissions
Tier | Approval default | Use for |
|
| Zero friction |
|
| Out-of-workspace operations surface as pending approvals |
|
| Analyzing untrusted samples (malware, unknown dumps) |
on_approval can be auto-allow, auto-deny, or ask (which exposes a pending approval for dsh_approval_decide to adjudicate).
🪟 Workspace visibility in the DSH GUI
Two different things — don't confuse them:
Depends on | Status | |
Workspace effectiveness | the conversation's | ✅ Always correct. Process working directory, file placement and sandbox write root all follow it |
GUI grouping |
| ⚠️ This server registers it, but the running DSH server caches the registry in memory — external file edits only take effect after a DSH restart |
Verified experimentally (test/workspace-effect.mjs): two conversations in two desktop folders, given no path at all, each wrote its file into its own workspace, self-reported the correct working directory, and leaked nothing into the server's cwd / home / temp / the other workspace (24/24 checks).
Maintenance CLI:
node bin/dsh-any-agent-workspaces.mjs --list # inspect the registry
node bin/dsh-any-agent-workspaces.mjs --backfill # register every session found on disk
node bin/dsh-any-agent-workspaces.mjs --prune-temp # drop temp-directory entries
node bin/dsh-any-agent-workspaces.mjs --prune-empty # drop empty workspaces whose path is gone
node bin/dsh-any-agent-workspaces.mjs --purge-test-sessions # delete test sessions (strict rules)⚠️ Before restarting DSH, don't touch workspaces in the GUI — the server will write its in-memory state back over the file.
🕶️ Logging and privacy
Silent by default — not one byte is written.
MCP's stdout is the protocol channel; anything mixed in corrupts the stream.
This server is launched by Claude/Codex, so its stderr lands in their logs.
DSH's own stderr may carry model output including reasoning, so it is never forwarded by default.
Level | Behavior |
| nothing at all |
| this server's own lifecycle events only — never any DSH output |
| more of our own events; still no DSH output |
| additionally forward DSH's raw stderr (may contain reasoning) |
Reasoning never reaches disk — with exactly one deliberate exception:
Not in logs (silent by default), and the state file holds metadata only (id, cwd, permissions, counters) — a smoke-test assertion guards this.
The async completion sentinel does persist the answer (it must, so a caller can collect offline) but strips
result.thinkingand marksthinking_omitted: true. A test asserts that even whenreasoning=fullis requested, no reasoning text appears in the sentinel — whiledsh_get(run_id)still returns it from memory. SetDSH_MCP_SENTINEL_INCLUDE_REASONING=1to break this on purpose.Sentinels are pruned after 7 days by default (
DSH_MCP_SENTINEL_TTL_MS;0keeps them forever).
(DSH itself writes its own session log under ~/.dsh/sessions; that is what makes resume possible and is outside this server's control.)
⚙️ Environment variables
Variable | Default | Purpose |
| auto-detected | Path to |
|
| DSH profile to drive. ⚠️ The stale name is deliberate — see Why is the profile still called |
| on | Whether this server may create/sync the DSH profile on first start. |
| unset | Pin where model routes are copied from during bootstrap: a profile name ( |
|
| Conversation registry, survives restarts |
|
| Root for async completion sentinels ( |
|
| Holder registrations for the write lock. ⚠️ All instances must agree on this value, or they cannot see each other's registrations and will misread each other as an unidentifiable holder (a GUI) and refuse to preempt |
|
| How long without a heartbeat before a holder counts as lost (and becomes auto-preemptible by |
|
| How long a conversation may sit idle before its DSH process is reaped. ★★ |
|
| ★ How many conversation processes to keep alive at once ( |
|
| Cache lifetime for |
|
| Startup pruning age for sentinels; |
| unset |
|
|
| Default tier for |
|
| Default reasoning effort |
|
| Wait bound for |
|
| How long a pending approval waits |
|
|
|
|
| Per-request timeout on the ACP channel (initialize, session/new, …) |
|
| How often the idle reaper checks for conversations to reclaim. Values below the safe floor fall back to the default |
|
|
|
| unset |
|
🧪 Tests
node test/run.mjs # smoke only (no LLM calls)
node test/run.mjs --all # everything
node test/cleanup.mjs # suite teardown on its own (temp dirs only; --dry-run to preview)The suite cleans up after itself: run.mjs always ends with cleanup.mjs, which removes test sessions and workspace registrations inside the OS temp directory only. Real project sessions and the repo's .state/ (your live conversation registry and sentinels) are never touched. To also remove Desktop\dsh-any-agent-test-* artifacts, run node bin/dsh-any-agent-workspaces.mjs --purge-test-sessions explicitly.
Suite | Checks | Covers |
| 29 | handshake, tool table, conversation creation, config, registry purity |
| 42 | protocol edges (double initialize, malformed lines, unknown method), argument validation, unknown ids, cursor edges, lifecycle idempotence, unicode |
| 30 | resume-with-memory across processes, interrupt, both interject modes |
| 16 | fire-and-forget + later collection |
| 36 | completion sentinel: atomicity, latch semantics, per-conversation namespacing under concurrency, cancelled runs still land it, and reasoning never reaching the file |
| 11 | sentinel retention: prunes over-age and crash-leftover files, keeps fresh ones, removes empty shells, |
| 31 | environment-variable guards: odd numbers always land on the safe side — |
| 24 | approval review context: the pending-approval snapshot carries the command, reason, cwd and original approval ID — not just a bare ID — so a caller can actually adjudicate; credential-shaped fields and common inline literals are redacted in both the structured context and the displayed title. Verified end-to-end over real stdio MCP |
| 38 | timeout semantics: |
| 79 | write lock and preemption: four holder classifications, |
| 36 | the async dispatch lifecycle: dispatch → collect the sentinel → idle reap → dispatch again, three rounds with no lock error; re-dispatch right on the reap boundary (widening the race); an immediate re-dispatch after |
| 31 | three simultaneous conversations + live incremental reads |
| 22 | writing code, running scripts, spawning its own subagents (verified on disk via child session headers) |
| 24 | workspace actually effective when no path is given |
| 36 | two folders × two conversations doing a read-only IDA Pro analysis |
| 19 | several instances and non-ASCII paths: reproduces "two instances share the registry and the later save drops the earlier instance's conversation", then proves that conversation is recoverable by id alone from the session store and actually usable; recovery never silently escalates privileges (a read-only conversation stays read-only); no |
| 11 | ★ whether the permission tiers actually take effect (a safety property): it ignores our own return values (the very thing that used to lie) and reads the session's own record ( |
| 13 | the cost of |
Total: 528 checks, all green. (492 in-suite + 36 acceptance)
Counts are the numbers each suite prints itself.
smoke/prune/guards/approval-details/boundaryrun without LLM calls; the rest need a working provider.
🔒 The write lock: one writer per conversation at a time
DSH conversations carry a cross-process write lock: it never expires while the holder lives, and no API can take it from a live holder — so whoever gets there first decides everything. This server turns that into "you can look, we can write":
Situation | Outcome |
This server holds the lock (the default) | ✅ Opening it in the GUI is read-only: you still see the content, but you cannot take the lock. The default |
You open it in the GUI while the lock is free | ❌ The GUI keeps it permanently (navigating away, waiting and archiving all fail to release it) and this server can never reconnect — only restarting |
Recommended | Watch progress with |
Who holds it is reported by dsh_status.lock_holder; dsh_takeover handles the four cases:
| Meaning | Action |
| this process holds it | nothing to preempt |
| the holder is alive but silent (no heartbeat for 90 s — typically that MCP is wedged) | ✅ |
| another live dsh-any-agent instance is using it | ⚠️ refused by default; |
| no registration → most likely your own DSH GUI | ❌ never killed (GUI conversations live inside the single |
Two measured footnotes. ① A crash leaves no orphan lock —
SIGKILLthe MCP and its children exit with it (their stdio pipe closes), releasing the lock automatically ✓; sostale-mcpreally means "alive but wedged". ② Several instances: the registry is last-writer-wins (a later save drops conversations it never saw), but the session store is authoritative — any session on disk is recoverable by id alone; if you give instances differentDSH_MCP_STATEfiles, pointDSH_MCP_LOCKS_DIRat one shared directory, or they cannot see each other's holder registrations.The full measurements (the A/B probe method, the evidence that the GUI opens read-only, why even archiving does not release the lock, and crash-vs-wedge) are in Usage Notes §3.5–3.12.
⚠️ Known limitations
No token-level streaming into model context — an MCP limitation, not DSH's. Callers get per-step results; humans can follow progress via stderr logs.
True mid-turn steering is impossible — ACP rejects concurrent prompts (
a prompt is already in flight for this session).dsh_interjectis the practical equivalent: cancel, then immediately start a new turn, history preserved.Image prompts are unsupported — ACP advertises
promptCapabilities: {image: false}.Restarting the MCP server takes its children with it — when this service is restarted or killed, the DSH children it spawned exit too (their stdio is a pipe to us): in-flight turns are interrupted and no sentinel lands (judge by inspecting the workspace, don't just wait for the file). The good news: the write lock is released automatically, so a restart always gets it back. The service also pushes no MCP notifications; "completion notification" is the sentinel file plus the caller's background waiter.
A conversation that dies before its first successful turn may never have materialized on disk — resume then fails with a clear error. Safe after the first message.
No renaming — DSH's title subsystem has no external rename API (
SessionTitleService.renamerequires a live in-process session). Titles are auto-generated from the first message.session/listreturns only{sessionId, cwd}and excludes already-open sessions — titles are filled in by this server from DSH's projection cache."Never reap" costs memory (measured 300–480 MB per conversation) — that is what buys "the GUI can never take the write lock". Two fresh-start measurements of one active conversation gave 476 MB and 295 MB of added resident memory (the DSH child plus this server); expect the higher figures on a cold process. Two ways to spend less: ①
DSH_MCP_MAX_LIVE(default 8 — over the cap, the least recently used process is reaped → bounded memory, locks still held where it matters); ② setDSH_MCP_IDLE_TTL_MSto a millisecond value (at the cost of the lock being takeable). To drop to zero right after a batch of work:dsh_release(all=true)✓ (lossless, everything resumes on demand).
❓ Why is the profile still called dsh-mcp?
Because renaming it would break things that already exist on disk.
When you first start this server it creates a DSH profile under ~/.dsh/profiles/dsh-mcp/. That directory name is referenced by three other persisted things:
Persisted identifier | Where | Breaks if renamed |
the profile directory |
| existing sessions can no longer resolve their provider ( |
| your client config | every deployment would need editing at once |
the | first line of the generated | the bootstrap would stop recognising the file as its own and never sync it again |
So the product is dsh-any-agent; the persisted identifiers keep the old name on purpose. This is a deliberate compatibility decision, not an oversight. If you are setting this up from scratch and want a different profile name, set DSH_MCP_PROFILE — the server will use whatever you give it.
💬 Support
Bug reports, feature requests, setup questions — open an issue; all of it is welcome there
Before reporting a failure — check the troubleshooting table and the Usage Notes; most setup failures are one of a handful of known causes
This is a one-maintainer project, so a clear reproduction (the exact call, the error text, your DSH version) gets a much faster answer than a description.
📜 License
MIT — see LICENSE.
Available Tools
11 toolsdsh_approval_decideA
裁决一个待决审批(仅当会话的 on_approval=ask 时会出现)。用于把 DSH 的越权/提权请求交给调用方决定,既不一律放行也不一律拒绝。
| Name | Required | Description | Default |
|---|---|---|---|
| decision | Yes | 放行或拒绝。 | |
| approval_id | Yes | 来自 dsh_get 的 pending_approvals。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses that this is a consequential privilege-escalation gate whose outcome is 'allow' or 'deny' and that neither default is applied automatically, which is useful context. However, it says nothing about what happens to the underlying request after the decision, whether a decision is final/idempotent, or what occurs if approval_id is stale or already resolved.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences: the operation and its trigger condition first, then the rationale. Nothing is padded and the actionable constraint is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The call shape is small and fully documented in the schema, and the description covers what the tool is for. For a decision tool with no output schema and no annotations, though, it leaves the agent without any expectation of the result or of failure modes when the approval is missing or already decided.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and every parameter already carries a description, including the enum on decision and the provenance hints for approval_id (from dsh_get pending_approvals) and conversation_id (from dsh_start or dsh_list). The prose adds no additional parameter semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('裁决一个待决审批' / adjudicate a pending approval) and adds the scoping condition that it only surfaces when the conversation's on_approval=ask. No sibling among dsh_start/dsh_send/dsh_interject handles approvals, so the operation is unambiguous, though the description never explicitly contrasts itself with those siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear triggering context — use it only when on_approval=ask produced a pending escalation — and frames the intent as routing the decision to the caller rather than auto-allowing or auto-denying. It stops short of stating exclusions (e.g. what to do if the approval is no longer pending) or naming any alternative tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_getA
查看单个会话的详情;带 run_id 时取那一次派活的最终结果(异步模式的验收入口)。不传 run_id 则取最近一次回合。返回:标题、工作区、权限档、状态、轮数、token 用量、上下文压力、待决审批,以及该回合的完整结果(正文/工具/用量/错误)。
| Name | Required | Description | Default |
|---|---|---|---|
| run_id | No | dsh_send 返回的 run_id;不传则取最近一次回合。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the full burden, and it does substantial work: it discloses the return payload (title, workspace, permission tier, status, turn count, token usage, context pressure, pending approvals, full turn result). It omits explicit read-only confirmation and any error behavior for invalid ids.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action; the run_id variant is emphasized before the return-value inventory. Dense but every clause carries information, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description appropriately enumerates the return fields, which is the main missing piece an agent would otherwise lack. Only error/edge-case behavior and an explicit read-only statement are absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description reinforces run_id's meaning as the async verification handle and restates the default-to-latest-turn behavior, but adds little the schema does not already say.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('查看' / retrieve) and resource ('单个会话的详情'), then splits the behavior into two modes (with vs. without run_id). This is clearly distinct from dsh_list (enumeration) and dsh_send (dispatch), though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit conditional guidance: with run_id it is the acceptance/verification entry for async dispatches; without run_id it returns the most recent turn. It does not name alternatives (e.g., dsh_list for discovery), but the selection conditions between the two modes are unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_interjectA
插话:会话正在跑某个回合时往里补一句话。两种模式——interject(默认)打断当前回合并立刻改口;queue 不打断,等本轮自然结束后紧接着说。会话空闲时等同于一次普通 dsh_send。说明:ACP 明确拒绝并发 prompt,所以做不到"让模型在同一个回合里即时改向";interject 是它的实用等价物(取消 + 立刻开新回合,历史完整保留)。
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | interject(默认)= 打断并改口;queue = 等本轮跑完接着说。 | |
| message | Yes | 要插进去说的话。 | |
| reasoning | No | 思考内容展示档位,默认 hide。 | |
| timeout_ms | No | 新回合的**等待**上限(毫秒),默认 30 分钟;0 = 一直等。到期转后台,不取消回合。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the load well: it discloses that interject cancels the in-flight turn and starts a new one, that history is preserved, and that ACP's no-concurrent-prompt rule makes true mid-turn steering impossible. It omits what the caller actually receives back (the full turn result vs. a background acknowledgement) and any permission/auth expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core definition, then modes, then the ACP caveat that justifies the design. Every clause carries information; the closing note on why true in-turn redirection is impossible preempts a likely agent misconception.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter mutation tool with no annotations and no output schema, the description covers the semantics and the important side effect (current turn cancelled). The missing piece is return shape — whether interject is synchronous with the reply or returns after handoff, which matters given timeout_ms can push work to the background.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both enums are documented inline, so the description's restatement of mode semantics adds little beyond structured data. The one genuinely additive point is the idle-conversation equivalence for mode, but the timeout_ms background-expiry behavior is only in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb+resource: injecting a message into a conversation that is mid-turn, and immediately enumerates the two behavioral modes. It implicitly demarcates itself from dsh_send by stating the idle-conversation equivalence, so an agent can place it against siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear conditions are given: use it when a turn is running, pick interject to cancel-and-redirect, pick queue to append after the turn, and fall back to dsh_send when idle. The gap is that it never explicitly contrasts with dsh_interrupt or dsh_takeover, which are the closest competing behaviors for the 'cancel the current turn' case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_interruptA
中断会话当前正在进行的回合(ACP 原生 session/cancel,走的就是用户手动按停止那条路径)。会话本身保持完好,可以立刻继续派新任务。
| Name | Required | Description | Default |
|---|---|---|---|
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the full burden, and it does disclose key behavior: it is a cancellation of the current turn, it maps to the native session/cancel path, and the session survives intact and is immediately reusable. It does not say what happens to partial output or whether anything is lost, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the action, with the clarifying mechanism in parentheses and the non-destructive guarantee second. Nothing is wasted and nothing important is buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter mutation-style control tool with no annotations and no output schema, the description covers purpose, mechanism, and the critical after-state. The remaining gap is return behavior and what happens to in-flight partial results, which the agent might want to know.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single conversation_id parameter is fully documented in the schema, including where its value comes from (dsh_start or dsh_list). The description adds no further parameter semantics, so the baseline of 3 for high schema coverage applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states a specific verb and resource: interrupting the currently running turn of a session. It also identifies the underlying mechanism (ACP native session/cancel, the same path as pressing stop), which makes the purpose unambiguous and distinguishable from siblings like dsh_interject or dsh_takeover.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when to use the tool — when the session has a turn in progress that should be stopped — and states the post-condition that new tasks can be dispatched immediately. It does not, however, explicitly contrast this with dsh_interject or dsh_takeover, so the choice among interruption-style siblings still requires inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_listA
列出会话及其运行状态,用来掌控多个会话实例。state: running(正在跑回合)/ idle(进程活着但空闲)/ detached(进程没开,可 resume 复活)。running 的会话会带上已跑时长、当前正在执行的工具、本轮已产出字数。数据来自两路合并:本服务持有的 + DSH 磁盘上尚未打开的(DSH 的 session/list 会排除已打开的会话)。想只看正在跑的,传 only_running=true;想看某个会话当前吐出的内容,接着用 dsh_read。
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | 只看某个工作区下的会话。 | |
| only_running | No | 只返回正在跑回合的会话,默认 false。**此模式自动跳过磁盘探测,毫秒级**(正在跑的会话必然在内存里)。 | |
| include_closed | No | 是否包含磁盘上未打开的会话(含别的实例 / GUI 建的),默认 true。**代价:要 spawn 一个 DSH 进程去探测磁盘,实测约 1 秒**(结果缓存 10 秒)。只列本服务已打开的会话就传 false,毫秒级。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the two-source data merge (in-memory sessions + not-yet-opened sessions on DSH disk), notes that DSH's session/list excludes already-open sessions, and gives concrete cost figures (~1s disk probe, 10s cache) with the only_running fast path. It omits ordering/pagination and any permission requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and state list are front-loaded, then filters, then the next-step pointer. Dense but every sentence carries operational information; the cached-duration detail is slightly more than needed for selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema and no annotations exist, yet the description tells the agent what running sessions return (elapsed time, currently executing tool, words produced this turn) and where data originates. Sufficient to call and interpret the result; minor gaps around ordering and result limits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters including their performance tradeoffs. The description adds the state enumeration and the data-merge context useful for interpreting include_closed, but repeats the only_running guidance rather than adding new parameter semantics. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: list sessions and their run state, with the stated purpose of managing multiple session instances. It defines the state vocabulary (running/idle/detached) so the agent knows exactly what 'run state' means, distinguishing it from siblings like dsh_status or dsh_read.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use only_running=true to see only active sessions, and use dsh_read next to see a session's output. It does not address when to prefer dsh_get/dsh_status over this tool, so it falls short of full alternative coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_readA
读会话的运行期输出(游标式增量)——用它可以看到"正在跑的会话"当前吐出了什么,不必等回合结束。典型用法:dsh_list(only_running) 找到在跑的会话 → 反复 dsh_read 并回传上次的 cursor,就能像切换窗口一样轮流观察多个会话。输出缓冲只在内存里且有界(环形,满则丢最旧),服务/进程退出即消失;思考内容不进缓冲——只有 include_reasoning=true 时才会附带"当前正在跑那一轮"的实时思考。
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 单次最多返回多少条,默认 200。 | |
| cursor | No | 上次返回的 cursor;首次不传或传 0。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 | |
| include_reasoning | No | 附带当前回合的实时思考内容,默认 false。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: the buffer is in-memory, bounded, ring-shaped, drops the oldest entries when full, and disappears when the service/process exits. It also discloses that reasoning is excluded from the buffer unless include_reasoning=true, and that only the currently running turn's reasoning is attached.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and the cursor/polling model, with buffer caveats after. Dense but every clause carries information; the parenthetical bolding and workflow sentence are slightly heavy but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the behaviors that determine correct invocation: polling with cursors, bounded retention, and the reasoning flag. With no output schema, it does not describe the shape of returned entries or the cursor field in the response, a minor remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds genuine meaning beyond it by explaining the cursor round-trip pattern (pass the previous cursor) and the exact semantics of include_reasoning (only the in-flight turn's live reasoning). Limit and conversation_id are left to the schema, which is acceptable at this coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (read a conversation's runtime output), plus the key qualifier that it is cursor-based incremental. Explicitly frames itself against the alternative workflow of waiting for a turn to finish, so an agent can tell why this tool exists versus dsh_get or a blocking call.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete step-by-step scenario: run dsh_list(only_running) to find live conversations, then poll dsh_read repeatedly, passing back the previous cursor, to rotate across multiple sessions. That is explicit when-to-use guidance naming the sibling tool to start from.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_releaseA
释放会话占用的 DSH 进程(释放写锁,让用户能在自己的 DSH GUI 里打开同一个会话)。会话本体留在磁盘上,之后仍可 resume;forget=true 才会从注册表里摘掉。★ 用完了想一次性腾内存就传 all=true(默认"永不回收"下每个会话约 300-480MB,全放掉即立刻归零,之后随时 resume)。
| Name | Required | Description | Default |
|---|---|---|---|
| all | No | 忽略 conversation_id,释放**所有**活着的会话进程(腾内存用)。默认 false。 | |
| forget | No | 同时从本服务的注册表里移除,默认 false。 | |
| conversation_id | No | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the write-lock release, that the session persists on disk, that forget=true mutates the registry, and quantifies the memory footprint (~300-480MB per session) plus the 'never reclaim' default. It is silent on what happens to in-flight work in a running session at release time and on error behavior for an invalid id.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Each clause earns its place — semantics, persistence, forget, then the all=true memory tip — and the core action is front-loaded before the flags. It is somewhat dense and the ★/quote styling is decorative rather than structural, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param mutation tool with no annotations and no output schema, the description covers the essential decision points: what is destroyed, what survives, and when to use each flag. Missing only edge-case behavior (invalid conversation_id, active-session handling) and return shape.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3; the description adds real meaning beyond the schema by explaining the memory rationale for all=true, the 'ignores conversation_id' interaction, and the registry-mutation consequence of forget=true. The interaction semantics (all overrides conversation_id) are the valuable addition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (release the DSH process / write lock held by a session) and immediately clarifies the scope: the session body stays on disk and remains resumable. The distinction between process release, registry removal (forget=true), and mass release (all=true) lets an agent separate this from siblings like dsh_interrupt or dsh_takeover.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance for each option: forget=true only when you want the registry entry gone, all=true when you want to reclaim memory all at once, otherwise the session stays resumable. It does not explicitly contrast with dsh_interrupt/dsh_takeover, which would have made the routing unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_sendA
向会话派一个任务。默认不阻塞(wait=false):立即返回 run_id + sentinel_file,回合在后台跑,跑完自动通知你 —— 你继续做别的事,也永远不用担心任务被"超时"打断。想原地等结果就显式传 wait=true(可配 timeout_ms;到期只会转后台,不会取消回合)。【超时不会毁掉任务】wait=true 到达 timeout_ms 仍没跑完时,本工具不会报错、也不会取消回合,而是自动降级为后台:返回 run_id + sentinel_file,回合继续跑完,结果稍后由哨兵文件通知。(timeout_ms=0 表示一直等。)所以任何时候你都能拿到结果,不用怕"超时了白干"。等待模式与后台模式都在回合结束时原子落地一个含最终结果的小文件——推荐用后台任务(Bash run_in_background + until [ -f sentinel_file ])等它,跑完自动通知你,无需轮询;文件出现后读它即得结果,或用 dsh_get(conversation_id, run_id) 验收,dsh_read 可中途看输出。默认隐藏思考内容(只返回正文 + 思考统计)。若会话正在忙,本工具会报错——改用 dsh_interject 插话,或用 dsh_interrupt 先打断。
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | **默认 false**:立即返回 run_id + sentinel_file,回合在后台跑,跑完由哨兵文件通知(推荐用法)。true = 原地阻塞到回合结束再返回完整结果。 | |
| prompt | Yes | 任务描述。可以引用工作区里的文件路径。 | |
| reasoning | No | 思考内容展示档位。默认 hide(只返回正文,思考只给统计)。full 会占用大量上下文。 | |
| timeout_ms | No | 仅 wait=true 时的**等待**上限(毫秒),默认 30 分钟;0 = 一直等。到期不会取消回合,只会转成后台并给你 run_id。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden and does so: it discloses that a timeout does not error and does not cancel the turn but degrades to background, that results land atomically in a sentinel file, that reasoning is hidden by default with a context-cost warning for `full`, and that a busy conversation produces an error. These are non-obvious failure-mode behaviors an agent could not infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The default non-blocking behavior is correctly front-loaded, but the same point — that timeouts never destroy the task — is made three separate times with heavy bolding, and the result-retrieval mechanics appear twice. Substantial length is spent on emphasis rather than new information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a dispatch tool with no annotations and no output schema, the description covers everything needed: what is returned (run_id + sentinel_file), how to obtain the final result (sentinel file, dsh_get, dsh_read mid-run), default reasoning visibility, and the error path. An agent has no unanswered question before calling it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the per-parameter descriptions already cover the wait default, timeout_ms degradation semantics, reasoning levels, and conversation_id provenance. The prose largely restates those same facts rather than adding new meaning, so the baseline 3 applies; the only marginal addition is the cross-parameter framing that timeout_ms matters solely when wait=true.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and resource (dispatch a task to a conversation) and immediately distinguishes the tool's two modes. It also names the siblings it is not for the busy case (dsh_interject, dsh_interrupt) and the verification path (dsh_get, dsh_read), so an agent can place it in the family without opening other schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use: default wait=false for fire-and-forget, explicit wait=true to block in place, and a recommended Bash run_in_background + `until [ -f sentinel_file ]` pattern instead of polling. It also states the negative case — if the conversation is busy the tool errors and you should use dsh_interject or dsh_interrupt — which is exactly the alternative routing an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_startA
新建一个 DSH 会话(一个可长期共事的编码 agent)。会话活在磁盘上,进程只是缓存;进程被回收或崩溃后会自动用 ACP session/resume 复活,对话不丢。返回 conversation_id 与对话标题。建好后用 dsh_send 派活。
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | Yes | 工作区绝对路径。决定 agent 的工作目录、AGENTS.md/技能的发现范围、以及沙箱可写根。 | |
| model | No | 模型名(如 deepseek-flash)。指定时必须同时给 provider。 | |
| provider | No | 模型提供方名(如 deepseek-official)。留空用 profile 默认。 | |
| permission | No | 权限档。默认 danger-full-access。read-only 适合分析不信任样本(如恶意软件);workspace-write 越界操作会冒泡为待决审批。**档位在创建时定下**:DSH 设计上"resume 的会话保留它自己记录的权限",所以事后改这个参数不会改变已有会话,要换档需新建。(本档位是**真实生效**的 —— 由启动时传入的 env 决定,并有 test/permission.mjs 对着会话自己的记录逐档核验。) | |
| title_hint | No | 可选的标题提示(DSH 的标题由首条消息自动生成,这里只是兜底显示)。 | |
| on_approval | No | 审批处理策略。默认 read-only → auto-deny,其余 → auto-allow。ask 会把审批挂起,需用 dsh_approval_decide 裁决。 | |
| reasoning_effort | No | 思考深度,默认 max。注意 ACP 自身的默认是"Provider default"(不设),本服务会显式设成 max。想省钱可调低。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and discloses significant behavioral traits: the session lives on disk, the process is just cache, and auto-resurrection via ACP session/resume prevents conversation loss. It does not cover failure modes, resource cleanup needs, or auth requirements, but the core persistence behavior is well explained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded: it opens with the core purpose, then adds relevant behavioral details and next steps in a few short clauses. Every sentence earns its place and there is no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of annotations and an output schema, the description provides a solid overview of what the tool does, its persistence behavior, return values, and next steps. However, it lacks guidance on when not to use it and omits mention of cleanup (e.g., dsh_release) or error handling, leaving minor gaps for full contextual completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already fully documents all seven parameters. The description adds no parameter-specific meaning beyond what is in the schema, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('新建') and resource ('DSH 会话'), and distinguishes it from the sibling tool dsh_send by explaining that after creation you use dsh_send to assign tasks. It also clarifies the return values (conversation_id and title), making the tool's purpose immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage context: it is for creating a new session, and it explicitly points to dsh_send for the next step. However, it does not state when not to use it (e.g., for resuming an existing session instead of creating a new one) or mention alternatives like dsh_get or dsh_list, leaving some guidance implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_statusA
轻量状态查询:会话是否活着、是否在忙、待决审批数、最近一次回合的结果。
| Name | Required | Description | Default |
|---|---|---|---|
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden; it does signal a read-only query and tells what the payload contains, which is the most important behavioral fact here. However, it says nothing about latency, whether the status is cached, or how recently-it-is-fresh the 'recent turn result' is, which matters for an agent polling state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no waste — the qualifier '轻量' leads and the returned fields follow immediately. Nothing could be removed without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully enumerates the returned fields, which is exactly the gap it needs to fill for a one-parameter query tool. Only the freshness/latency dimension of the status snapshot is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is only one parameter and schema coverage is 100%; the schema already documents that conversation_id comes from dsh_start or dsh_list. The description adds no further syntax or format guidance, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('轻量状态查询') and enumerates the exact fields returned (liveness, busy flag, pending approval count, last turn result), which is more precise than the name alone. It does not explicitly contrast with close siblings like dsh_get or dsh_read, so an agent still has to infer the difference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The word '轻量' implies this is the cheap first-look call, but there is no explicit statement of when to prefer it over dsh_get or dsh_read, and no prerequisites or exclusions. Usage is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dsh_takeoverA
抢占会话的写锁(DSH 的写锁是跨进程内核信号量,持有者活着就不过期,没有 API 能直接夺走——唯一手段是杀掉持有者进程,所以本工具会先判定持有者是谁):① 若锁其实空着,正常 resume 即可,不会杀任何东西;② 若持有者是本服务自己留下的孤儿进程(MCP 崩了/重启了,子进程还活着握着锁)→ 自动杀掉并接管,安全;③ 若持有者是另一个活着的 dsh-any-agent 实例 → 默认拒绝,传 force=true 才夺;④ 若持有者无法识别(多半是你自己的 DSH GUI 正开着这个会话)→ 绝不杀(那会连你的界面和正在看的对话一起杀掉),只报告,请你在那个窗口里关掉该会话后重试。注意:杀掉正在跑回合的持有者会中断那一轮(会话日志记为 interrupted),会话本体不受损。
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | 仅用于"另一个活着的 dsh-any-agent 实例持有"这一种情况;对 GUI/未知持有者无效(永远不杀)。 | |
| conversation_id | Yes | 会话 id,来自 dsh_start 或 dsh_list。 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: it discloses that the tool is destructive (kills a process), that killing a holder mid-turn interrupts that turn and marks the log as interrupted while leaving the conversation intact, and that unidentified/GUI holders are never killed. The safety boundary of force=true is also spelled out, which is exactly the kind of context annotations would otherwise supply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The numbered case structure (①②③④) is genuinely functional and every clause carries decision-relevant information. It is dense and the long parenthetical in the opening sentence delays the concrete action slightly, so it is not maximally front-loaded, but nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, no-annotation, no-output-schema tool, the description covers the mechanism (kernel semaphore, no revoke API), the failure modes (orphan vs. foreign instance vs. unknown holder), the side effects (interrupted turn, intact conversation), and the user-recovery path. An agent has everything needed to invoke it or correctly abstain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both params are already documented, hitting the baseline 3, but the description adds real behavioral meaning for force: it applies only to case ③ (another live dsh-any-agent instance) and has no effect on GUI/unknown holders because those are never killed. conversation_id gains nothing beyond the schema, keeping this below a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific action on a specific resource: seize the conversation's write lock, explaining that the lock is a cross-process kernel semaphore and that the only takeover mechanism is killing the holder process. It explicitly contrasts itself with the ordinary 'resume' path (case ①), so an agent can tell it is an escalation tool rather than the normal way to attach to a session.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It enumerates four exhaustive situations with explicit decisions: free lock → do nothing and resume; orphaned child of this service → kill and take over; another live dsh-any-agent instance → refuse unless force=true; unidentified holder (likely the user's own DSH GUI) → never kill, report and ask the user to close it. When-to-use, when-not-to-use, and the only override are all stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
11 tool updates
v0.1.0- First observed
dsh_approval_decide - First observed
dsh_get - First observed
dsh_interject - First observed
dsh_interrupt - First observed
dsh_list - First observed
dsh_read - First observed
dsh_release - First observed
dsh_send - First observed
dsh_start - First observed
dsh_status - First observed
dsh_takeover
TDQS
Scored across 11 tools
Most tools target clearly distinct operations (start, send, interrupt, release, takeover, approval_decide), but there is real overlap between dsh_get, dsh_status, and dsh_read — all report on a session's state/output — and between dsh_interject (interrupt mode) and dsh_interrupt. The descriptions do disambiguate these (read=cursor-based live output, status=lightweight check, get=full detail/run result), so confusion is limited but not zero.
All names use a consistent dsh_ prefix with snake_case, and 9 of 11 follow a clean verb style (start, send, release, list, read, get, interject, interrupt, takeover). The deviations — dsh_approval_decide (noun_verb) and dsh_status (noun) — are minor and still readable.
11 tools is well within the sweet spot for a session-management server, and each tool maps to a distinct lifecycle concern: create, dispatch, observe, steer, cancel, reclaim, and approve. Nothing feels redundant in count.
The lifecycle is well covered: create, send, list, read, get, interject, interrupt, release (with forget), takeover, and approval handling, plus auto-resume via ACP. Minor gaps: no explicit resume/open tool, no way to change a session's permission profile or on_approval setting after creation, and no delete/purge beyond release+forget.
Maintenance
Related MCP Connectors
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Remote MCP server exposing your Node Command fleet (files, shell, processes, browser) to AI hosts.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceLets any MCP-capable coding harness (Cursor, Claude Code, Codex, Gemini CLI, VS Code/Copilot, opencode, and others) hand a self-contained task to a real, isolated DeepSeek Harness process that works in a chosen workspace with its own context window, model, and toolchain, then returns the final answer. Exposes task submission with time estimates and acceptance criteria, status polling with live progress and process-tree telemetry, graceful cancel and forced kill of whole process trees, and a health probe reporting concurrency, deadlines, and recent jobs.MIT
- AlicenseNot gradedqualityBmaintenanceLets Claude Code, Codex, or any MCP client drive DeepSeek Harness as a long-lived coding agent: starting and resuming conversations, dispatching tasks in the background, streaming live output and reasoning stats, interjecting or interrupting mid-turn, handling approval prompts, and collecting results later. Sessions persist by ID, so work survives process restarts and can be handed back to a DSH GUI when needed.325 npm15MIT
- AlicenseNot gradedqualityAmaintenanceEnables any MCP-capable client (Claude Code, Cursor, VS Code, and others) to deliver messages into a local DeepSeek Harness over stdio or Streamable HTTP. It also exposes a pull-based task queue so producers can publish work that clients claim, complete, or list.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP-compatible clients to delegate complex multi-step programming and research goals to coordinated teams of specialized subagents, with cross-session messaging, swarm and pipeline workflows, sandboxed workspace tools, persistent SQLite state, and a live dashboard.1MIT