MCP server that lets AI coding agents interact with interactive CLI processes. Uses a real PTY + xterm headless to render terminal output as clean text — no ANSI escape soup.
Works with Claude Code, Cursor, Windsurf, or any MCP-compatible client.
AI agents can run shell commands, but they choke on anything interactive:
$ eas build --platform ios
✔ Select a build profile › (waiting for input...)
This MCP server bridges that gap — spawn the process, read the screen, send keystrokes.
claude mcp add-json interactive-cli '{"command":"npx","args":["-y","interactive-cli-mcp"]}'Or add to ~/.claude/settings.json manually:
{
"mcpServers": {
"interactive-cli": {
"command": "npx",
"args": ["-y", "interactive-cli-mcp"]
}
}
}git clone https://github.com/ohernandezdev/interactive-cli.git
cd interactive-cli
npm install && npm run buildclaude mcp add-json interactive-cli '{"command":"node","args":["/absolute/path/to/interactive-cli/dist/index.js"]}'Point your client's MCP config at:
command: node
args: ["/path/to/interactive-cli/dist/index.js"]
transport: stdio
| Tool | Description | Read-only |
|---|---|---|
spawn | Start an interactive process in a PTY | No |
send_input | Type text (appends Enter by default) | No |
send_keys | Send special keys in sequence (arrows, ctrl combos, etc.) | No |
get_screen | Capture current terminal screen, optional regex search | Yes |
wait_for | Block until a regex pattern appears in output | No |
wait_for_exit | Block until the process exits | No |
resize | Change terminal dimensions | No |
list_sessions | List all active sessions | Yes |
close | Kill process, clean up session | No |
sequenceDiagram
participant Agent as AI Agent
participant MCP as interactive-cli
participant PTY as Process (PTY)
Agent->>MCP: spawn("eas build --platform ios")
MCP->>PTY: Start process in PTY
PTY-->>MCP: Initial output
MCP-->>Agent: sessionId + screen
Agent->>MCP: get_screen(sessionId)
MCP-->>Agent: "Select profile: ❯ dev staging prod"
Agent->>MCP: send_keys(["down","down","enter"])
MCP->>PTY: ↓ ↓ ⏎
PTY-->>MCP: "✔ Selected: production"
MCP-->>Agent: updated screen
Agent->>MCP: wait_for("Build complete|error", 600s)
loop Every 1s
MCP->>PTY: check output
end
PTY-->>MCP: "Build complete"
MCP-->>Agent: matched + screen
Agent->>MCP: close(sessionId)
MCP->>PTY: SIGTERM
MCP-->>Agent: final screen + exit code
spawn({command: "ssh user@server.com",// full command stringcwd: "/path/to/project",// optional working directoryenv: {"NODE_ENV": "production"},// optional extra env varscols: 120,// terminal width (default 120)rows: 30,// terminal height (default 30)waitMs: 3000,// ms to wait for initial output (default 2000)})Commands run through your shell ($SHELL or /bin/zsh), so pipes, redirects, and builtins work.
send_input({sessionId: "s1",text: "yes",// text to typepressEnter: true,// append Enter (default true). false for password fieldswaitMs: 2000,// ms to wait for response (default 2000)})Send one or more keys in sequence with a small delay between each:
send_keys({sessionId: "s1",keys: ["down","down","enter"],// navigate menudelayBetweenMs: 50,// ms between keys (default 50)waitMs: 1500,// ms to wait after last key (default 1500)})Supported keys:entertabescapespaceupdownleftrightbackspacedeletehomeendpage_uppage_downf1–f12ctrl+cctrl+dctrl+zctrl+lctrl+actrl+ectrl+rctrl+wctrl+uctrl+kctrl+pctrl+nyn0–9
get_screen({sessionId: "s1",search: "error|warning",// optional regex to highlight matching lines})// Returns: { screen, cursor: { row, col }, searchResults, stats }wait_for({sessionId: "s1",pattern: "\\$|#|>",// regex to match (case-insensitive)timeoutMs: 30000,// max wait (default 30s)intervalMs: 1000,// check interval (default 1s)})// Returns: { matched: true/false, screen, elapsed }wait_for_exit({sessionId: "s1",timeoutMs: 60000,})// Returns: { alive: false, exitCode: 0, screen }Sessions are exposed as MCP resources:
interactive-cli://sessions— JSON list of all sessionsinteractive-cli://sessions/{id}/screen— live screen content
Pre-built prompt templates for common flows (appear as slash commands in Claude Code):
eas_build— EAS build with credential handlingssh_session— SSH connection with command executionrepl_session— Start a REPL (Python, Node, psql, etc.)docker_interactive— Run an interactive Docker container
graph LR
A[AI Agent] <-->|MCP Protocol| B[interactive-cli server]
B --> C[Session Manager]
C --> D[node-pty<br/><i>real PTY</i>]
C --> E[xterm headless<br/><i>screen render</i>]
C --> F[Truncation<br/><i>80K limit</i>]
D <--> G[Child Process<br/>ssh, eas, python...]
- node-pty spawns a real pseudo-terminal, so the child process thinks it's talking to a human
- xterm headless maintains a virtual terminal buffer that renders ANSI sequences into a clean 2D text grid
- Output is truncated at 80K chars (MCP clients like Claude Code cap at 100K) with smart middle-truncation preserving start and end
- Tool annotations (
readOnlyHint,destructiveHint) tell the client which tools are safe to run in parallel and which need permission
- Node.js >= 18
- macOS or Linux (node-pty uses native PTY)
MIT