dsh-codex-mcp
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-codex-mcpAdd typed input support to src/sum.mjs and run its node:test tests."
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-codex-mcp
A DSH host plugin that runs a loopback-only Streamable HTTP MCP server inside the DeepSeek Harness process, so Codex can dispatch coding work to DSH over MCP — no shell wrapper, no per-call script.
中文说明 · MIT · zero runtime dependencies
Codex CLI
└─ MCP (Streamable HTTP + Bearer token) http://127.0.0.1:39391/mcp
└─ DSH host plugin dsh-codex-mcp (lives inside the DSH process)
└─ node <dsh install>/lib/bin.js --profile headless --json
└─ DSH reads/writes code, runs commands, streams NDJSON eventsDSH is still the agent that writes the code. Codex only dispatches the task, reads back a structured result, and re-runs the acceptance command itself.


Why a plugin and not a DSH tool
A regular DSH agent tool (ctx.tools) is visible only to DSH's own model — Codex cannot see it. To be callable from another agent, the capability has to leave the process. This plugin therefore:
starts an HTTP MCP endpoint on
127.0.0.1when the DSH host loads it, and closes it when the plugin unloads (ctx.effectreturns a disposer);speaks the MCP Streamable HTTP transport and legacy JSON-RPC directly — the package deliberately has no dependencies, so there is no MCP SDK or
@deepseek-ai/*package to resolve inside a profile;reuses the DSH install that hosts it: it finds
@deepseek-ai/dsh'slib/bin.jsnext toresources/app, or whereverDSH_APP_ROOT/dshClipoints.
Related MCP server: dsh-browser-bridge
Features
Three MCP tools —
dsh_env,dsh_run,dsh_resume. New sessions and follow-ups both carry the session id back to the caller.Structured results — final answer,
session,cwd,exit,turn_end, per-turn token usage, step count, duration; failures also return the stderr tail and a classified reason.Honest failure — a call is an MCP error when: it timed out, the child exited non-zero,
turn_endwas notcompleted, nofinalevent arrived, or the final answer is empty. DSH's own "I'm done" is never taken at face value by the plugin.Process-tree kill on timeout (
taskkill /T /Fon Windows, process group elsewhere), so no orphaned agents.Concurrency cap (
maxConcurrentRuns, default 4) — extra calls fail fast instead of queueing silently.Loopback-only + Bearer token — token generated on first start (
32random bytes,0600), compared with sha256 +timingSafeEqual, path$DSH_HOME/codex-mcp.tokenby default.Bounded NDJSON parsing — malformed lines are counted, not fatal; event count, line size and stderr tail are all capped.
Bilingual tool descriptions (
locale/en.json,locale/zh.json) so the calling model reads instructions in its own language.
Requirements
DSH | Desktop or CLI install whose |
Node | `^22.19.0 |
DSH credentials | a model API key already stored in your |
DSH profile | a |
Install
# from GitHub (recommended)
dsh plugin --profile desktop add github:GEB-lab/dsh-codex-mcp
# from a release tarball
dsh plugin --profile desktop add https://github.com/GEB-lab/dsh-codex-mcp/releases/latest/download/dsh-codex-mcp.tgz
# local checkout, for development (changes apply after a host restart)
dsh plugin --profile desktop add link:/absolute/path/to/dsh-codex-mcpdsh plugin is the DSH plugin command: it forwards to the profile's package manager, then selects the new bundle in dsh.profile.bundles. If HMR is on, the plugin loads immediately; otherwise restart DSH.
Check that it came up:
curl -i http://127.0.0.1:39391/mcp -X POST \
-H "authorization: Bearer $(cat ~/.dsh/codex-mcp.token)" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{}}}'or, without curl:
node scripts/smoke.mjs # protocol + dsh_env only, spends no tokens
node scripts/smoke.mjs --task "reply SMOKE_OK" --cwd /path/to/projectThe smoke script prints the exact config.toml block for your machine.
Codex configuration
~/.codex/config.toml:
[mcp_servers.dsh_codex]
url = "http://127.0.0.1:39391/mcp"
http_headers = { Authorization = "Bearer <contents of $DSH_HOME/codex-mcp.token>" }
startup_timeout_sec = 30
tool_timeout_sec = 3600
enabled = truecodex mcp list should then show dsh_codex … enabled Bearer token.
Then, from Codex:
Use
dsh_runto have DSH, inC:\work\proj, add typed input support tosrc/sum.mjsplus threenode:testcases; only touchsrc/andtest/. After it returns, runnode --test test\*.test.mjsyourself and report the result.
Tool reference
dsh_env()
No side effects, no tokens spent. Returns JSON: plugin{name,version,pid,startedAt}, mcp{url,token,tokenFile}, dsh{cli,version,profile,dshHome,permissionMode,node,nodeVersion}, defaults{cwd,timeoutSec}, runtime{activeRuns,maxConcurrentRuns}, and a ready-to-paste codexConfig snippet.
dsh_run(task, cwd?, timeout_sec?, include_events?)
Starts a new headless session. task is sent on stdin (no quoting problems, no argv length limit). On success the text result looks like:
[DSH done] turn 1 · steps 1 · 2.9s
session: session-1a2b3c4d-...
cwd: /work/proj
exit: 0 · turn_end: completed
usage: in 469 / out 8 / cacheRead 5760 / total 6237
--- final answer ---
DSH_PLUGIN_OK
--- continue this session ---
dsh_resume({ "session_id": "session-1a2b3c4d-...", "task": "<next task>" })dsh_resume(session_id, task, cwd?, timeout_sec?)
Continues an existing session. It must run in the same cwd as the original, otherwise DSH refuses before starting (and the plugin says so).
Failure payloads carry the classification (timed out, exit code N, turn_end was X, no final answer, empty final answer), the stderr tail, event counts, and a next-step hint for the usual causes (missing credentials, wrong profile, unknown session, cwd mismatch, unauthorized).
Configuration
All keys live under this entry's config in cordis.patch.yml (bundle default) or in ~/.dsh/profiles/<profile>/cordis.patch.yml (your override):
- id: dsh-codex-mcp
name: dsh-codex-mcp
config:
port: 39400
defaultCwd: 'D:\code'key | default | meaning |
|
| bind address; non-loopback is refused unless |
|
|
|
|
| endpoint path |
|
| Bearer token; created when missing |
|
| rotate the token on next start |
|
| child |
|
| child DSH profile |
|
| child |
| host cwd | used when a call omits |
|
| per-call timeout |
|
| parallel headless tasks |
| auto-detected | explicit path to |
| auto-detected | explicit Node executable |
|
|
|
|
| append the raw NDJSON event tail to results |
~/, relative paths and $DSH_HOME are expanded; unknown keys are ignored.
Security model
Binds
127.0.0.1by default. Requests whose peer is not loopback are rejected even if you widen the bind, unless you opt in withallowNonLoopback: true.Every request needs the Bearer token; the endpoint returns
401withWWW-Authenticate: Bearer realm="dsh-codex-mcp"otherwise. Token comparison is constant-time over SHA-256 digests.Request bodies are capped at 4 MiB; JSON-RPC batches are supported; unknown methods get
-32601.The child process runs with
DSH_PERMISSION_MODE=danger-full-accessbecause headless DSH is unattended — it really can edit files and run commands with your user's rights. Always pass acwdinside the project you mean to change, and keep the token out of repositories and screenshots.The token file is created with mode
0600; rotate withregenerateToken: true(then update Codex's config).
Troubleshooting
symptom | cause / fix |
| nothing listening, or token mismatch. Run |
| another process owns the port — change |
| the headless profile cannot see your key; enable the credentials/authorization providers in |
| the child ran a non-headless profile; keep |
| resume must use the original |
plugin edits do not take effect | the host already loaded the module — restart DSH (config changes behave the same way) |
agent cannot be found | set |
Development
npm test # node --test test/*.test.mjs — 19 cases, no network, uses a fake dsh CLI
node scripts/smoke.mjs --url http://127.0.0.1:39391/mcp --task "reply SMOKE_OK"
node index.js # standalone server, no DSH host neededThe test suite covers auth (401 / wrong token), session headers, batching, notifications, unknown methods and paths, SSE keep-alive, DELETE, tools/list, dsh_env contents, a successful run, malformed event lines, resume argument passing, non-zero exit with diagnostics, empty answers, turn_end other than completed, and timeout kill.
Limitations
One plugin instance per host; the endpoint is local-only by design.
Config or code changes need a host restart to take effect.
No streaming progress to the caller — a
dsh_runcall returns when the task finishes (bounded bytimeout_sec).DSH's sandbox profile decides what the child can do; this plugin does not add isolation of its own.
License
MIT
中文说明
一句话:在 DSH 宿主进程里起一个只监听回环地址的 Streamable HTTP MCP 服务,让 Codex 通过 MCP 把开发任务派给 DSH headless agent 执行,不再需要 PowerShell 包装器。
为什么做成插件:DSH 的普通 agent 工具只对 DSH 自己的模型可见,Codex 看不到。要让另一个 agent 调用,能力必须暴露到进程外——所以插件在宿主里起 HTTP MCP 端点,卸载时由 ctx.effect 注册的清理逻辑关掉。
安装:
dsh plugin --profile desktop add github:GEB-lab/dsh-codex-mcpCodex 侧(~/.codex/config.toml):
[mcp_servers.dsh_codex]
url = "http://127.0.0.1:39391/mcp"
http_headers = { Authorization = "Bearer <token 文件内容>" }
tool_timeout_sec = 3600
enabled = truetoken 默认在 ~/.dsh/codex-mcp.token(首次启动自动生成,权限 0600)。
三个工具:
dsh_env()— 查 dsh 版本、CLI 路径、profile、权限模式、token 位置,并给出可直接粘贴的 Codex 配置片段;dsh_run(task, cwd?, timeout_sec?)— 开新会话,任务走 stdin,回传最终答案 + session + cwd + 退出码 +turn_end+ token 用量;dsh_resume(session_id, task, cwd?, timeout_sec?)— 同一 cwd 续跑上一个会话。
失败判据(插件内建,调用方不必自己判断):超时 / 非零退出 / turn_end 不是 completed / 没有 final 事件 / 最终答案为空 —— 任一命中都返回 MCP 错误,并附 stderr 尾部与下一步提示。超时会 taskkill /T /F 杀掉整棵进程树。
安全:只绑 127.0.0.1,Bearer token(sha256 + 定时安全比较),请求体上限 4 MiB;子进程以 danger-full-access 运行(headless 无人值守必须如此),所以派任务时把 cwd 限定在项目目录内,别把 token 提交进仓库。
自测:npm test(19 个用例,用假 dsh CLI,不联网)、node scripts/smoke.mjs(像 Codex 一样连端点跑一轮)。
局限:单实例;改配置/改代码需重启宿主生效;不给调用方推送流式进度(调用在任务结束时返回)。
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage
Hosted MCP memory and agent control plane for durable conversations, jobs, and operations.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceServes as a single local MCP entry point that proxies configured upstream MCP tools to multiple coding agents, keeps sessions warm, serializes exclusive services, and provides SQLite-backed task, lock, and memory tools.MIT
- AlicenseNot gradedqualityAmaintenanceServes a local dsh workspace as a protected Streamable HTTP MCP endpoint, letting authenticated web AI agents read, search, patch, inspect, and operate the project, with optional shell commands, LSP support, and public tunneling.MIT
- AlicenseNot gradedqualityBmaintenanceServes a loopback-only MCP endpoint that lets a same-machine client start, steer, and cancel DSH Sessions it knows by id, list their durable subagent trees, deliver messages to continuable children, and read durable event logs with paging and chunked oversized-event recovery. It maps every call onto native Session Controller and subagent services without owning task state, starting a second listener, or registering model-facing tools.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.225 npm15MIT