Skip to main content
Glama
Ar9av

termmirror

by Ar9av

termmirror

An MCP server that gives AI agents real interactive terminal sessions — and a live web view where a human can watch those sessions and type into them.

Most agent tooling can only run commands that exit on their own. Anything that prompts, redraws, or waits for a keystroke is out of reach: installers, ssh with a password challenge, pdb, vim, a package manager's configuration screen — and another agent's CLI. termmirror keeps real PTY sessions open so an agent can work them the way a person does, one keystroke at a time, while a human can look over its shoulder and step in when needed.

One agent driving another agent's CLI, watched live in the browser

One agent driving a Claude Code session through termmirror — it accepted the trust prompt, asked a question, and read the answer off the screen. The browser view is live, and typing in it goes straight to the same terminal.

A vim session driven keystroke by keystroke

A full-screen TUI under the same tools: the agent opened vim, moved to the end of the file, entered insert mode, and typed a line.

Features

  • Real PTY sessions. Backed by node-pty with a headless terminal emulator holding screen state. No tmux or other external dependency.

  • Built for TUIs. Alternate-screen detection, key encoding for control and navigation keys, and scrollback that unwraps soft-wrapped lines.

  • Reliable waiting. wait returns once a program has responded and settled, so the screen you read reflects your input rather than the state before it.

  • Live web view. Any session can be watched in the browser as it runs. Typing in the page goes straight to the PTY, so a human can take over and hand back.

  • Agent-to-agent. Designed so one agent can drive another agent's CLI through a multi-turn conversation.

  • Browser sessions too. The same three properties for a real Chrome window: the agent drives it by accessibility ref, a human watches and clicks in the web view, and the whole thing records.

  • Recordable. Any session can be recorded in the background and exported as a GIF, an mp4, or an asciicast, which makes demoing a terminal workflow a two-tool-call job.

Related MCP server: ddx-term-mcp

Requirements

  • Node.js 20 or newer

  • macOS or Linux

Installation

npm install termmirror

Or from source:

git clone https://github.com/Ar9av/termmirror.git
cd termmirror
npm install && npm run build

termmirror repairs node-pty's helper permissions at runtime, so --ignore-scripts costs nothing on macOS and Windows, where node-pty ships a prebuilt binding.

On Linux there is no prebuilt binding — node-pty compiles one from its own install script. With ignore-scripts=true (npm v12's default, and a common hardening setting) that is skipped silently, and the first session fails with Failed to load native module: pty.node. Build it once with npm rebuild node-pty --foreground-scripts, which needs make, g++ and python3.

Register the server with Claude Code:

claude mcp add --scope user termmirror -- node /absolute/path/to/termmirror/dist/src/index.js

--scope user matters: without it the server is registered for the current project only, and the tools are missing from every other one. The .mcp.json in this repo is the same kind of project-scoped registration — it serves the repo's own development, needs npm run build to have run, and Claude Code asks to approve it the first time.

Any MCP client works; the server speaks stdio.

Usage

Three tools, used in this order:

send_input  →  wait  →  read_screen

read_screen returns the screen as it looks now — a screenshot, not a log of everything printed. wait is what makes that screenshot worth reading: it blocks until the program has responded and gone quiet, so you see the state after your keystroke.

Tools

Tool

Description

create_session

Starts a session ($SHELL by default, or any command) and returns its id plus a URL to watch it.

send_input

Types text into a session, optionally submitting it with Enter.

send_keys

Sends ctrl+c, escape, tab, arrow keys, f1f12, alt+X, and similar.

read_screen

Returns the visible screen as plain text; scrollback reaches into history.

wait

Waits for the session to respond and settle (idle), or for text to appear (pattern).

list_sessions

Lists all sessions, alive or exited, with watch URLs.

resize

Changes the terminal dimensions.

start_recording

Begins capturing the session's output in the background.

stop_recording

Finishes the recording and renders it to GIF, mp4, or asciicast.

browser_open

Launches a Chrome window the agent drives, and returns a URL to watch it.

browser_navigate

Loads a URL and returns the page snapshot.

browser_snapshot

Returns the page as an accessibility tree with a [ref=eN] per element.

browser_act

Clicks, types, presses, hovers, selects, scrolls or uploads, and returns the new snapshot.

browser_tabs

Lists, switches, opens and closes tabs.

browser_wait

Waits for the network to settle, or for text to appear on the page.

kill_session

Terminates a session, terminal or browser.

Waiting

idle is the default and the right choice in most cases. Interactive programs redraw continuously while they work and fall silent when it is your turn, so silence is a reliable completion signal — including for spinners and TUIs that expose no prompt to match against. Increase timeout for slow operations.

A no_output result means nothing came back since your input: the program is either busy and silent, or the input never registered.

Use pattern when the expected text is known. It matches against the whole screen, including your own echoed input, so wait for something the program prints rather than a marker you just typed.

Watching a session

The first session starts a local web server on port 7878, or the next free port if something is already there — most often a termmirror that outlived its client. Open the URL returned by create_session to see the session live. Typing in the page writes directly to the PTY, which lets a human enter a password or answer a prompt the agent should not handle, then hand control back — no handoff protocol, just the same terminal from the other side.

Variable

Effect

TERMINAL_UI_PORT

Port for the web view. Set explicitly, a busy port is an error rather than a fallback; 0 picks a free port.

TERMINAL_NO_UI

Set to 1 to disable the web view entirely.

Browser sessions

A browser is the same kind of session as a terminal, under its own tools:

browser_open  →  browser_act  →  (browser_act again)

browser_act returns the page snapshot it produced, so one call per step is usually enough. The snapshot is an accessibility tree rather than a picture:

- heading "Example Domain" [level=1] [ref=e2]
- link "More information..." [ref=e4]

Every element carries a [ref=eN], and browser_act targets those refs — or a CSS selector, if that is easier. This is what makes a click deterministic: no coordinates to guess and nothing for a vision model to misread. Pass screenshot: true to browser_snapshot when the layout itself is the question.

A big page is cut at about 4k tokens, with a note saying how much was left out — a news front page is three times that in full and a Wikipedia article eight times, and every action returns a snapshot. depth shows the whole page in less detail; full: true returns all of it.

termmirror drives the Google Chrome already installed on the machine and never downloads a browser of its own. The window is visible by default; pass headless on a server. Pass profile to reuse a named profile under ~/.termmirror/profiles, which keeps logins between sessions — one session at a time per profile, since Chrome will not open a profile twice.

A browser session appears in the web view like any other, as a live image of the page. Clicks, scrolls and keystrokes in that image go to the same page the agent is driving, so a human can solve a login or a CAPTCHA and hand straight back.

Tabs, dialogs, downloads and uploads

A click that opens a new tab switches to it and returns that tab's snapshot, because that is invariably what the click was for and an agent left talking to the page underneath has no way to notice. browser_tabs goes back, opens another, or closes one; the snapshot says which tab it came from whenever more than one is open.

A dialog freezes the page until it is answered, so the answer cannot be decided after the fact. Set dialog on the browser_act call that raises one, with dialog_text for a prompt(). Anything unanswered is dismissed, and the next snapshot reports what the dialog said and what was done with it.

Downloads are saved under ~/.termmirror/downloads/<session>/ and named in the next snapshot. Uploads are browser_act with kind upload and a list of local files.

Recording a session

start_recording captures everything the session prints from that point on, passively, while the agent keeps driving it as usual. stop_recording finishes the capture and renders it:

start_recording  →  (drive the session)  →  stop_recording

Recordings are written as asciicast v2 (.cast) under ~/.termmirror/recordings/ unless a path is given. A .cast is a complete recording on its own — asciinema play file.cast replays it — and stop_recording renders it to a shareable file:

format

Output

Needs

gif (default)

Animated GIF

agg

mp4

H.264 video

agg and ffmpeg

cast

The asciicast only

nothing

brew install agg ffmpeg   # or: cargo install --git https://github.com/asciinema/agg

Pauses longer than idle_time_limit seconds (default 2) are shortened in the render — a session spends most of its wall clock waiting on an agent turn or a build, and none of that is worth watching at real speed. Pass idle_time_limit: null to keep the original timing, and speed to scale the whole thing.

select cuts a range instead, in seconds — "40:" from 40s on, ":90" up to 90s, "40:90" between. Use it for a stretch that is busy but not worth watching: a spinner redrawing for a minute is never idle, so idle_time_limit will not touch it.

A browser session records the same way, through the same two tools. It captures video rather than an event stream, so it writes a .webm, renders with ffmpeg alone, and ignores idle_time_limit and select — a video has no event timings to re-stamp. Recording also turns on Playwright's action overlay, so the video shows a cursor moving to each click and highlights what it hit, rather than a page that changes for no visible reason.

Neither binary ships with termmirror. Without them stop_recording still returns the .cast along with a note on how to install what was missing, so a recording is never lost to a missing renderer. Recordings are also finalized automatically when the process exits or the session is killed.

Example

node examples/drive-claude.mjs "what is 2+2? reply with just the number"

One agent starts claude, accepts its trust prompt, asks a question, and reads the answer off the screen — the same tools the MCP interface exposes, written out longhand.

Development

npm run build    # compile TypeScript to dist/
npm test         # build, then run the test suite

The suite covers the wait semantics the rest of the server depends on, key encoding, scrollback, the web view's live stream and take-over typing, browser snapshots, actions, frames and video, and an end-to-end run over the real MCP protocol. The browser tests skip themselves when Chrome is not installed.

A note on the name

The package is termmirror, with two m's. alias/termirror/ is a stub package that depends on it so the one-m spelling resolves to the right place.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides AI agents with fully interactive terminal sessions, including TUI support, keyboard control, and screen capture across Windows, Linux, and Mac.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to execute commands in a shared tmux session that is simultaneously visible to a human via a web-based terminal UI.
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to spawn and interact with real terminal sessions, capturing screenshots of rendered TUI output and sharing live sessions for debugging.
    4 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to securely execute terminal commands with persistent sessions, async jobs, and mission control, while providing a live dashboard for human oversight.
    4
    Apache 2.0