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.

View on GitHub

v1.1.0 · MCP server · Node 18+ · macOS & Linux · MIT

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

  1. spawn

    Start it

    The command runs in a real pseudo-terminal, so it behaves exactly as it does in your terminal.

  2. get_screen

    Read it

    xterm headless renders the output into the clean text a person sees: menus, prompts, progress bars. No ANSI escape soup.

  3. send_input / send_keys

    Answer it

    Type a reply, or press keys: arrows, Enter, Tab, Ctrl+C, F1 to F12.

  4. 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-mcp

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.