← Back to home@Leawind

dsh-carrot-on-a-stick

Operate DeepSeek Harness (dsh) over MCP: expose sessions, task execution, and workspace management to any MCP client.

Stars
0
Language
TypeScript
Created
Sep 22, 2026
Updated
Oct 1, 2026
GitHub repo

Introduction

中文English

DSH Carrot on a Stick

A DSH plugin that exposes DSH operations through the MCP protocol。

MCP features

Tools

ToolPurpose
dsh_get_starteddsh crash course for the agent — concepts, recipes, error recovery. Call this first
echoconnectivity check
dsh_statusplugin status: version, listening, uptime, queue counters, connected clients
dsh_list_toolslist the host's global tool registry
workspace_listregistered workspaces and their sessions
model_listroutable providers / models / reasoning efforts, and the current default
agent_runrun a task now, get a structured result back; sessionId continues a session; per-call provider / model / reasoningEffort / preset; cancellable, with progress heartbeats
agent_steermid-run guidance to a running agent — steer acts at the next step boundary, inject adds context only
task_inboxqueue a background task, get a taskId; idempotencyKey dedupes retries
task_resultfetch a queued task's result (detail: "status" for cheap polling)
task_listqueue overview: queued / running / finished
task_cancelcancel a queued (immediate) or running task (cancelling → cancelled)
session_listknown sessions (live + persisted), newest first
session_historyrecent turns of a session; roles filter (e.g. ["assistant"])
select_modelswitch an existing session's model, history preserved
attach_sessionattach a session to the workspace of its cwd
rename_sessionrename a session

Resources

Resource-capable clients can read the same data without spending tokens, and subscribe to changes instead of polling:

URIContent
dsh://status[/config|/stats|/connections]deployment status
dsh://guide[/{section}]dsh usage guide (markdown)
dsh://toolshost global tool registry
dsh://models[/{provider}]model catalog
dsh://presetslegal preset ids
dsh://workspaces[/{id}]workspace registry
dsh://sessions[/{id}]session list / metadata
dsh://sessions/{id}/history[/{before}]conversation digest (paged)
dsh://sessions/{id}/events[/{after}]raw event stream (JSONL, paged)
dsh://sessions/{id}/activitylive turn window
dsh://queue[/{taskId}]queue / task detail incl. the full result
dsh://agentsresident session pool

resources/subscribe pushes task status transitions, turn-end history refreshes, and live activity over SSE — no polling. Setting resourceFirst: true in the config retires the nine read-only tools and returns results as resource references (for clients confirmed to support MCP resources; default false keeps tools and resources coexisting).

Everyday patterns

One-off task — synchronous, structured summary back; continue later with sessionId (send only the delta in context):

{ "name": "agent_run", "arguments": { "task": "fix the failing test in src/auth", "cwd": "/workspace/app" } }

Background queue — submit, poll cheaply, fetch once done:

{ "name": "task_inbox",   "arguments": { "task": "…", "cwd": "/workspace/app" } }
{ "name": "task_result",  "arguments": { "taskId": "…", "detail": "status" } }
{ "name": "task_cancel",  "arguments": { "taskId": "…" } }
{ "name": "task_result",  "arguments": { "taskId": "…" } }

Sessions — session_list to find one, session_history to read it, select_model to switch its model, agent_steer to redirect it mid-run.

Models & presets

  • Priority: per-call arguments > plugin config > host default. A half-set pair completes from the lower source; unresolvable calls fail loudly.
  • Three ways to change models: per-call provider+model · select_model mid-conversation · pin via config with allowModelOverride: false. Presets (preset argument, for new sessions) work the same way under allowPresetOverride.
  • Sessions are pooled per cwd + model + preset: same directory, different model/preset → a separate session. Taking over a session keeps its model and preset.
  • Continuing by sessionId runs in the session's own directory; an explicitly passed cwd must match it.

Result size — detail controls token cost: summary (default, a few hundred tokens) / normal (~2k) / full (debugging); task_result adds a status level for polling. Results are structured (sessionId, model, changes, verification, leftovers, …); failures come back with isError: true.

