ddx-term-mcp
Enables AI agents to execute commands in a shared tmux session that is simultaneously visible to a human via a web-based terminal UI.
README
dudoxx-ai-terminal
Shared multi-terminal bridge — one tmux session, many addressable terminals,
attached by a human (web / xterm.js) and an AI agent (MCP) simultaneously,
both looking at the exact same terminals. Architecture in one line: a single
pinned-size tmux session is the shared state; a NestJS broker owns the
canonical terminal registry and fans output to the web UI per-terminal over
WebSocket; an MCP server lets Claude shell out to the same tmux session (it
holds no PTY); a Next.js web app renders the terminals with xterm.js.
Packages
| Package | Role | Port / transport |
|---|---|---|
@ddx/term-contract (packages/ddx-term-contract) |
Shared zod types — WS frames, MCP tool I/O, terminal/session descriptors | library (ESM+CJS+types) |
ddx-term-broker |
Human channel + canonical state: tmux control-mode attach, registry, REST CRUD, per-terminalId WS | HTTP/WS 13330 (DDX_TERM_BROKER_PORT) |
@dudoxx/ddx-term-mcp |
Agent channel: MCP stdio server, thin tmux client (no PTY) | stdio (node dist/server.js) |
ddx-term-web |
Next.js 16 xterm.js UI, one tab per terminalId | HTTP 13340 (DDX_TERM_WEB_PORT) |
Ports default to the high 133XX band on purpose — clear of
3000/5173/8080so the bridge never collides with your own running codebases. Override via env or a.envfile (see Configuration).
ddx-cli-py/ddx-cli-tsare interactive e2e-target fixtures, not workspace packages — they are never built or published from here.
Bring-up
The normal path is agent-driven — you do not start a dev server by hand. Build once,
register the MCP (below), then call any term_* verb: the MCP server's supervisor spawns
the broker (13330) and web (13340) automatically and attaches to them.
pnpm install
pnpm build # builds all packages incl. ddx-term-mcp/dist/server.js
# or just the MCP server + its contract dep:
pnpm --filter @dudoxx/ddx-term-mcp... build
Then open the web UI at http://localhost:13340 to watch the same terminals the agent
drives. Lock files at ~/.ddx-term/{broker,web}.lock make the stack a machine-wide
singleton; set DDX_TERM_WEB=0 to run broker-only (headless).
Hacking on the broker/web themselves?
pnpm devstill runs both concurrently via turbo (broker 13330 + web 13340) for a live edit loop — but it is no longer required for normal agent use. SeeSETUP.md.
Register the MCP server (agent channel)
Add this to a Claude Code project's .mcp.json (or your Claude Desktop config).
A copy lives in .mcp.json.example — replace /abs/path
with this repo's absolute path:
{
"mcpServers": {
"ddx-term": {
"command": "node",
"args": ["/abs/path/dudoxx-ai-terminal/ddx-term-mcp/dist/server.js"],
"env": {
"DDX_TERM_SOCKET": "/tmp/ddx-term.sock",
"DDX_TERM_SESSION": "ddx-shared",
"DDX_TERM_DEFAULT": "t01",
"DDX_TERM_BROKER_PORT": "13330",
"DDX_TERM_WEB_PORT": "13340"
}
}
}
}
| Env | Default | Purpose |
|---|---|---|
DDX_TERM_SOCKET |
/tmp/ddx-term.sock |
tmux -S socket of the shared session |
DDX_TERM_SESSION |
ddx-shared |
session hosting all terminals (windows) |
DDX_TERM_DEFAULT |
t01 |
terminalId used when a verb omits terminalId |
DDX_TERM_BROKER_PORT |
13330 |
broker HTTP/WS port |
DDX_TERM_WEB_PORT |
13340 |
web UI HTTP port |
DDX_TERM_HOST |
127.0.0.1 |
bind/connect host (loopback by design) |
DDX_TERM_WEB |
(unset → web on) | set 0 to run broker-only (no web spawn) |
The env: block is the per-client override layer — it wins over .env files. The full
table (read caps, allow-list, supervisor-written URLs) is in DEPENDENCIES.md.
Global install (every Claude Code session)
The package is bin-shaped (ddx-term-mcp → dist/server.js) with a prepare
script that builds on install. To register it for all projects, add it to your
user-scope MCP config via the CLI (run from this repo so $PWD resolves):
pnpm -F @dudoxx/ddx-term-mcp build # produces an executable dist/server.js
claude mcp add ddx-term --scope user \
--env DDX_TERM_SOCKET=/tmp/ddx-term.sock \
--env DDX_TERM_SESSION=ddx-shared \
--env DDX_TERM_DEFAULT=t01 \
--env DDX_TERM_BROKER_PORT=13330 \
--env DDX_TERM_WEB_PORT=13340 \
-- node "$PWD/ddx-term-mcp/dist/server.js"
Verify with claude mcp list (look for ddx-term … ✔ Connected).
Configuration
Every setting has a built-in code default and three override layers — highest wins, and
override: false means an explicitly-set var always beats a file:
- The MCP client
env:block (.mcp.json/claude mcp add --env) — per agent / per client. - A project-local
.envin the current working directory. - A global
~/.ddx-term/.env— applies to every client on the machine.
Copy .env.example to .env (project) or ~/.ddx-term/.env (global) and
edit. The MCP server loads both files (src/load-dotenv.ts) before resolving ports; the
broker and web load them too for the standalone pnpm dev path. Example — move the whole
stack onto a different band:
# ~/.ddx-term/.env
DDX_TERM_BROKER_PORT=14330
DDX_TERM_WEB_PORT=14340
.env is gitignored; .env.example is the committed template.
Publishing is wired —
@dudoxx/ddx-term-mcpships as a self-contained npm package. Its publish bundle (build:bundle, tsup) inlines@ddx/term-contractfrom source into a singledist/server.js(noExternal: ['@ddx/term-contract']), keeping onlyzod+@modelcontextprotocol/sdkas external runtime deps. That bundle is wired asprepublishOnly, so the published tarball carries noworkspace:*reference — the contract stays"private": trueand is never published separately (by design; it lives in the changesetignore[]). Once a version is released to the registry (see Release & versioning below), thenpx -y @dudoxx/ddx-term-mcpform resolves. Until the first registry release, register by absolute path as above.
The MCP server is a thin tmux client — it never owns a PTY (no node-pty); it
shells out to tmux against the same session the broker and the human are attached
to. That shared-state property is the whole point: a command the agent types is
visible live in the web UI.
Attach natively from a real terminal emulator (human channel, no browser)
Because the bridge IS a standard tmux session, you can also attach with a native
tmux control-mode client — iTerm2 and WezTerm render tmux -CC natively:
tmux -CC attach -t ddx-shared
This opens the shared terminals as native tabs/windows in iTerm2 / WezTerm, alongside the web UI and the agent — all three see the same terminals.
Verification
pnpm typecheck # turbo run typecheck across all packages
pnpm lint
pnpm test # turbo run test (unit + e2e; e2e spins throwaway tmux on a temp socket)
Release & versioning (Changesets → npm)
Only @dudoxx/ddx-term-mcp publishes to public npm; broker, web, and the
contract are "private": true and listed in .changeset/config.json ignore[].
pnpm changeset # 1. describe the change + pick a bump (patch/minor/major)
git commit && push # 2. open a PR that includes the .changeset/*.md
On merge to main, .github/workflows/release.yml runs Changesets' action:
- It opens (or updates) a "Version Packages" PR that bumps
package.json, writesCHANGELOG.md, and consumes the changeset files. - Merging that PR triggers
pnpm release→build:bundle(tsup, inlines the contract) →changeset publish→npm publishof any package whose version changed.
The publish authenticates via the repo secret NPM_TOKEN (an npm automation
token with publish rights to the @dudoxx scope) and emits provenance
(NPM_CONFIG_PROVENANCE=true). After the first release lands, the
npx -y @dudoxx/ddx-term-mcp registration form resolves.
Verify a tarball before publishing:
cd ddx-term-mcp && pnpm build:bundle && npm pack --dry-run— it must listdist/server.js(self-contained, noworkspace:*),package.json,README.md,INSTALLATION.md,LICENSE, and the twoassets/*.png.
For the full release walkthrough, bump-type guidance, and CI details see
CLAUDE_PUBLISHING.md and
ddx-documentation/05-publishing/release-flow.md.
Dudoxx UG / Acceleate Consulting - Walid Boudabbous walid@acceleate.com
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。