Give a real terminal
interactive-cli is an MCP server that runs commands in a real pseudo-terminal and hands your agent the rendered screen, so it can answer prompts, pick menu items and drive REPLs, SSH and installers the way you would.
An illustrative session: the agent spawns eas build --platform ios, reads the screen, presses down, down, enter to pick the production profile, answers y to the Apple login prompt, waits for the build to complete, then closes the session with exit code 0.
The problem
Agents run commands. Then a command asks something.
A shell tool sends a command and waits for it to finish. An interactive command never finishes: it waits for a person. The agent hangs, times out or kills it, and you end up typing the answer yourself.
A terminal running npm init, stopped at the prompt "package name: (my-app)", waiting for input.
How it works
Four calls, the way you would do it
spawn
Start it
The command runs in a real pseudo-terminal, so it behaves exactly as it does in your terminal.
get_screen
Read it
xterm headless renders the output into the clean text a person sees: menus, prompts, progress bars. No ANSI escape soup.
send_input / send_keys
Answer it
Type a reply, or press keys: arrows, Enter, Tab, Ctrl+C, F1 to F12.
wait_for
Wait for it
Block until a pattern shows up, such as a prompt or "Build complete", so long jobs need no polling.
Tools
Nine tools
spawn(command, cwd?, env?, cols = 120, rows = 30, waitMs = 2000)
Start a command in a real PTY and get the first screen back.
send_input(sessionId, text, pressEnter = true, waitMs = 2000)
Type text and press Enter. Set pressEnter to false to type without submitting.
send_keys(sessionId, keys[], delayBetweenMs = 50, waitMs = 1500)
Press keys in sequence: arrows, Enter, Tab, Ctrl combos, F1 to F12.
get_screen(sessionId, search?, waitMs = 500)read-only
Capture the current screen. With search (a regex), get the matching lines with 2 lines of context.
wait_for(sessionId, pattern, timeoutMs = 30000, intervalMs = 1000)
Block until a regex (case-insensitive) matches the output, or time out.
wait_for_exit(sessionId, timeoutMs = 30000)
Wait for the process to end and get the final screen and exit code.
resize(sessionId, cols, rows)
Change the terminal size so TUIs can reflow their content.
list_sessions()read-only
List every session with its status, uptime and activity.
close(sessionId)
SIGTERM, then SIGKILL if needed. Returns the final screen.
Resources: interactive-cli://sessions and interactive-cli://sessions/{id}/screen. Prompts: eas_build, ssh_session, repl_session, docker_interactive (slash commands in Claude Code).
Install
One line to install
claude mcp add interactive-cli -- npx -y interactive-cli-mcpcodex mcp add interactive-cli -- npx -y interactive-cli-mcpThis writes [mcp_servers.interactive-cli] with command = "npx" and args = ["-y", "interactive-cli-mcp"] to ~/.codex/config.toml.
{
"mcpServers": {
"interactive-cli": {
"command": "npx",
"args": ["-y", "interactive-cli-mcp"]
}
}
}Save it as ~/.cursor/mcp.json.
npx -y interactive-cli-mcpPoint any stdio MCP client at this command.
Requires Node 18 or later, on macOS or Linux.
Good for
- eas build
- gcloud auth login
- npm init
- ssh
- docker login
- python, node and psql REPLs
- interactive installers
- TUI menus
FAQ
Questions
Does it work on Windows?
Not yet. Commands run through your shell ($SHELL -c, zsh by default), so it needs macOS or Linux.
Is it safe?
It runs the commands your agent asks for, in your shell, with your permissions. spawn, send_input, send_keys and close are marked destructive, so clients such as Claude Code ask before using them; get_screen and list_sessions only read.
What about very long output?
Each result is capped at 80,000 characters (Claude Code's limit is 100,000), cut in the middle so the start and the end survive. get_screen with a search returns the matching lines with context.
Can it type passwords?
send_input with pressEnter set to false types without submitting. The agent sees what the terminal shows, and terminals usually hide what you type at a password prompt.