A PTY-based MCP server with strong Windows support, giving MCP-capable AI clients and their agents persistent, interactive shell access via pseudo-terminals (node-pty). Unlike simple exec-based approaches, this keeps PTY-backed shell sessions alive across steps, with bidirectional communication for interactive CLI tools, incremental reads, and session state that carries forward. See the Changelog
npx mdskills install pungggi/smart-terminal-mcp@pungggi? Sign in with GitHub to claim this listing.Comprehensive PTY-based terminal server with excellent tool variety and strong cross-platform support
1# smart-terminal-mcp2[](https://smithery.ai/server/pungggi/smart-terminal)3[](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.pungggi/smart-terminal)45A PTY-based MCP server with strong Windows support, giving MCP-capable AI clients and their agents persistent, interactive shell access via pseudo-terminals ([node-pty](https://github.com/microsoft/node-pty)).67Unlike simple `exec`-based approaches, this keeps PTY-backed shell sessions alive across steps, with bidirectional communication for interactive CLI tools, incremental reads, and session state that carries forward.89See the [Changelog](CHANGELOG.md) for recent updates.10## Why use this instead of your AI client's built-in terminal?1112Install this if you want a more consistent terminal workflow across AI clients, instead of relying on whatever built-in terminal behavior a single client happens to provide.1314This MCP is most useful when you want:1516- **Portable workflow across clients** -- The same terminal tools and habits work across Claude Code, Cursor, Trae, Antigravity, and other MCP-capable clients.17- **Reusable prompts and tooling** -- Workflows built around tools like `terminal_wait`, `terminal_retry`, `terminal_run_paged`, and `terminal_get_history` are easier to reuse across teams and clients, with less lock-in to one client's terminal behavior.18- **Persistent terminal state** -- Keep the same shell session alive across steps, including the current folder, environment, and running processes.19- **Better interactive behavior** -- Safely handle tools that expect TTY behavior—such as `vim`, `npm` prompts, or interactive installers that often hang on standard piped stdin. Support full interactive sessions with REPLs, Ctrl+C, arrow keys, and dynamic prompts.20- **More control over large output** -- Truncate, page, diff, retry, wait for patterns, or fetch history instead of dumping everything at once.21- **More predictable automation** -- Use deterministic completion markers instead of guessing when a command is done.2223If your AI client already provides a stable, stateful, interactive terminal with good output handling, you may not need this MCP for basic command execution. The main reason to add it is to make terminal-driven workflows more explicit, reusable, and portable across clients.2425## Features2627Think of this as a **controlled keyboard + terminal for an agent running inside an MCP client**. It opens a persistent PTY-backed shell session so the agent can send commands and keystrokes, read output, and continue working in the same session.2829### Core terminal features3031- **Interactive terminal sessions** -- Keeps a persistent PTY-backed shell session open so the agent can send input, read output, and pick up where it left off.32- **Deterministic command completion** -- `terminal_exec` uses unique markers so it can tell when a command has finished.33- **Clean output** -- Pre-command markers help keep returned output readable, even when shells echo commands or expand aliases.34- **Working directory tracking** -- `terminal_exec` reports the current folder after each command.3536### Long output and long-running commands3738- **Interactive reads and writes** -- `terminal_write` + `terminal_read` support prompts, REPLs, and other interactive programs without leaving the current session.39- **Pattern waiting** -- `terminal_wait` can pause until specific text appears, such as `server listening on port`.40- **Retry helper** -- `terminal_retry` can re-run flaky commands with bounded backoff and optional output matching.41- **Best-effort progress notifications** -- Long `terminal_exec` / `terminal_wait` calls can emit `notifications/progress` when the client provides a progress token.42- **Output truncation** -- `terminal_exec` and `terminal_read` shorten very large output by returning the beginning and the end.43- **Paged read-only output** -- `terminal_run_paged` returns large read-only output one page at a time instead of sending the full result at once.44- **Output diffing** -- `terminal_diff` compares two command results and returns a unified diff.4546### Safety and usability4748- **Safer one-shot commands** -- `terminal_run` executes binaries directly with `cmd + args` and `shell=false` for more predictable automation.49- **Structured parsers** -- Some supported read-only commands can return both raw text and parsed output.50- **Blocking mitigations** -- Disables pagers (`GIT_PAGER=cat`, `PAGER=cat`), suppresses PowerShell progress output, and sets UTF-8 for `cmd.exe` on Windows.51- **Special key support** -- Can send Ctrl+C, Tab, arrow keys, and similar keys without manually constructing escape sequences.52- **Session management** -- Supports named sessions, idle cleanup, and up to 10 concurrent sessions.53- **Shell auto-detection** -- Windows: `pwsh.exe` > `powershell.exe` > `cmd.exe`. Linux/macOS: `$SHELL` > `bash` > `sh`.5455Progress notifications are not the same as full stdout streaming. They currently send periodic status updates for `terminal_exec` and `terminal_wait`, usually based on elapsed time and the latest output line. Whether you see them depends on your MCP client.5657## Token efficiency5859This MCP does not magically compress terminal output, but it **can help agents use fewer tokens in terminal-heavy workflows** by returning smaller, more targeted responses and making it easier to revisit output only when needed.6061The main benefit is **model-context efficiency**, not guaranteed savings in the underlying command's runtime or total bytes produced.6263- Use **`terminal_run_paged`** for large read-only output when **the agent** wants one page of the returned result at a time.64- Lower **`maxLines`**, **`pageSize`**, or **`tailLines`** when **the agent** only needs a narrow slice of the output.65- Use **`summary: true`** or **`parseOnly: true`** with `terminal_run` when **the agent** benefits more from structured results than raw text.66- Use **`terminal_wait({ returnMode: "match-only" })`** when the agent only needs to know whether a pattern appeared.67- Use **`terminal_get_history`** when **the agent** needs to revisit earlier output without re-dumping the whole session into the conversation.6869In practice, this lets agents inspect terminal state more selectively instead of repeatedly dumping large logs back into the conversation.7071## Installation7273Recommended: run the stable release directly via `npx`:7475```bash76npx smart-terminal-mcp@stable77```7879Or install globally:8081```bash82npm install -g smart-terminal-mcp83```8485Or clone for development:8687```bash88git clone <repo-url>89cd smart-terminal-mcp90npm install91```9293## Configuration9495### Claude Desktop9697Add to your `claude_desktop_config.json`:9899```json100{101 "mcpServers": {102 "smart-terminal": {103 "command": "npx",104 "args": ["-y", "smart-terminal-mcp@stable"]105 }106 }107}108```109110### Claude Code111112```bash113claude mcp add smart-terminal -- npx -y smart-terminal-mcp@stable114```115116### Augment Code117118Add to your Augment MCP settings:119120```json121{122 "mcpServers": {123 "Smart Terminal": {124 "command": "npx",125 "args": [126 "smart-terminal-mcp@stable"127 ]128 }129 }130}131```132133If you want to pin an exact release instead of following the stable tag, replace `@stable` with a version such as `@1.0.1`.134135## Tools136137### `terminal_start`138139Start a new interactive terminal session.140141| Param | Type | Default | Description |142|-------|------|---------|-------------|143| `shell` | string | auto-detected | Shell executable (e.g. `pwsh.exe`, `bash`) |144| `cols` | number | 120 | Terminal width |145| `rows` | number | 30 | Terminal height |146| `cwd` | string | server CWD | Working directory |147| `name` | string | -- | Friendly session name |148| `env` | object | -- | Custom environment variables (e.g. `{ "NODE_ENV": "test" }`) |149150**Returns**: `sessionId`, `shell`, `shellType`, `cwd`, `banner`151152### `terminal_exec`153154Execute a command with deterministic completion detection. Large outputs are truncated to head + tail based on `maxLines`. If the MCP client sends a `progressToken`, long-running calls may also emit best-effort `notifications/progress` updates.155156| Param | Type | Default | Description |157|-------|------|---------|-------------|158| `sessionId` | string | *required* | Session ID |159| `command` | string | *required* | Command to execute |160| `timeout` | number | 30000 | Timeout in ms (max 10min) |161| `maxLines` | number | 200 | Max output lines before truncation |162163**Returns**: `output`, `exitCode`, `cwd`, `timedOut`164165### `terminal_run`166167Run a one-shot non-interactive command using `cmd + args` with `shell=false`. Safer than `terminal_exec` for predictable automation. Output is capped by `maxOutputBytes` rather than head + tail truncation. Shell built-ins such as `dir` or `cd` are not supported. On Windows, `terminal_run` resolves `PATH`/`PATHEXT` and launches `.cmd` / `.bat` wrappers via `cmd.exe` when needed. Prefer passing the target executable directly as `cmd` instead of wrapping it in `powershell -Command` or `cmd /c`, especially when Windows paths contain spaces. For tools that always exit `0`, you can require a specific exit code and/or validate a success regex against a file written by the command.168169| Param | Type | Default | Description |170|-------|------|---------|-------------|171| `cmd` | string | *required* | Executable to run |172| `args` | string[] | `[]` | Argument array passed directly to the executable |173| `cwd` | string | server CWD | Working directory |174| `timeout` | number | 30000 | Timeout in ms |175| `maxOutputBytes` | number | 102400 | Max combined stdout/stderr bytes to capture |176| `parse` | boolean | `true` | Attempt structured parsing for supported commands |177| `parseOnly` | boolean | `false` | Drop raw stdout if parsed |178| `summary` | boolean | `false` | Return a concise summary when supported |179| `successExitCode` | number or `null` | 0 | Exit code required for success |180| `successFile` | string | -- | Optional file to validate after exit |181| `successFilePattern` | string | -- | Regex that must match `successFile` |182183**Returns**: `ok`, `cmd`, `args`, `cwd`, `exitCode`, `timedOut`, `durationMs`, `stdout.raw`, `stdout.parsed`, optional `stdout.summary`, `stderr.raw`, optional `checks`, optional `hint`184185### `terminal_run_paged`186187Run a read-only one-shot command using `cmd + args` with `shell=false` and return a single page of stdout lines from the captured output. This pages the returned result instead of using head + tail truncation. Paged mode does not parse partial output, but it can return a concise summary for supported read-only commands when `summary: true`.188189| Param | Type | Default | Description |190|-------|------|---------|-------------|191| `cmd` | string | *required* | Read-only executable to run |192| `args` | string[] | `[]` | Argument array passed directly to the executable |193| `cwd` | string | server CWD | Working directory |194| `timeout` | number | 30000 | Timeout in ms |195| `maxOutputBytes` | number | 102400 | Max combined stdout/stderr bytes to capture |196| `page` | number | 0 | 0-indexed page number |197| `pageSize` | number | 100 | Lines per page |198| `summary` | boolean | `false` | Return a concise summary when supported |199200**Returns**: Same envelope as `terminal_run`, plus `pageInfo.page`, `pageInfo.pageSize`, `pageInfo.totalLines`, `pageInfo.hasNext`201202### `terminal_write`203204Write raw data to a terminal (for interactive programs). Follow with `terminal_read`.205206| Param | Type | Description |207|-------|------|-------------|208| `sessionId` | string | Session ID |209| `data` | string | Data to write (`\r` for Enter, `\t` for Tab) |210211### `terminal_read`212213Read buffered output with idle detection. Large outputs are truncated to head + tail based on `maxLines`.214215| Param | Type | Default | Description |216|-------|------|---------|-------------|217| `sessionId` | string | *required* | Session ID |218| `timeout` | number | 30000 | Hard timeout in ms |219| `idleTimeout` | number | 500 | Return after this many ms of silence |220| `maxLines` | number | 200 | Max output lines |221222**Returns**: `output`, `timedOut`223224### `terminal_get_history`225226Retrieve past terminal output without consuming it. Non-destructive — returns historical output from a rolling buffer (last ~10,000 lines). Useful for reviewing output that was already read or missed.227228| Param | Type | Default | Description |229|-------|------|---------|-------------|230| `sessionId` | string | *required* | Session ID |231| `offset` | number | 0 | Lines to skip from the end (0 = most recent). Use for pagination. |232| `maxLines` | number | 200 | Max lines to return |233| `format` | string | `"lines"` | Response format: `lines` or `text` |234235**Returns**: `lines` or `text`, plus `totalLines`, `returnedFrom`, `returnedTo`236237These defaults favor agent usability while still allowing tool callers to lower `maxLines` or `pageSize` explicitly when they want tighter responses.238239### `terminal_send_key`240241Send a named special key.242243| Param | Type | Description |244|-------|------|-------------|245| `sessionId` | string | Session ID |246| `key` | string | Key name (see below) |247248**Supported keys**: `ctrl+c`, `ctrl+d`, `ctrl+z`, `ctrl+l`, `ctrl+a`, `ctrl+e`, `ctrl+u`, `ctrl+k`, `ctrl+w`, `tab`, `enter`, `escape`, `up`, `down`, `left`, `right`, `home`, `end`, `pageup`, `pagedown`, `backspace`, `delete`, `f1`-`f12`249250### `terminal_wait`251252Wait for a specific pattern in the output stream. By default, responses return only the last `tailLines`; use `returnMode: "full"` for the full matched output or `"match-only"` to suppress output entirely. If the MCP client sends a `progressToken`, long-running waits may also emit best-effort `notifications/progress` updates.253254| Param | Type | Default | Description |255|-------|------|---------|-------------|256| `sessionId` | string | *required* | Session ID |257| `pattern` | string | *required* | String or regex pattern |258| `timeout` | number | 30000 | Timeout in ms |259| `returnMode` | string | `"tail"` | Response mode: `tail`, `full`, `match-only` |260| `tailLines` | number | 50 | Number of tail lines to return |261262**Returns**: `output`, `matched`, `timedOut` (`output` may be empty in `match-only` mode)263264### `terminal_retry`265266Retry a command in the same terminal session until it succeeds or retries are exhausted.267268| Param | Type | Default | Description |269|-------|------|---------|-------------|270| `sessionId` | string | *required* | Session ID |271| `command` | string | *required* | Command to execute |272| `maxRetries` | number | 3 | Retry count after the first attempt |273| `backoff` | string | `"exponential"` | Backoff mode: `fixed`, `linear`, `exponential` |274| `delayMs` | number | 1000 | Base delay in ms |275| `timeout` | number | 30000 | Timeout per attempt in ms |276| `maxLines` | number | 200 | Max output lines per attempt |277| `successExitCode` | number or `null` | 0 | Exit code required for success |278| `successPattern` | string or `null` | `null` | Optional regex that must match output |279280**Returns**: `success`, `attempts`, `lastResult`, `history`281282### `terminal_diff`283284Run two commands in the same terminal session and return a bounded unified diff of their outputs.285286| Param | Type | Default | Description |287|-------|------|---------|-------------|288| `sessionId` | string | *required* | Session ID |289| `commandA` | string | *required* | Baseline command |290| `commandB` | string | *required* | Comparison command |291| `timeout` | number | 30000 | Timeout per command in ms |292| `maxLines` | number | 200 | Max output lines per command |293| `contextLines` | number | 3 | Diff context lines |294295**Returns**: `resultA`, `resultB`, `diff`, `identical`296297### `terminal_resize`298299Resize terminal dimensions.300301| Param | Type | Description |302|-------|------|-------------|303| `sessionId` | string | Session ID |304| `cols` | number | New width |305| `rows` | number | New height |306307### `terminal_stop`308309Stop and clean up a terminal session.310311| Param | Type | Description |312|-------|------|-------------|313| `sessionId` | string | Session ID to stop |314315### `terminal_write_file`316317Write content directly to a file on disk. Resolves paths relative to the session's CWD. Safer and more robust than piping content through `echo` — handles special characters, newlines, and large files correctly.318319| Param | Type | Default | Description |320|-------|------|---------|-------------|321| `sessionId` | string | *required* | Session ID (used to resolve working directory) |322| `path` | string | *required* | File path (relative to session CWD, or absolute) |323| `content` | string | *required* | File content to write |324| `encoding` | string | `"utf-8"` | File encoding (`utf-8`, `ascii`, `base64`, `hex`, `latin1`) |325| `append` | boolean | `false` | Append to file instead of overwriting |326327**Returns**: `success`, `path` (absolute), `size` (bytes), `append`328329### `terminal_list`330331List all active terminal sessions.332333| Param | Type | Default | Description |334|-------|------|---------|-------------|335| `verbose` | boolean | `true` | Include full metadata |336337**Returns**: `sessions`, `count` (`verbose: false` returns `id`, `name`, `cwd`, `alive`, `busy` only)338339## Usage Examples340341### Run a command342343```344terminal_start() -> { sessionId: "a1b2c3d4" }345terminal_exec({ sessionId, command: "ls -la" }) -> { output: "...", exitCode: 0, cwd: "/home/user" }346```347348### Run a safe one-shot command349350```351terminal_run({ cmd: "git", args: ["status", "--porcelain=v1", "--branch"] })352-> { ok: true, stdout: { raw: "...", parsed: { branch: {...}, staged: [], modified: [], untracked: [] } } }353```354355For Windows tools installed under `Program Files`, prefer this shape over `powershell -Command`:356357```358terminal_run({ cmd: "C:\\Program Files\\Vendor\\Tool.exe", args: ["/flag:value", "/other"] })359```360361For compilers that only report success in a log file:362363```364terminal_run({365 cmd: "C:\\Program Files\\Vendor\\Tool.exe",366 args: ["/compile:script.mq5", "/log:build.log"],367 successFile: "build.log",368 successFilePattern: "0 error"369})370```371372### Page through large read-only output373374```375terminal_run_paged({ cmd: "git", args: ["log", "--oneline"], page: 0, pageSize: 100 })376-> { ok: true, stdout: { raw: "...", parsed: null }, pageInfo: { totalLines: 120, hasNext: true } }377```378379### Interactive Python REPL380381```382terminal_start({ name: "python" })383terminal_write({ sessionId, data: "python3\r" })384terminal_read({ sessionId }) -> Python banner385terminal_write({ sessionId, data: "2 + 2\r" })386terminal_read({ sessionId }) -> "4"387terminal_send_key({ sessionId, key: "ctrl+d" }) -> exit Python388```389390### Wait for a server to start391392```393terminal_start({ name: "dev-server" })394terminal_write({ sessionId, data: "npm run dev\r" })395terminal_wait({ sessionId, pattern: "listening on port", timeout: 60000 })396terminal_wait({ sessionId, pattern: "listening on port", returnMode: "full" })397```398399### Retry a flaky command400401```402terminal_retry({ sessionId, command: "npm test", maxRetries: 2, backoff: "fixed", delayMs: 1000 })403-> { success: true, attempts: 2, lastResult: { output: "...", exitCode: 0, cwd: "...", timedOut: false } }404```405406### Diff two command outputs407408```409terminal_diff({ sessionId, commandA: "git show HEAD~1:README.md", commandB: "type README.md" })410-> { identical: false, diff: "--- git show HEAD~1:README.md\n+++ type README.md\n@@ @@\n..." }411```412413## Architecture414415```416src/417 index.js Entry point, server bootstrap, graceful shutdown418 tools.js MCP tool registrations with Zod schemas419 command-runner.js One-shot non-interactive command execution (shell=false)420 command-parsers.js Structured parsers for supported read-only commands421 pager.js Line-based pagination helper for large stdout422 pty-session.js PTY session: marker injection, idle read, buffer mgmt423 smart-tools.js Retry and diff helpers for higher-level terminal tools424 regex-utils.js Shared user-regex validation and compilation425 session-manager.js Session lifecycle, TTL cleanup, concurrency limits426 shell-detector.js Cross-platform shell auto-detection427 ansi.js ANSI escape code stripping428```429430### Structured parser support431432`terminal_run` currently parses a small set of read-only command signatures:433434- `git log --oneline`435- `git log --oneline -n <count>`436- `git status --porcelain=v1 --branch`437- `git status --short --branch`438- `git status --short`439- `git branch`440- `git branch --all` / `git branch --remotes`441- `git branch -vv`442- `git branch --show-current`443- `git rev-parse --abbrev-ref HEAD`444- `git rev-parse --show-toplevel`445- `git rev-parse --is-inside-work-tree`446- `git diff --name-only`447- `git diff --name-status`448- `git diff --stat`449- `git diff --shortstat`450- `git ls-files`451- `git remote -v`452- `tasklist /fo csv /nh`453- `where <name>` / `which <name>`454455Set `parseOnly: true` to omit `stdout.raw` when a supported parser succeeds. Unsupported commands still return `stdout.raw`; `stdout.parsed` is `null`.456457Set `summary: true` to return `stdout.summary` and suppress `stdout.raw` for supported command signatures. If no summary is available, raw stdout is preserved.458459`terminal_run_paged` supports `summary: true` for read-only commands: `git` (`branch`, `diff`, `log`, `ls-files`, `remote`, `rev-parse`, `status`), `tasklist`, `where`, and `which`.460461When parsing was requested but no parser matched, `terminal_run` may include a short `hint` for parser-worthy command signatures with larger raw output:462- currently limited to `git` plus `where` / `which`463- only when the command succeeds and `stdout.raw` is large enough to be worth suggesting464- wording: `Structured parser unavailable for this command signature. If you need this often, propose one.`465466## License467468MIT469
Full transparency — inspect the skill content before installing.