Skip to main content

MCP server for interactive terminal sessions (SSH, REPLs, database CLIs)

Project description

terminal-mcp banner

MCP server for interactive terminal sessions — SSH, REPLs, database CLIs, and TUI apps.

PyPI Python 3.10+ License: MIT CI CodeQL

Install in VS Code Install in VS Code Insiders Install in Cursor Install in Claude Desktop

terminal-mcp demo


Why This Exists

If you've hit any of these limitations with Claude Code, terminal-mcp solves them:

  • "Claude Code can't handle interactive sessions" — The built-in Bash tool runs each command in a fresh subprocess. No persistence, no back-and-forth.
  • "SSH not supported in Claude Code" — You can't SSH into a server and run multiple commands across an active connection.
  • "Claude Code Bash tool doesn't support REPLs" — Python, Node, Ruby, and other interpreters need a persistent session for multi-line interaction.
  • "How to use psql / mysql / redis-cli with Claude Code" — Database CLIs require a live connection that survives across tool calls.
  • "Interactive terminal not working in Claude Code" — TUI apps (htop, vim, ncdu, fzf) need a real PTY with special key support.
  • "Claude Code can't send arrow keys or Tab" — The Bash tool has no concept of terminal escape sequences.

terminal-mcp fills this gap by exposing MCP tools that create and manage real PTY sessions. Each session runs as a persistent child process; you send input, special keys, and control characters and read output across multiple tool calls for as long as the session lives.

Features

  • Persistent PTY sessions — real terminal sessions that survive across tool calls
  • Special key support — arrow keys, Tab, Escape, function keys (F1–F12), Home/End, Page Up/Down
  • Control characters — Ctrl-C, Ctrl-D, Ctrl-Z, Ctrl-L, and telnet escape
  • Two read modes — stream (waits for output to settle) and snapshot (pyte screen buffer for TUI apps)
  • ANSI stripping — optional removal of escape sequences for clean text output
  • Idle cleanup — automatic session cleanup after configurable timeout
  • Session management — list, label, and manage multiple concurrent sessions
  • Dynamic resize — resize terminal dimensions on the fly with SIGWINCH support
  • Secret input — send passwords without logging them
  • Scrollback history — access terminal scrollback buffer beyond the visible screen
  • One-shot execution — run a single command without manual session management
  • Output truncation — automatic truncation of large outputs to prevent context overflow
  • Env var configuration — configure all settings via TERMINAL_MCP_* environment variables
  • PyPI distribution — install directly with pip install terminal-mcp

Supported Clients

Client Status Install
Claude Code (CLI) ✅ Supported ~/.claude.json or .mcp.json
Claude Desktop ✅ Supported One-click install
VS Code (Copilot Chat) ✅ Supported One-click install or .vscode/mcp.json
Cursor ✅ Supported One-click install or Settings → MCP
Windsurf ✅ Supported ~/.codeium/windsurf/mcp_config.json

Quickstart

Install

Recommended — no install needed:

uvx terminal-mcp

Or install via pip:

pip install terminal-mcp

Or from source:

git clone https://github.com/mkpvishnu/terminal-mcp.git
cd terminal-mcp
pip install -e ".[dev]"

Register with Claude Code

Add to ~/.claude.json (or project .mcp.json):

{
  "mcpServers": {
    "terminal": {
      "command": "uvx",
      "args": ["terminal-mcp"]
    }
  }
}

Register with Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "terminal": {
      "command": "uvx",
      "args": ["terminal-mcp"]
    }
  }
}

Register with VS Code / Cursor

Click the one-click install badge above, or add to .vscode/mcp.json:

{
  "servers": {
    "terminal-mcp": {
      "command": "uvx",
      "args": ["terminal-mcp"]
    }
  }
}

Verify it works

session_exec  exec="echo hello from terminal-mcp"

Demo

SSH session to a remote server
session_create  command="ssh user@myserver.example.com"  label="prod-ssh"
session_read    session_id="a1b2c3d4"  timeout=5.0
session_send    session_id="a1b2c3d4"  password="mypassword"
session_send    session_id="a1b2c3d4"  input="df -h"
session_read    session_id="a1b2c3d4"
session_close   session_id="a1b2c3d4"
Python REPL
session_create  command="python3"  label="repl"
session_read    session_id="e5f6g7h8"
session_send    session_id="e5f6g7h8"  input="import math"
session_send    session_id="e5f6g7h8"  input="print(math.sqrt(144))"
session_read    session_id="e5f6g7h8"
session_close   session_id="e5f6g7h8"
TUI navigation with special keys
session_create  command="python3 -m openclaw configure"  label="openclaw"
session_read    session_id="x1y2z3w4"  timeout=3.0
session_send    session_id="x1y2z3w4"  key="down"
session_send    session_id="x1y2z3w4"  key="down"
session_send    session_id="x1y2z3w4"  key="enter"
session_read    session_id="x1y2z3w4"
session_send    session_id="x1y2z3w4"  key="tab"
session_read    session_id="x1y2z3w4"
session_close   session_id="x1y2z3w4"
One-shot command execution
session_exec  exec="ls -la /tmp"
session_exec  exec="python3 -c 'print(42)'"  command="bash"  timeout=10.0
Sending Ctrl-C to interrupt
session_send    session_id="a1b2c3d4"  control_char="c"
session_read    session_id="a1b2c3d4"

