DeepSeek Harness Desktop MCP
by omokom
README.md
# DeepSeek Harness Desktop MCP
Loopback-only MCP bridge that lets a local MCP client drive an **already-open**
DeepSeek Harness Desktop session: create standard-mode tasks, read bounded output,
continue, cancel, and inspect compatibility diagnostics. The Desktop runs the model;
the bridge exposes tools and requires a Bearer token from a dedicated external file.
Tested on Windows with Node.js 24 and DeepSeek Harness Desktop `0.2.1-rc.2`. Other
platforms and Desktop versions are unverified. MIT licensed.
## Capabilities
- Loopback-only HTTP MCP endpoint at `http://127.0.0.1:47821/mcp` (host fixed to
`127.0.0.1`) with mandatory Bearer authentication.
- Six MCP tools:
- `list_sessions` — bounded metadata for the configured default workspace only;
- `submit_task` — creates a fresh standard-mode session (configured default
workspace, or one explicitly enrolled project directory) and admits a task with
an explicit idempotent `requestId`;
- `get_task` — bounded visible output with a resumable `{seq, offset}` cursor,
status, and durable completion evidence;
- `continue_task` — steer or follow up on the same authorized session with an
explicit `requestId`;
- `cancel_task` — official active-turn cancel; it does not dispose the session or
clear its inbox;
- `diagnostics` — bridge/protocol, model-route, workspace, and tool-limit facts.
- Fixed route `deepseek-official` / `deepseek-flash` / `reasoningEffort: max`.
Bridge-side checks fail closed on a mismatch; there is no model fallback.
- Sessions are created with `agentPreset: standard`; non-standard or unknown presets
are not silently adopted on continuation or resume.
- Admission is not completion. Success requires consumed-input and completion
evidence newer than the request baseline. No headless fallback and no background
auto-updater are built in.
## Requirements
- Windows (tested; other platforms unverified).
- Node.js 24 or newer.
- DeepSeek Harness Desktop `0.2.1-rc.2` or a compatible build providing
`ctx.sessionController`, `ctx.sessionQuery`, `ctx.agents`,
`ctx.workspaceRegistry`, and `ctx.agentDefaultModel`.
- Desktop open and configured with the fixed model route above. The bridge runs
inside the Desktop process; it does not start a Host.
- A private writable location for the token and non-secret bridge state, outside Git.
## Install
1. Install dependencies (network access to the npm registry is required):
```powershell
npm ci
```
`npm ci` uses the committed lockfile. The repository `.npmrc` disables install
scripts; no lifecycle scripts are required.
2. Create the dedicated bearer token in a private external file:
```powershell
npm run create-token
```
Default path: `%DSH_HOME%\desktop-mcp\token`, otherwise
`%USERPROFILE%\.dsh\desktop-mcp\token`. To choose another path:
```powershell
node scripts/create-token.mjs --token-file "C:\secure\deepseek-desktop-mcp.token"
```
The script refuses to overwrite an existing file, prints only the path, and never
prints the token. Keep the file private; never commit it or paste its value into
logs or chat. The plugin requires at least 32 characters and fails closed
otherwise.
3. Configure the Desktop plugin. Edit [cordis.patch.yml](cordis.patch.yml) so
`workspaceId` is your Desktop's **default workspace id**. The id committed in
this repository is the author's local example and is not portable; `src/config.js`
also carries the same local value as a fallback, so set `workspaceId` explicitly.
Keep `port: 47821` unless it is already in use. `tokenFile` and `workspacesFile`
are optional; their defaults are `$DSH_HOME/desktop-mcp/token` and
`$DSH_HOME/desktop-mcp/workspaces.json`.
4. Install this checkout as a local bundle through the Desktop **Plugins** UI. The
bundle patch inserts the `deepseek-desktop-mcp` listener row; enable the row, then
toggle it Off/On after changes. If Desktop reports that a restart is required,
restart it. Do not hand-edit `app.asar` or the Desktop's reserved profile.
5. Run the read-only compatibility check:
```powershell
$env:DEEPSEEK_DESKTOP_MCP_ROOT = "<checkout>"
powershell -NoProfile -ExecutionPolicy Bypass -File "<checkout>\integration\skill\deepseek-executor\scripts\invoke-desktop.ps1" -Check
```
The launcher resolves the checkout from `-ProductRoot <checkout>` or
`DEEPSEEK_DESKTOP_MCP_ROOT`; set one of them. A successful `CHECK` line reports
the tool list, `bridge.protocol` (`deepseek-desktop-mcp/bridge/2`), the
`workspacePath` and `standardMode` capabilities, the configured model route, and
the configured default workspace. `stale_bridge` means the loaded bundle is
older than the checked-out code; `model_route_mismatch` means Desktop's route is
not the required route. The check never submits a task. After `npm ci`,
`node src/dispatch.js --check` is the equivalent direct form.
## CLI: run and control tasks
The launcher is [invoke-desktop.ps1](integration/skill/deepseek-executor/scripts/invoke-desktop.ps1),
a wrapper around [src/dispatch.js](src/dispatch.js) with the same options, exit codes,
and per-request state/log files. Set up paths once:
```powershell
$Repo = "<checkout>"
$Launcher = Join-Path $Repo "integration\skill\deepseek-executor\scripts\invoke-desktop.ps1"
$env:DEEPSEEK_DESKTOP_MCP_ROOT = $Repo
```
New projectless task (configured default workspace):
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File $Launcher -TaskFile .\task.md -LogFile .\run-1.log
```
New task in an exact project directory (explicit opt-in):
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File $Launcher -TaskFile .\task.md -Workspace "C:\work\my-project" -LogFile .\run-2.log
```
Continue the same Desktop session:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File $Launcher -TaskFile .\task.md -SessionId <sessionId> -LogFile .\run-3.log
```
Resume after exit 3 or 5, using a new log file:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File $Launcher -TaskFile .\task.md -ResumeState .\run-3.log.state.json -LogFile .\run-4.log
```
For a continuation resume, add `-SessionId <sessionId>` to the resume command.
Cancel the active turn:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File $Launcher -Cancel -StateFile .\run-3.log.state.json
```
The first dispatch prints `ADMITTED {...}` with the `sessionId`, `requestId`,
resolved workspace, and state path, then a bounded `RESULT {...}`. A new request
needs a new `-LogFile`; state defaults to `<log>.state.json`. Cancel writes a
separate `<state>.cancel.json` receipt and does not delete the execution state.
Exit codes: `0` completed, `2` failed, `3` blocked/pending, `4` canceled,
`5` running or wait timeout, `6` preflight/state/network error, and `130`
interrupted by Ctrl-C (the Desktop task keeps running). Exit 3 and 5 are resumable
with `-ResumeState`; they are not success. Run `node src/dispatch.js --help` after
`npm ci` for the raw option list.
## Workspace routing
| Request | Target workspace | Behavior |
|---|---|---|
| New task, no `-Workspace` | Configured default workspace | Submits without `workspacePath`; enrolls nothing and creates no extra workspace. |
| New task, explicit `-Workspace <dir>` | That exact existing project directory | Launcher canonicalizes and adds the directory to the non-secret allowlist; the bridge resolves or reuses the workspace. |
| Continuation, no `-Workspace` | The session's existing workspace | Derived from the session; the session is never re-homed. |
| Continuation with `-Workspace` | Must equal the session workspace | A mismatch is refused before `continue_task`. |
| `-ResumeState`, no explicit workspace | The workspace stored in that state | Request id, task hash, workspace, and target session are verified; default changes do not re-home a stored request. |
Notes:
- The configured default workspace is **not** a fallback for an invalid explicit
project path or an unavailable default diagnostic.
- There is **no automatic Codex project detection**. A cwd, task-book directory, log
directory, or artifact directory is an execution location, never project identity;
only an explicit `-Workspace` selects a project workspace for a new task.
- `list_sessions` lists only the configured default workspace. Use `get_task`,
`continue_task`, and `cancel_task` with session ids for enrolled project sessions.
- The allowlist is non-secret and holds exact canonical directories only. Roots,
parents, globs, wildcards, unknown directories, and symlink escapes are refused.
## Standard mode and model route
- `submit_task` creates the session with an explicit `agentPreset: standard`; it
never uses the default agent and never changes the global default.
- Continuation and resume reject non-standard or unknown presets before prompting.
- The required route is `deepseek-official` / `deepseek-flash` /
`reasoningEffort: max`. Diagnostics reports the configured route and whether it
matches; a mismatch fails closed. The bridge does not change Desktop model
settings and has no account or model fallback.
## Optional: register as a native Codex MCP server
Copy [mcp-registration.toml.template](integration/codex/mcp-registration.toml.template)
and replace `{{HEADER_HELPER}}` with the absolute path to
`integration/skill/deepseek-executor/scripts/header-helper.ps1`:
```toml
[mcp_servers.deepseek_desktop]
url = "http://127.0.0.1:47821/mcp"
required = false
http_headers_helper = 'powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\path\to\integration\skill\deepseek-executor\scripts\header-helper.ps1"'
```
The helper reads the token from the default external token path and prints only the
`Authorization` header as JSON; no secret is stored in the TOML. If you configured a
custom `tokenFile`, append `-TokenFile "<absolute-path>"` to the helper command.
Start a fresh Codex session/turn after changing MCP configuration so the server and
its tools are loaded.
Any MCP client can also connect directly to the endpoint with an
`Authorization: Bearer <token>` header, reading the token from the external token
file.
## CLI or MCP?
Both interfaces use the same bridge, runtime, token, and Desktop session. Choose the
one that fits the caller:
| Interface | Good fit | What you get |
|---|---|---|
| CLI launcher (`invoke-desktop.ps1`) | Scripted executor lifecycle, retries, resumes, local orchestration | `ADMITTED`/`RESULT` output, JSONL logs, explicit exit codes, per-request state files |
| Native MCP tools | MCP-capable clients and agent integrations | Direct tool calls with structured results and idempotent `requestId` handling |
Routine CLI/MCP calls need no Computer Use. Computer Use is only for Desktop UI
installation or reload.
## Token, allowlist, and state
- The bearer token is external, private, and never committed or printed. Missing,
empty, short, or unreadable token files fail closed. The plugin listener binds only
to loopback.
- The workspace allowlist (`workspaces.json`) is non-secret. The configured default
workspace is seeded; an explicit project opt-in appends that exact canonical
directory before submit. A corrupt or unreadable allowlist makes explicit project
submissions fail closed.
- Admission state (`admissions.json`) holds request/session ids, input hashes, modes,
phases, and resolved workspace facts — never task text. State/log writes are
atomic; corrupt reads fail new admissions closed.
- Each dispatch uses one `<log>.state.json` plus a UTF-8 JSONL log. Logs may contain
visible task output, including sensitive content; treat logs and state as private.
A cancel receipt is written separately and never overwrites the execution state.
## Migration and legacy helpers (optional, environment-specific)
- [scripts/migrate-codex.mjs](scripts/migrate-codex.mjs) is an optional migration
helper for a legacy local Codex MCP registration/skill setup. It is not required
for normal use, is not portable as-is, and this repository does not ship an
applied migration state.
- The default mode prints a dry-run plan. `--apply` journals original fingerprints
and backups before mutating anything; `--rollback` restores from a manifest,
interrupted journal, or orphaned backup directory. Review
`node scripts/migrate-codex.mjs --help` and the machine-specific defaults
(`--codex-home`, `--agents-skills`, `--skill-source`, `--workdir`,
`--default-workspace`, `--product-root`, `--private-root`, `--workspaces-file`)
before applying anything, and keep the plan and backups outside Git.
- [invoke-harness-shim.ps1](integration/codex/invoke-harness-shim.ps1) is an optional
compatibility shim; replace `{{CANONICAL_LAUNCHER}}` with the canonical launcher
path before use. [invoke-harness-recovery.ps1](integration/skill/deepseek-executor/scripts/invoke-harness-recovery.ps1)
is an explicit legacy headless recovery path only; it requires
`-IUnderstandRecovery` (or `DEEPSEEK_EXECUTOR_RECOVERY=1`) and is never an
automatic fallback. Normal use is the Desktop bridge.
## Honest limitations
- Desktop must stay running; the plugin lives inside the Desktop process and cannot
start or revive a Host. Closing Desktop stops control.
- The fixed model route is mandatory. Change Desktop's route yourself if needed; the
bridge never changes model settings and has no model or account fallback.
- `list_sessions` is scoped to the configured default workspace. Enrolled project
sessions require explicit session ids.
- There is no automatic Codex project detection and no cwd-based project guess; an
explicit project must be the exact existing directory you intend to use.
- There is no background auto-updater or update framework. Upgrade by replacing the
checkout and reloading the bundle (Plugins Off/On; restart if Desktop requests it).
- Admission is not completion, and cancellation does not dispose a session or clear
its inbox. No performance or cross-platform claims are made beyond the tested
Windows/Node 24/Desktop context above.
## Development checks
```powershell
npm run check
npm test
```
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues