Skip to content
Conduit

mcp server

pty-pilot

MCP server letting an agent drive interactive PTY/pipe terminal sessions

Shine12222/pty-pilotcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "pty-pilot": {
      "command": "uvx",
      "args": [
        "pty-pilot"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 23, 2026

pty-pilot

An MCP server that lets an agent pilot a terminal — REPLs, password prompts, full-screen TUIs, anything that needs a real PTY. Architecturally modeled after OpenAI Codex CLI's UnifiedExec.

The built-in Bash tool in most agent harnesses is one-shot: it runs a command and returns the output. It can't drive a REPL, can't answer a prompt, and chokes on TTY-only commands like claude doctor, ssh user@host …interactive…, password prompts, or curses-style TUIs. pty-pilot adds four MCP tools that solve that.

Built and tested with Claude Code; works with any MCP client.

Tools

ToolPurpose
term_open(cmd, …)Spawn a process. Returns final output if it exits within yield_ms, otherwise a session_id.
term_send_keys(session_id, chars, …)Write to stdin, return any output that arrives before yield_ms. Empty chars polls without writing.
term_close(session_id)SIGTERM, then SIGKILL after 500ms. Kills the whole process group.
term_list()List live sessions with PID, cmd, age, idle.

Full parameters documented in the tool descriptions registered with the server (see src/pty_pilot/server.py).

Design notes

  • No expect/prompt detection. Every call deadline-drains the output buffer; the model picks yield_ms and re-polls. The minimum-yield clamps (250 ms active, 5000 ms empty-poll) keep models from busy-looping. Borrowed from Codex.
  • PTY backend: ptyprocess (Unix). tty=false uses a plain subprocess.Popen with pipes — cheaper, but no terminal semantics. Default is tty=false.
  • Env scrubbing on spawn: TERM=dumb, NO_COLOR=1, PAGER=cat, GIT_PAGER=cat, LANG=C.UTF-8, etc. Suppresses most color/pager noise at the source.
  • ANSI strip default-on. raw=true keeps escapes (rarely needed).
  • Bounded memory: per-session head/tail buffer capped at 256 KiB. Per-call response further truncated to max_output_bytes (default 32 KiB).
  • Session limit: 32 concurrent. LRU eviction; the 4 most-recently-used are protected from eviction.
  • Process-group kill: SIGTERM (then SIGKILL) to the entire pgid, so REPL → shell → child trees get reaped.

Install

Requires Python ≥ 3.10 and an MCP client.

git clone https://github.com/Shine12222/pty-pilot
cd pty-pilot
python3 -m venv .venv
.venv/bin/pip install -e .

Register with Claude Code (user scope, available everywhere):

claude mcp add --scope user pty-pilot "$PWD/.venv/bin/python" -- -m pty_pilot

Verify:

claude mcp list
# pty-pilot: …/.venv/bin/python -m pty_pilot - ✓ Connected

Open a new Claude Code session and the term_* tools will be visible under /mcp.

When to use vs. plain Bash

Use Bash for one-shot commands. It's cheaper and gives the model less rope. Use pty-pilot only when:

  • The program needs a real TTY (password prompts, sudo, full-screen TUIs).
  • You're driving a REPL (python -i, node, psql, sqlite3, …).
  • You need to inspect a long-running process mid-flight.

Avoid for git interactive flows — use git -c core.editor=true, --no-edit, GIT_EDITOR=true, or commit -m. The Codex team explicitly warns against this and the same caveat applies here.

Tests

.venv/bin/python tests/test_smoke.py

Eight scenarios: one-shot exec, mid-flight pipe polling, Python REPL via PTY, multi-line REPL input, ANSI strip, ANSI raw mode, head/tail truncation under heavy output, terminate of long-running session.

Limitations

  • Unix only for now. Windows ConPTY would be a second backend in process.py.
  • No 2D screen model. Output is a flat byte stream; cursor-positioning TUIs (vim, htop, fullscreen ncurses) survive but look messy. A tmux capture-pane-style alternative is plausible for v2.
  • No sandboxing. Trusts the host environment. Codex has Seatbelt/bwrap/landlock; pty-pilot does not.
  • No expect-pattern primitive. If you need "send X, wait for prompt Y to come back," you loop in the model. A term_send_and_wait(pattern, timeout) could be added if it turns out to be the bottleneck.

Credits

Architecture and parameter shapes (esp. yield_ms clamps, head/tail elision, env scrub list, session protection policy, "don't drive git interactive" warning) are lifted from the OpenAI Codex CLI's UnifiedExec. Thanks to that team for doing the hard design work first.

License

MIT — see LICENSE.

↑↓ navigate openesc close