Tool Reference

session_create

Spawn a persistent PTY terminal session.

Parameter Type Required Default Description
command string Yes Shell command to run (e.g. bash, python3, ssh user@host)
label string No command name Human-readable label
rows integer No 24 Terminal height
cols integer No 80 Terminal width
idle_timeout integer No 1800 Seconds before auto-close
enable_snapshot boolean No false Enable pyte screen buffer for snapshot reads
scrollback_lines integer No 1000 Scrollback history lines (requires enable_snapshot)

Returns: session_id, label, pid, created_at

session_send

Send input text, a control character, or a special key to an active session. Only one of input, control_char, key, or password may be provided per call.

Parameter Type Required Default Description
session_id string Yes Session ID from session_create
input string No Text to send
press_enter boolean No true Append carriage return after input
control_char string No Control character: c d z l ]
key string No Special key (see table below)
password string No Password or secret (not logged)

Returns: bytes_sent

Supported special keys
Key Description Key Description
up Arrow up f1f12 Function keys
down Arrow down home Home
left Arrow left end End
right Arrow right page-up Page Up
tab Tab page-down Page Down
shift-tab Shift+Tab insert Insert
escape Escape delete Delete
enter Enter backspace Backspace
Supported control characters
Char Signal Description
c SIGINT Interrupt (Ctrl-C)
d EOF End of file / logout (Ctrl-D)
z SIGTSTP Suspend (Ctrl-Z)
l Clear screen (Ctrl-L)
] Telnet escape

session_resize

Resize the terminal window of an active session.

Parameter Type Required Default Description
session_id string Yes Session ID
rows integer Yes New terminal height
cols integer Yes New terminal width

Returns: rows, cols

session_read

Read output from a session.

Parameter Type Required Default Description
session_id string Yes Session ID
mode string No stream stream or snapshot
timeout number No 2.0 Settle timeout in seconds (stream mode)
strip_ansi boolean No true Strip ANSI escape sequences
scrollback integer No Lines of scrollback history (snapshot mode)

Returns: output, bytes_read, prompt_detected, is_alive, truncated, total_lines (when scrollback used)

session_close

Terminate a session gracefully (EOF → SIGHUP → SIGKILL).

Parameter Type Required Description
session_id string Yes Session ID to close

Returns: exit_status

session_exec

Execute a command in a temporary session and return output. The session is automatically cleaned up.

Parameter Type Required Default Description
exec string Yes Command to execute
command string No bash Shell to use
timeout number No 5.0 Seconds to wait for output
rows integer No 24 Terminal height
cols integer No 80 Terminal width

Returns: output, bytes_read, session_id

session_list

List all active sessions with their status and idle time.

Returns: sessions (array), count

Architecture

flowchart LR
    Client[AI Client] -->|MCP JSON-RPC| Server[terminal-mcp]
    Server --> SM[Session Manager]
    SM --> S1[PTY 1: bash]
    SM --> S2[PTY 2: python3]
    SM --> S3[PTY 3: ssh user@host]
    S1 & S2 & S3 -.->|PTY output| Reader[Reader Thread]
    Reader -.->|buffer| Server
stateDiagram-v2
    [*] --> Active : session_create
    state Active {
        Idle --> Sending : session_send
        Sending --> Idle
        Idle --> Reading : session_read
        Reading --> Idle
        Idle --> Resizing : session_resize
        Resizing --> Idle
    }
    Active --> [*] : session_close
    Active --> [*] : idle_timeout

Each session is backed by a real PTY allocated via pexpect.spawn. The design has four main parts:

Background reader thread. A daemon thread continuously reads from the PTY file descriptor in 4096-byte chunks and appends bytes to an in-memory buffer. The thread is lock-protected and dies automatically when the child process exits.

Output settling (stream mode). session_read in stream mode polls the buffer until no new bytes have arrived for timeout seconds (default 2s), then returns everything written since the last read call. A hard ceiling of timeout + 10s prevents infinite blocking.

Snapshot mode. When a session is created with enable_snapshot=true, all PTY output is also fed into a pyte virtual screen buffer. session_read with mode="snapshot" returns the current rendered screen — useful for programs that use cursor movement (vim, htop, ncdu).