Configuration (cordis.yml)

- insert:
      - id: dsh-carrot-on-a-stick
        name: 'dsh-carrot-on-a-stick'
        config:
            http: true
            port: 8090
            host: 127.0.0.1 # localhost only by default; add auth before exposing
            # authToken: 'your-secret-token'
            # workspaceRoots: ['/workspace']   # cwd whitelist (also gates session surfaces)
            # allowedHosts: ['my-box.lan']     # extra allowed Host header values
            # preset: 'standard'               # agent preset to mount
            # model: ''                        # pair with provider; empty = follow host default
            # provider: ''
            # reasoningEffort: ''              # empty = adapter default
            # allowModelOverride: true         # false = pin the model
            # allowPresetOverride: true        # false = pin the preset
            # queuePersistPath: '.dsh/queue.json'
            # queuePersistKey: 'passphrase'    # encrypt the persistence file
            # sessionTtlMs: 86400000
FieldDefaultMeaning
host / port127.0.0.1 / 8090MCP server bind address; a listen failure fails plugin startup
authToken—Bearer token; enforced on every request when set
workspaceRoots—cwd whitelist — agents may only work inside listed directories; also limits which sessions can be taken over or read
allowedHosts—extra allowed Host-header values (bind host and loopback always allowed)
provider / modelfollow host settingsdefault model for spawned agents; configure as a pair
reasoningEffortadapter defaultdefault reasoning effort (ids in model_list)
allowModelOverridetruewhether callers may override the model per call
allowPresetOverridetruewhether callers may pick a preset for new sessions
presetstandardagent preset to mount (per-call preset applies to new sessions only)
defaultDetailsummarydefault detail level for agent_run / task_result
resourceFirstfalseresource-first mode: read-only tools retire, results come back as resource references
reattachOrphansfalsebulk-attach ungrouped sessions to workspaces at startup
maxQueue / taskTtlMs / maxAgents100 / 24 h / 8queue capacity, result retention, session-pool size
taskTimeoutMs0 (off)auto-cancel an agent turn after this long
progressIntervalMs5000 (min 250)progress heartbeat interval
queuePersistPath— (off)persist the task queue; finished results survive restarts, queued tasks re-execute, running tasks are marked interrupted
queuePersistKey— (plaintext + warning)encrypts the persistence file; wrong passphrase / corrupt file → queue starts empty
sessionTtlMs86400000idle MCP transport sessions are reaped after this (0 = never)

Security

⚠️ This plugin exposes local code execution (equivalent to RCE). It binds 127.0.0.1 only. When enabling:

  1. Set authToken — protects against other local processes and DNS rebinding;
  2. Set workspaceRoots — without it there is no directory boundary at all; with it, execution and session reads (list / history / steer / takeover) are both constrained;
  3. Never bind 0.0.0.0 or expose to LAN/WAN without a reverse proxy + TLS + auth.

Built-in guards: Host- and Origin-header allowlists (DNS-rebinding guard), HTTP surface limited to /mcp, 401 answers with a Bearer challenge, idle transport sessions reaped after sessionTtlMs. Note that spawned sessions follow the host/preset approval policy — this plugin runs unattended, so ask-style policies may pop dialogs or fail closed; verify sensitive operations under your preset before deploying. If you enable queuePersistPath, task payloads are written to that file (plaintext unless queuePersistKey is set) — choose a location with appropriate permissions.

Web settings panel

The bundle adds a Settings → dsh-carrot-on-a-stick section to the dsh web UI: live status badge, the MCP endpoint (click to copy), start/stop buttons, connected client list, and queue counters.

Known limitations

  • Spawned sessions follow the host/preset approval policy — ask-style policies may pop a dialog or fail closed in unattended use.
  • select_model requires the web profile's sessionController; headless deployments fall back to the model catalog plus per-call overrides.
  • dsh_list_tools only lists the host-global registry; per-agent tool visibility needs a host-side API.
  • Without queuePersistPath the queue lives in memory and is lost on restart.

Acknowledgements

Started as a copy of chushixixin/dsh-harness-mcp-server (MIT, thanks @chushixixin), now an independent project.