gentle-terminal
Handles interactive Git workflows such as git rebase -i, presenting the todo list to the user as a screenshot and stopping for a human decision instead of auto-answering or resubmitting prompts.
Runs interactive full-screen TUIs such as htop in a real terminal emulator, letting an agent press keys and read the live rendered screen (colors, cursor movement, alternate screen) instead of relying on one-shot command output.
Opens a persistent Python REPL session, typing code, pressing Enter and reading the resulting output, with variables and environment kept alive across successive commands rather than in isolated one-off invocations.
Drives a full-screen interactive Vim session inside a real PTY, typing text and pressing keys while reading the screen back as text or a screenshot, so an agent can edit buffers and inspect editor state without leaving a half-finished edit.
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., "@gentle-terminalOpen a Python REPL, type 2+2, press Enter, and show me the output"
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.
gentle-terminal
Approval-first terminal control for AI coding agents.
Let Claude Code, Codex, Cursor, OpenCode and any MCP client drive real interactive shells, CLIs and TUIs (and even supervise other coding agents) without auto-approving prompts, mistaking timeouts for success, or killing work in progress.
Quick start · Connect your client · Examples · How it works · Safety model · FAQ
Why gentle-terminal?
Most agent shell tools run one command and hand back its output. That breaks the moment a program is interactive: vim, htop, a Python REPL, git rebase -i, an installer asking questions, an SSH login, or another AI agent like Codex CLI waiting for approval.
terminal-use solves the mechanics: a real PTY rendered by a real terminal emulator, exposed over MCP. gentle-terminal adds the judgment an agent needs to use that power responsibly:
Without a policy, agents tend to... | gentle-terminal teaches them to... |
Treat a wait timeout as "done" or "failed" | Re-read the screen and classify the state: working, needs input, idle or exited |
Press Enter again or resubmit when nothing seems to happen | Type once; re-waiting is never resubmitting |
Pick "Always allow" on trust and approval prompts | Surface the exact prompt to you; one-time approval only when your policy covers it |
Trust another agent's "I'm done" | Inspect the artifact and run the check independently |
Kill a slow process to make progress | Keep supervising in bounded waits and report long stalls to you |
Leave orphaned shells behind | Close owned idle sessions and report anything uncertain |
Related MCP server: Menager MCP Server
Features
🖥️ Real terminals, not command runners. Full PTY with colors, cursor movement and alternate screen, readable as text or PNG screenshots (powered by terminal-use).
🛡️ Approval-first Agent Skill. A compact
SKILL.md(about 6 KB) with hard rules, an eleven-state decision table and an efficient call loop. Specialized guidance (authorization, recovery, Windows, engine limits, agent supervision) is loaded only when needed.🤖 Supervise other coding agents. A state-based workflow with versioned, observed signals for Codex CLI, OpenCode, Antigravity CLI and Claude Code startup.
🧪 Behavior pinned by tests. A deterministic simulated agent exercises delayed trust overlays, approval menus, silent hangs, crashes, false completion claims, prompt injection and respawns through the real MCP server, without any model calls.
📌 Pinned and verifiable. Runtime locked to
terminal-use@0.2.1with SHA-512 integrity, installed with--ignore-scripts; a test fails if the pin drifts.🧳 Portable launcher. One
node launch.mjsentry point that works from any cwd and from paths with spaces, forwarding stdio, arguments and exit codes in the same process.🔌 One command for every client.
node install.mjsregisters or updates gentle-terminal in each installed client: Cursor, VS Code, Codex, Claude Code, Claude Desktop, OpenCode, Antigravity, Gemini CLI and Windsurf. Ready-to-copy configs are included for manual setup.✅ Tested on Linux, macOS and Windows, x64 and ARM64. 44
node:testtests, including E2E supervision against a simulated agent and the installer against a temporary home folder, plus a deterministic MCP smoke test in CI; no model or network calls.🪶 No hidden global changes. No global install, no PATH or profile edits. Client configs and skill links change only when you run the installer, with a backup of every file it edits.
Quick start
Requirements: Node.js 22.12 or newer and npm, on Windows, Linux or macOS (x64 or arm64).
git clone https://github.com/reyrodman/gentle-terminal.git
cd gentle-terminal
node install.mjs --dry-run # shows which clients it found and what it would change
node install.mjs # installs the runtime and registers every client it foundThe installer is the same command for a first install and for every later update, and it is safe to run again:
Finds your clients by their config files and only touches clients that are installed. One file can serve several apps: Cursor IDE and
cursor-agentshare~/.cursor/mcp.json, the Codex CLI, app and IDE extension share~/.codex/config.toml, and the Antigravity IDE andagyshare~/.gemini/config/mcp_config.json. VS Code, Claude Code, Claude Desktop, OpenCode, Gemini CLI and Windsurf have their own files.Adds or updates per client. A missing entry is added as
gentle-terminal. An entry that already runs a gentle-terminallaunch.mjsfrom another folder (an older copy, a moved clone) is repointed in place, keeping its name and extra arguments such as--cwd. Other servers and settings are kept as they are.Never guesses. A file with comments, invalid JSON or a TOML layout it does not recognize is left untouched and reported as a manual step (exit code 2).
Backs up and checks. Every changed file gets a
.bak-gentle-terminal-<time>copy, is written atomically and read back. The pinned runtime is installed only when missing or out of date (the previous one is restored ifnpm cifails), and the server must answer an MCP handshake before any client is touched.Links the skill (a junction on Windows, a symlink elsewhere) into each client's user skills folder:
~/.agents/skills(Codex, VS Code, Cursor),~/.claude/skills,~/.config/opencode/skills,~/.gemini/config/skillsand~/.gemini/skills. An existing folder with that name is never replaced.
Restart or reload the clients it lists at the end. Other options: --only cursor,codex, --skip vscode, --node <path>, --uninstall, --help.
Updating
npm run update # = node install.mjs --updateIt fast-forwards this git checkout (it refuses local changes, a diverged branch or a folder that is not its own git checkout), then runs the installer from the new code, which updates the runtime and every client. It follows the branch you are on: releases land on main, and on any other branch it prints a note with the git switch command. Nothing updates in the background: you decide when a new version runs.
To check the bundle itself, npm run verify prints 0.2.1, runs the test suite and the MCP smoke test. Expected tail:
PASS: persistent variable, typed text, Enter, read, exit 7, destroy, disconnect; server exit=0 stderr=""; owned shell gone.Connect your MCP client manually
node install.mjs does this for you. To do it by hand: gentle-terminal is a standard stdio MCP server, command node, argument the absolute path to launch.mjs. Run npm run setup once to install the pinned runtime.
claude mcp add gentle-terminal --scope user -- node /absolute/path/to/gentle-terminal/launch.mjscodex mcp add gentle-terminal -- node /absolute/path/to/gentle-terminal/launch.mjsOr merge examples/codex/config.toml into ~/.codex/config.toml:
[mcp_servers.gentle-terminal]
command = "node"
args = ["/absolute/path/to/gentle-terminal/launch.mjs", "--cwd", "/approved/workspace"]{
"mcpServers": {
"gentle-terminal": {
"command": "node",
"args": ["/absolute/path/to/gentle-terminal/launch.mjs", "--cwd", "/approved/workspace"]
}
}
}Files: examples/cursor/mcp.json, examples/claude-desktop/claude_desktop_config.json.
See examples/vscode/mcp.json: place it in .vscode/mcp.json for one workspace, or merge its servers into your user mcp.json (Code/User/mcp.json) for all of them.
See examples/opencode/opencode.json. OpenCode expects a command array, not a shell string.
On Windows, use an absolute Windows path (C:/tools/gentle-terminal/launch.mjs or escaped backslashes in JSON). More details and caveats: references/clients.md.
Adding the MCP server gives the agent thetools. Loading SKILL.md (through your client's skill discovery, or by asking the agent to read it) gives it the policy. Use both.
Usage examples
Ask your agent in plain language. A few prompts that show what the skill changes:
Open a terminal in this project, set APP_ENV=test, run the test suite and tell me the real exit code.
Keep the session open so I can run more commands with the same environment.Start Codex CLI in /approved/workspace and ask it to add a unit test for parseConfig.
If it asks to trust the folder or approve a command, stop and show me the exact prompt.
When it says it's done, verify the test file exists and run it yourself before telling me.Run `git rebase -i HEAD~3`, show me the todo list as a screenshot, and stop there.
I'll decide what to squash.What a well-behaved supervised session looks like (tool calls from the agent's side):
terminal_create { command: "codex", cwd: "/approved/workspace" } -> Created session 1
terminal_wait { sessionId: 1, until: "quiet", quietMs: 3000, timeoutMs: 30000 }
⤷ input box visible, but STARTING: wait quiet again -> a trust/hooks menu appeared later
⤷ AWAITING_HUMAN: the exact menu is shown to the user, who answers "once" -> agent waits
terminal_type { sessionId: 1, text: "add a unit test for parseConfig" } (no \n: input boxes treat it as paste)
terminal_press { sessionId: 1, key: "Enter" } -> task echoed in the transcript (submitted once)
terminal_wait { sessionId: 1, until: "quiet", quietMs: 3000, timeoutMs: 120000 }
⤷ timed out -> "esc to interrupt" still visible -> state: RUNNING -> wait again
terminal_wait { ... } -> "Worked for 2m 14s", prompt back -> state: IDLE
⤷ agent reads the new test file and runs it in a separate command session: exit 0
terminal_type { sessionId: 1, text: "/quit" } + terminal_press { key: "Enter" }
terminal_wait { sessionId: 1 } -> command exited (exit code 0)
terminal_destroy { sessionId: 1 }More prompts, including REPLs, full-screen editors and long builds: examples/prompts.md.
How it works
flowchart LR
U([You]) -->|task + approval policy| A[AI agent<br/>Claude Code · Codex · Cursor · OpenCode]
A -->|reads| S[[SKILL.md<br/>hard rules · 11 states · on-demand references]]
A -->|MCP stdio| L[launch.mjs<br/>Node/platform guards]
L -->|same process| T[terminal-use 0.2.1<br/>pinned · SHA-512 verified]
T --> P[Real PTY + xterm emulator]
P --> X[shells · REPLs · TUIs · other agents]
A -.->|trust / login / approval prompts| Ulaunch.mjschecks Node (>= 22.12) and platform, then imports the officialterminal-usebinary in the same process, so arguments, stdio, signals and exit codes behave exactly as upstream. No child wrapper, no extra engine. Its only environment change: an inherited empty ordumbTERMbecomesxterm-256color, matching the emulator, so TUIs started from IDE or agent shells still run on Windows (Linux and macOS sessions always getxterm-256color).runtime/holds a lockfile that pinsterminal-use@0.2.1by registry integrity. Each machine installs its own platform-specific prebuilt binaries;node_modulesis never shipped.SKILL.mdencodes the operating policy: classify the screen into one of eleven states after every response, stop at human decisions, prove completion with fresh markers, verify artifacts before follow-ups, exit naturally and clean up owned sessions. The files inreferences/are read on demand only.
The server also publishes the upstream terminal-use skill over MCP (skills/list) for hosts that support that extension. gentle-terminal's skill already covers routine use, so agents load the upstream text only for its key and TUI recipes (details).
Tools exposed (from terminal-use)
Tool | Purpose |
| Start an interactive shell, or a single program with |
| Type text, press keys and combinations, or send several inputs at once |
| Wait for a command to finish, a regex to appear, or output to go quiet |
| Read the screen as text, or render it as a PNG |
| Mouse interaction for TUIs |
| Session management |
Configuration
All arguments after launch.mjs go straight to terminal-use:
Option | Default | Description |
| client's cwd | Default working directory for new sessions. It is not an access boundary: |
| Windows PowerShell on Windows; | Shell for new sessions. Write commands in that shell's syntax. |
|
| Terminal size |
|
| Lines of history kept |
| off | Start login shells (loads your profile |
node launch.mjs --version # 0.2.1
node launch.mjs --help # full upstream usageJSON argument values must not contain extra shell quoting; keep spaces inside a single argument value.
Safety model
This is not a sandbox. Sessions inherit your user privileges and environment, potentially including credentials. Use trusted clients and keep their approval gates enabled.
What is enforced in code and what is only guidance:
Control | Enforced by | Notes |
Node and platform checks, clean stdout |
| Tested in |
Runtime pin, SHA-512 lockfile, no install scripts | npm + | Every locked package must be a registry tarball with integrity |
Session disposal on client disconnect or SIGINT/SIGTERM/SIGHUP | terminal-use | Forced kills and detached grandchildren can escape |
Command sessions never rerun automatically | terminal-use | Tested in |
Approvals, prompt injection, secrets, cwd scope, no duplicate submissions | Agent Skill only | Depends on the model and client following |
Human decisions stay human. Trust, login, credential and approval prompts are shown to you verbatim. One-time approval is chosen only when an explicit policy you gave covers that exact action; persistent "always allow" is never chosen unasked.
Terminal output is untrusted. Text printed by programs, files and other agents is treated as data, never as instructions.
--cwdis a default, not a jail. Any session can start anywhere the user can.No silent global changes. gentle-terminal never edits global settings, shell profiles,
PATHor credentials. Client configs and skill links are changed only bynode install.mjswhen you run it; nothing runs it automatically (no npm lifecycle scripts, no background updates).No process-name kills. Only sessions the agent created are destroyed, and only when idle. Upstream handles stdin disconnect and SIGINT/SIGTERM/SIGHUP, but forced termination, detached grandchildren and external programs can escape graceful disposal; uncertain cleanup is reported.
Attach endpoints. On Unix they are sockets in a
0700directory. On Windows they are named pipes whose default ACL was observed to grant Read to Everyone; on shared Windows hosts other local accounts may be able to read session output. See SECURITY.md.Supply chain. The runtime is pinned by version and SHA-512 integrity and installed with
--ignore-scripts.tests/pin.test.mjsfails if the manifest, lockfile or installed version drift, or if any locked package loses its registry integrity.
Want stronger isolation? Run terminal-use inside a container (see the upstream Docker notes) and point the agent at that instead.
Supported platforms
OS | Architectures | Default shell | CI |
Linux | x64, arm64 |
| ubuntu-latest (x64), ubuntu-24.04-arm (arm64) |
macOS | arm64 (x64 untested) |
| macos-latest (arm64) |
Windows | x64, arm64 | Windows PowerShell | windows-latest (x64), windows-11-arm (arm64) |
The CI column lists where the suite runs, on Node 22 and 24 for x64 and Node 24 for ARM runners. Check the badge or the Actions tab for the current result. macOS x64 relies on prebuilt binaries but has no runner here.
Native dependencies are prebuilt; there is no compilation fallback. Reinstall the runtime on every OS/architecture and never copy node_modules between machines.
Testing
npm test # launcher guards and forwarding, pin and lockfile integrity, skill validation,
# export allowlist + privacy scan, E2E supervision against a simulated agent
npm run smoke # real MCP stdio round-trip: persistent state, keys, waits, exit codes, disconnect cleanup
npm run bench # cost benchmark: startup, payload sizes, per-tool latency, memory, calls per taskNo test makes model or network calls, and scratch state lives in the OS temp directory. The smoke test starts the server, drives a shell, checks exit 7 propagation, then disconnects and confirms its own shell is gone. evals/activation.json lists behavior cases (activation, authorization, silence, verification) for manual runs with real agents; they need a model, so they are not part of CI.
Measured cost
Windows 11 x64, Node 24.16, terminal-use 0.2.1, 15 runs (npm run bench); sizes are bytes on the wire, tokens are a 4-bytes-per-token estimate.
Metric | v1.0.0 | v1.1.0 | Note |
Skill description (discovery, always in context) | 252 B | 349 B | +97 B: now also says when not to activate |
Skill text required before the first action | 34,301 B (~8.6k tokens) | 6,272 B (~1.6k tokens) | v1.0.0 required README, workflow and upstream skill up front. Current |
Run a command and read its exit status | 4 calls, 3,324 B, p50 534 ms | 1 call, 1,002 B, p50 314 ms | type + press + wait + read vs. one type with |
| 24,828 B | 24,828 B | Upstream schema, unchanged |
Cold start to | p50 398 ms | p50 398 ms | Same launcher and runtime |
Server memory idle / with 6 shells | 109 MB / 378 MB | 109 MB / 378 MB | Working set of the server process |
Server CPU while idle | 0 ms per 5 s | 0 ms per 5 s |
The call-economy row measures the guidance, not a server change: the after-column is what an agent following SKILL.md sends. Under the full benchmark load, the upstream MCP SDK prints a MaxListenersExceededWarning on stderr (stdout stays valid JSON-RPC); it did not reproduce with 60 screenshots or 60 reads alone.
Build a distributable archive
npm pack --dry-run --json --ignore-scripts # inspect the exact allowlisted file list
npm pack --ignore-scripts # creates gentle-terminal-1.2.0.tgzThe package is private, so it can be packed but not published to npm by accident. The files allowlist excludes installed modules, caches and scratch data, and the export test scans every packed file for home-directory paths.
Project structure
gentle-terminal/
├── SKILL.md # Agent Skill: hard rules, state table, efficient loop, on-demand index
├── install.mjs # Installs/updates the runtime, MCP entries and skill links for every client found
├── launch.mjs # Same-process launcher for the pinned MCP server
├── smoke.mjs # Deterministic MCP stdio smoke test
├── references/ # Loaded on demand
│ ├── workflow.md # Supervising coding agents, versioned client signals
│ ├── authorization.md # Trust, approval, login and secret prompts
│ ├── recovery.md # Timeouts, respawns, crashes, disconnects
│ ├── windows.md # PowerShell, cmd and ConPTY behavior
│ ├── engine.md # terminal-use limits, wait semantics, measured cost
│ └── clients.md # Activation notes per MCP client
├── bench/ # Reproducible cost benchmark
├── evals/ # Behavior cases for real-agent evaluation
├── examples/ # Copy-paste client configs and example prompts
├── runtime/ # Pinned terminal-use manifest + lockfile (install per machine)
├── tests/ # node:test suite, simulated agent fixture, MCP test client
├── docs/assets/ # README artwork
└── .github/ # CI, issue and PR templatesFAQ
terminal-use provides the terminal tools and a usage skill. gentle-terminal pins and verifies that runtime, gives you a portable launcher, and layers a stricter approval-first supervision policy on top, aimed at the hardest case: an agent driving another agent or a long interactive task where timeouts, prompts and false "done" claims are common.
Only one-time approvals, and only when you have given an explicit policy that covers that exact action. Otherwise it stops and shows you the prompt. You can also attach your own terminal to the session (terminal-use attach) and answer directly.
Yes, any installed shell via --shell. The skill requires the agent to write commands in the syntax of the selected shell, not merely the operating system.
It is the minimum supported by terminal-use and its prebuilt native dependencies. The launcher fails fast with a clear message on older versions.
Your MCP client was probably launched from a GUI and inherited a bare environment. Pass --login to start login shells that read your profile.
Contributing
Contributions are welcome: new client signals for the workflow, client config examples, docs and tests. Please read CONTRIBUTING.md and our Code of Conduct first.
Security
Please report vulnerabilities privately as described in SECURITY.md. Do not open public issues for security problems.
Acknowledgements
terminal-use by Computer Agent Labs: the PTY + terminal emulator MCP server this project builds on.
Model Context Protocol and the Agent Skills format.
Third-party licenses: THIRD_PARTY_NOTICES.md.
License
MIT © 2026 reyrodman and gentle-terminal contributors.
If gentle-terminal saves your agent from one bad "Always allow", consider giving it a ⭐
This server cannot be deployed
Maintenance
Related MCP Connectors
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
- ParleyOAuthdev.weldra
Coordination hub for AI coding agents: message teammates, ask humans, audit every event.
Shared control plane for AI coding agents — tasks, memory, decisions, file locks. 12 tools.
Context and control for any AI. Agent identity, scoped access, and proof across tools.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables AI agents to start and manage pseudo-terminal sessions, run shell commands and interact with REPLs programmatically.7-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents or human operators to spawn, monitor, drive, and hook into child terminal sessions—such as CLI tools, interactive shells, and polyglot runtimes—via POSIX pseudo-terminals, providing multiplexed control, event interception, and memory-bounded observability for orchestration.MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to drive real interactive terminal sessions—PTYs, TUIs, REPLs, SSH, even another agent's CLI—with a live web view where humans can watch and type in.3 npm2MIT
- AlicenseAqualityAmaintenanceEnables AI assistants to spawn and control persistent local shells, SSH sessions, REPLs, debuggers, and serial/UART devices with zero-loss output capture, timestamped history, and atomic expect matching for command execution and prompt synchronization.1243 PyPIMIT