Idle cleanup. SessionManager runs a background cleanup loop (every 60s by default) that closes sessions idle longer than their idle_timeout. The default timeout is 30 minutes. Concurrent sessions are capped at 10 by default.

Configuration

All settings can be overridden via environment variables prefixed with TERMINAL_MCP_:

Setting Env Var Default Description
max_sessions TERMINAL_MCP_MAX_SESSIONS 10 Maximum concurrent sessions
idle_timeout TERMINAL_MCP_IDLE_TIMEOUT 1800 Seconds before auto-close
default_rows TERMINAL_MCP_DEFAULT_ROWS 24 Default terminal height
default_cols TERMINAL_MCP_DEFAULT_COLS 80 Default terminal width
read_settle_timeout TERMINAL_MCP_READ_SETTLE_TIMEOUT 2.0 Output settle timeout
max_output_bytes TERMINAL_MCP_MAX_OUTPUT_BYTES 100000 Max bytes per read
cleanup_interval TERMINAL_MCP_CLEANUP_INTERVAL 60 Seconds between cleanup

Per-session overrides for rows, cols, and idle_timeout can be passed to session_create.

Changelog

v0.3.2

  • Buffer memory cap — per-session PTY buffer capped at 1MB (configurable via TERMINAL_MCP_MAX_BUFFER_BYTES), prevents unbounded memory growth on long-running sessions
  • Async event loop — all blocking PTY calls wrapped in asyncio.to_thread(), unblocking the event loop for concurrent MCP requests
  • Snapshot ANSI strippingstrip_ansi parameter now correctly applied in snapshot and scrollback read modes
  • Exec output truncationsession_exec now applies max_output_bytes truncation to prevent context overflow
  • SIGTERM cleanup — added signal handler to close all PTY sessions on SIGTERM (Docker stop, kill, systemd)
  • Removed unused import atexit from server.py

v0.3.1

  • MCP registry publication — added mcp-name marker and server.json for official MCP registry
  • Version bump for registry metadata

v0.3.0

  • Output truncation — large outputs are now automatically truncated to max_output_bytes (100KB default)
  • Environment variable config — all settings configurable via TERMINAL_MCP_* env vars
  • session_resize tool — dynamically resize terminal dimensions (sends SIGWINCH)
  • Secret inputpassword parameter on session_send for credentials (redacted from logs)
  • Scrollback bufferpyte.HistoryScreen with configurable history depth; scrollback param on session_read
  • session_exec tool — one-shot command execution with automatic session cleanup
  • PyPI publishingpip install terminal-mcp via trusted publishing workflow

v0.2.0

  • Special key support — arrow keys, Tab, Escape, function keys (F1–F12), Home/End, Page Up/Down, and more via the key parameter on session_send
  • Mutual exclusivityinput, control_char, and key are now validated as mutually exclusive
  • Added GitHub Actions CI (Python 3.10–3.13) and CodeQL security scanning
  • Added project metadata, classifiers, and MIT license

v0.1.0

  • Initial release
  • Persistent PTY sessions via pexpect
  • Stream and snapshot read modes
  • Control character support (Ctrl-C, Ctrl-D, Ctrl-Z, Ctrl-L)
  • Session management with idle cleanup

Running Tests

pip install -e ".[dev]"
pytest tests/ -v

Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: pytest tests/ -v
  5. Submit a pull request

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

terminal_mcp-0.3.2.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

terminal_mcp-0.3.2-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file terminal_mcp-0.3.2.tar.gz.

File metadata

  • Download URL: terminal_mcp-0.3.2.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for terminal_mcp-0.3.2.tar.gz
Algorithm Hash digest
SHA256 7a59c8c0d3da0cb95fd686174853aabd97f256fef947221dd53c8161d51f9217
MD5 44503182626c7a7c6f080ceeca5b3af7
BLAKE2b-256 3b6814add12d25080a1f2fc9f70e6da63a583e58cb03d85fe08ec000f55faed6

See more details on using hashes here.

Provenance

The following attestation bundles were made for terminal_mcp-0.3.2.tar.gz:

Publisher: publish.yml on mkpvishnu/terminal-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file terminal_mcp-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: terminal_mcp-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for terminal_mcp-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7b407f15412d56709893da46d72ca27f30d35d774e3f6035e7ea366740011f34
MD5 e78fbb34ab9369c606bcff57894eb004
BLAKE2b-256 90931d55e62756a0a98471873ff2cd1cf78b954c020ed8167ad6d6c0f3923a68

See more details on using hashes here.

Provenance

The following attestation bundles were made for terminal_mcp-0.3.2-py3-none-any.whl:

Publisher: publish.yml on mkpvishnu/terminal-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page