{"id":"629fb49e-4cdf-4432-a7d4-0561a0a3004b","slug":"pty-pilot","kind":"mcp","name":"pty-pilot","summary":"MCP server letting an agent drive interactive PTY/pipe terminal sessions","owner":"Shine12222","repoUrl":"https://github.com/Shine12222/pty-pilot","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-05-01T12:29:15.000Z","addedAt":"2026-10-21T08:50:00.902Z","logoUrl":"https://avatars.githubusercontent.com/u/119743354?u=3fe2196e5c96ceae8f4e8e86d777c12d32bc7ea7&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools","cloud-infra"],"tags":["agent","claude-code","pty","repl","terminal"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-23T18:37:07.225Z","sample":false,"url":"https://conduits.dev/tools/pty-pilot","language":"Python","transports":["stdio"],"install":{"pypi":"pty-pilot"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# pty-pilot\n\n> 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`.\n\nThe 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.\n\nBuilt and tested with [Claude Code](https://www.anthropic.com/claude-code); works with any MCP client.\n\n## Tools\n\n| Tool | Purpose |\n|---|---|\n| `term_open(cmd, …)` | Spawn a process. Returns final output if it exits within `yield_ms`, otherwise a `session_id`. |\n| `term_send_keys(session_id, chars, …)` | Write to stdin, return any output that arrives before `yield_ms`. Empty `chars` polls without writing. |\n| `term_close(session_id)` | SIGTERM, then SIGKILL after 500ms. Kills the whole process group. |\n| `term_list()` | List live sessions with PID, cmd, age, idle. |\n\nFull parameters documented in the tool descriptions registered with the server (see `src/pty_pilot/server.py`).\n\n## Design notes\n\n- **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.\n- **PTY backend**: [`ptyprocess`](https://github.com/pexpect/ptyprocess) (Unix). `tty=false` uses a plain `subprocess.Popen` with pipes — cheaper, but no terminal semantics. Default is `tty=false`.\n- **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.\n- **ANSI strip default-on.** `raw=true` keeps escapes (rarely needed).\n- **Bounded memory**: per-session head/tail buffer capped at 256 KiB. Per-call response further truncated to `max_output_bytes` (default 32 KiB).\n- **Session limit**: 32 concurrent. LRU eviction; the 4 most-recently-used are protected from eviction.\n- **Process-group kill**: `SIGTERM` (then `SIGKILL`) to the entire pgid, so REPL → shell → child trees get reaped.\n\n## Install\n\nRequires Python ≥ 3.10 and an MCP client.\n\n```sh\ngit clone https://github.com/Shine12222/pty-pilot\ncd pty-pilot\npython3 -m venv .venv\n.venv/bin/pip install -e .\n```\n\nRegister with Claude Code (user scope, available everywhere):\n\n```sh\nclaude mcp add --scope user pty-pilot \"$PWD/.venv/bin/python\" -- -m pty_pilot\n```\n\nVerify:\n\n```sh\nclaude mcp list\n# pty-pilot: …/.venv/bin/python -m pty_pilot - ✓ Connected\n```\n\nOpen a new Claude Code session and the `term_*` tools will be visible under `/mcp`.\n\n## When to use vs. plain Bash\n\nUse **Bash** for one-shot commands. It's cheaper and gives the model less rope. Use **pty-pilot** only when:\n\n- The program needs a real TTY (password prompts, sudo, full-screen TUIs).\n- You're driving a REPL (`python -i`, `node`, `psql`, `sqlite3`, …).\n- You need to inspect a long-running process mid-flight.\n\n**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.\n\n## Tests\n\n```sh\n.venv/bin/python tests/test_smoke.py\n```\n\nEight 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.\n\n## Limitations\n\n- **Unix only** for now. Windows ConPTY would be a second backend in `process.py`.\n- **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.\n- **No sandboxing.** Trusts the host environment. Codex has Seatbelt/bwrap/landlock; pty-pilot does not.\n- **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.\n\n## Credits\n\nArchitecture 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`](https://github.com/openai/codex/tree/main/codex-rs/core/src/unified_exec). Thanks to that team for doing the hard design work first.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n"}