Skip to main content
Glama
SHTO88

mcp-terminal-bridge

by SHTO88
README.md
# MCP Terminal Bridge (`mcp-terminal-bridge`)

A cross-platform **Model Context Protocol (MCP) server** that provides AI agents (Cursor, Windsurf, Claude Code, Antigravity, etc.) with persistent, interactive pseudo-terminals (PTYs).

Standard AI agent execution tools rely on basic child-process pipes (`child_process.exec` / `spawn`), which fail on interactive operations (like AWS SSM sessions, SSH prompts, `sudo` passwords, and REPLs) and pollute LLM context with ANSI escape noise. This project bridges that gap by running true pseudo-terminals (PTYs) with headless VT100 emulation, deterministic exit-code probing, concurrent execution queuing, and an embedded zero-config Web TUI mirror.

---

## Setup & Installation

Add `terminal-bridge` to your MCP settings (Cursor, Claude Desktop, Windsurf, Claude Code, etc.):

```json
{
  "mcpServers": {
    "terminal-bridge": {
      "command": "npx",
      "args": ["-y", "mcp-terminal-bridge"]
    }
  }
}
```

---

## The Problem Solved

- **AWS SSM Sessions** (`aws ssm start-session`): Fails or hangs because `session-manager-plugin` requires an interactive TTY and terminal dimensions.
- **SSH Connections** (`ssh user@host`): Fails with _"Pseudo-terminal will not be allocated because stdin is not a terminal"_.
- **Interactive Questions & Prompts**: Prompts like `Are you sure you want to continue connecting (yes/no)?`, `[Y/n]` confirmations, or password challenges freeze because standard tools wait indefinitely for process exit.
- **Concurrent Input Collisions**: When an AI agent fires multiple commands simultaneously, characters collide on screen and corrupt the shell prompt.
- **ANSI & Escape Noise**: Raw terminal streams contain thousands of VT100 control codes that waste tokens and confuse LLM parsers.

### The Solution:

1. **Cross-Platform PTY Layer (`node-pty`)**: Spawns true pseudo-terminals using **Windows ConPTY** on Windows and **POSIX PTY (`openpty`)** on Linux & macOS (identical to VS Code's integrated terminal).
2. **Headless Terminal Emulation (`@xterm/headless`)**: Parses raw ANSI streams into clean, human-readable 2D text grids and scrollback lines without inflating token usage.
3. **Three-Tier Completion Engine**:
   - **Sentinel Probing**: Deterministically captures command exit codes (`$?`) without terminating the remote session.
   - **Idle Quiescence**: Detects interactive prompts when output pauses (e.g. `(yes/no)?`) so the agent can respond using `terminal_send_input`.
   - **Prompt Regex**: Fallback detection for custom shell prompts.
4. **Concurrent Execution Queuing**: Parallel command executions on the same session are chained via a sequential mutex queue (`executionQueue`), preventing overlapping input, character scrambling, and screen corruption.
5. **Embedded Web TUI Mirror**: Built-in HTTP and WebSocket server running at `http://localhost:4040` (with Hub & Satellite multi-client support). Developers can watch the AI agent type live at 60 FPS, see live queued command badges (`⏳ X queued`), and toggle **Interactive Takeover** to type into the terminal directly from the browser.
6. **Asciinema Auto-Recording**: Every session is automatically recorded into standard Asciinema v2 format (`.cast` file in `~/.mcp-terminal-bridge/recordings/`) for post-mortem replay and auditing.

<p align="center">
  <img src="assets/live-terminal-preview.jpg" alt="MCP Terminal Bridge Live Web TUI" width="100%" />
  <br>
  <em>Real-time browser terminal mirror at <code>http://localhost:4040/session/:id</code> with View Only and Interactive Takeover modes.</em>
</p>

---

## Exposed MCP Tools

| Tool                           | Description                                                               | Key Parameters                                                                           |
| :----------------------------- | :------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------- |
| `terminal_ssm_connect`         | Connects to an AWS EC2 instance via SSM or SSH-over-SSM                   | `target` (Instance ID: `i-xxxx`), `region`, `profile`, `useSsh`, `sshUser`, `sshKeyPath` |
| `terminal_spawn`               | Spawns a persistent local shell or custom interactive CLI                 | `command` (default shell), `args`, `cwd`, `name`, `cols`, `rows`                         |
| `terminal_execute`             | Runs a command in an active session and waits for completion/prompt       | `sessionId`, `command`, `timeoutMs`, `useSentinel`, `idleTimeoutMs`                      |
| `terminal_send_input`          | Sends text answers, passwords, or control keys (`ctrl_c`, `enter`, `tab`) | `sessionId`, `input`, `specialKey`, `waitMs`                                             |
| `terminal_read_screen`         | Reads the current clean 2D screen text (like a screenshot in plain text)  | `sessionId`, `scrollbackLines`                                                           |
| `terminal_list_sessions`       | Lists all active sessions, process IDs, and uptimes                       | None                                                                                     |
| `terminal_close`               | Gracefully exits or terminates a session                                  | `sessionId`, `force`                                                                     |
| `terminal_check_prerequisites` | Validates presence of AWS CLI, Session Manager Plugin, and OpenSSH        | None                                                                                     |

---

## Example AI Agent Workflows

### Scenario 1: AWS SSM Session

1. **Agent initiates SSM**:
   ```json
   // Tool Call: terminal_ssm_connect
   {
     "target": "i-0123456789abcdef0",
     "region": "us-east-1"
   }
   ```
2. **Agent runs commands on the remote EC2 instance**:
   ```json
   // Tool Call: terminal_execute
   {
     "sessionId": "term-xxxx",
     "command": "uname -a && cat /etc/os-release"
   }
   ```
3. **Agent stops a runaway process with Ctrl+C**:
   ```json
   // Tool Call: terminal_send_input
   {
     "sessionId": "term-xxxx",
     "specialKey": "ctrl_c"
   }
   ```

### Scenario 2: Handling Interactive Prompts

1. Agent runs `sudo apt install nginx`.
2. Output stops with `Do you want to continue? [Y/n]`.
3. Server returns status `"idle_prompt"` with the question text.
4. Agent calls:
   ```json
   // Tool Call: terminal_send_input
   {
     "sessionId": "term-xxxx",
     "input": "Y\n"
   }
   ```

---

## Architecture

```
mcp-terminal-bridge/
├── src/
│   ├── index.ts               # FastMCP server entry point (stdio & HTTP stream)
│   ├── osal/                  # Operating System Abstraction Layer
│   │   ├── detector.ts        # Platform and shell auto-detection (Linux, macOS, Windows)
│   │   ├── paths.ts           # Binary resolution for aws, session-manager-plugin, ssh
│   │   └── keys.ts            # Terminal keystroke and control sequences
│   ├── pty/                   # Pseudo-terminal & Emulation
│   │   ├── virtual_terminal.ts# @xterm/headless 2D screen buffer & ANSI sanitization
│   │   ├── completion.ts      # Sentinel exit code detector & quiescence debounce
│   │   ├── session.ts         # Stateful PTY session wrapper
│   │   └── manager.ts         # Multi-session lifecycle registry
│   └── tools/                 # FastMCP Tool Registrations
│       └── index.ts           # Tool schemas and execution logic
├── test/
│   └── test_session.ts        # Integration test suite
├── package.json
└── tsconfig.json
```

## License

MIT