enhanced-terminal
A shell command / terminal executor with async support
Enhanced Terminal MCP Server
A standalone Model Context Protocol (MCP) server that provides terminal execution, binary detection, and shell detection capabilities.
Features
Tools
-
enhanced_terminal - Execute shell commands with smart async switching
- Streaming Output: Real-time output notifications in sync mode
- Automatically switches to background after 50 seconds (configurable)
- PTY support with proper terminal emulation
- Configurable working directory, shell, timeout, and token preview limits
- Security denylist blocks dangerous commands
- Returns job ID for tracking background tasks
-
enhanced_terminal_job_status - Get status and output of background jobs
- Check progress of long-running commands
- Retrieve full output when complete
- View exit codes and duration
-
enhanced_terminal_job_list - List all jobs (running and completed)
- See recent command history
- Filter and limit results
- Quick overview of job statuses
-
enhanced_terminal_job_cancel - Cancel running background jobs (Unix only)
- Send SIGTERM to running processes
- Graceful termination of long-running commands
-
enhanced_terminal_job_stdin - Send input to running background jobs
- Write exact UTF-8 text to a job's PTY stdin
- Include
\nininputto submit a line - Useful for prompts after commands switch to background
-
detect_binaries - Detect developer tools with 16 concurrent checks
- Scans PATH for 190+ common development tools across 26 categories
- Fast parallel version detection
- Supports filtering by category (rust_tools, python_tools, etc.)
- Categories include: package managers, build systems, programming language tools, editors, containers, and more
Note: Shell information is automatically detected at server startup and included in the server instructions, so no separate tool call is needed to discover available shells.
Key Features
- Streaming Notifications: Emits MCP logging notifications as command output arrives (client support varies)
- Smart Async Switching: Commands automatically move to background after 50 seconds (configurable)
- Security Denylist: Blocks dangerous commands like
rm -rf /,shutdown, fork bombs, etc. - Job Management: Track, monitor, feed stdin to, and cancel background jobs with rich metadata
- Job Filtering: Filter jobs by status, tags, or working directory
- Output Pagination: Seek into specific byte ranges of very long logs
- Job Tags: Categorize jobs with custom tags for easy filtering
- Call Logging: Appends every
enhanced_terminalshell execution request toenhanced_terminal_calls.jsonl - 16 Concurrent Checks: Fast parallel binary detection
- PTY Support: Full terminal emulation for interactive commands
Installation
Prerequisites
- Rust with 2024 edition support (Rust 1.85+ recommended)
- Cargo
Build from Source
git clone <repository-url>
cd enhanced-terminal-mcp
cargo build --release
The binary will be located at target/release/enhanced-terminal-mcp.
Sudo Workflow (Recommended)
This server handles sudo commands automatically to avoid password prompts during tool execution:
- First sudo command: Triggers an askpass dialog (via
sudo -A -v) to authenticate once - Subsequent sudo commands: Rewritten to
sudo -n(non-interactive) and use the cached sudo timestamp - Keepalive: Background task refreshes the timestamp every 5 minutes to keep it valid
All of this is enabled by default. The sudo_wrapper_applied field in results shows when the -n flag was added.
Recommended Setup: Sudoers Timestamp Sharing
For the best experience, configure sudo to share timestamps across all your sessions (not just per-TTY):
- Create
/etc/sudoers.d/enhanced-terminal-mcpusingvisudo:
sudo visudo -f /etc/sudoers.d/enhanced-terminal-mcp
- Add these lines:
Defaults !tty_tickets
Defaults timestamp_timeout=10
Defaults use_pty
-
Benefits:
- Prime sudo once in any terminal:
sudo -v - The MCP server will reuse that timestamp automatically
- No askpass dialog needed (unless timestamp expires)
- Works across all your terminal sessions and the MCP server
- Prime sudo once in any terminal:
-
Security note:
!tty_ticketsmeans any process running as your user can reuse your sudo timestamp while it's valid. Keeptimestamp_timeoutreasonable (e.g., 10 minutes).
Alternative: Askpass-based Workflow (Default Behavior)
If you prefer not to change sudoers, the server defaults will work:
- Default askpass path:
~/scripts/askpass-zenity.sh - First sudo command → askpass dialog
- Server keeps timestamp alive → no more prompts
The server will automatically pass through these env vars for GUI askpass:
DISPLAY(defaults to:0)WAYLAND_DISPLAY(defaults towayland-0)XDG_RUNTIME_DIRDBUS_SESSION_BUS_ADDRESS
Configuration (Optional)
These environment variables control sudo behavior (all default to ON):
# Enable/disable sudo wrapping and keepalive (default: 1)
ENHANCED_TERMINAL_SUDO_WRAP=1
ENHANCED_TERMINAL_SUDO_KEEPALIVE=1
ENHANCED_TERMINAL_SUDO_KEEPALIVE_PRIME=1
# Custom askpass path (default: ~/scripts/askpass-zenity.sh)
ENHANCED_TERMINAL_SUDO_ASKPASS=/path/to/your/askpass.sh
# Keepalive refresh interval in seconds (default: 300, min: 30)
ENHANCED_TERMINAL_SUDO_KEEPALIVE_REFRESH_SECS=300
Debugging
Enable detailed logging to see sudo priming/wrapping behavior:
RUST_LOG=debug enhanced-terminal-mcp
Look for log lines about sudo -A -v (priming) and sudo -n (wrapping).
Usage
Running the Server
The server uses stdio transport for MCP communication:
./enhanced-terminal-mcp
Configuration
Add to your MCP client configuration:
{
"mcpServers": {
"enhanced-terminal": {
"command": "/path/to/enhanced-terminal-mcp",
"args": []
}
}
}
Call Logging
Every enhanced_terminal tool call is appended as one JSON object per line to enhanced_terminal_calls.jsonl in the repository root. Each entry includes an RFC3339 UTC datetime, the tool name, and the full submitted parameters. Writes use an in-process mutex and, on Unix, an exclusive file lock so concurrent tool calls and test server processes do not interleave JSON records.
Override the log path with ENHANCED_TERMINAL_CALL_LOG_PATH if needed.
Working Directory Defaults
If cwd is omitted, it defaults to .. That . is resolved relative to the MCP server process working directory supplied by the caller/client. In practice, when Codex starts this MCP server from a project, omitted cwd uses that project/server launch directory. Pass cwd explicitly when you need a specific repository or subdirectory.
Tool Examples
enhanced_terminal
Basic synchronous execution (completes quickly). cwd is optional; omitting it uses the MCP server process working directory supplied by the caller/client:
{
"command": "ls -la",
"cwd": ".",
"shell": "bash"
}
Long-running command (auto-switches to background after 50 seconds by default):
{
"command": "npm install",
"cwd": "./my-project",
"shell": "bash"
}
Force immediate async execution (useful for interactive commands that need stdin):
{
"command": "read -p 'stdin> ' value; echo received=$value",
"force_async": true
}
With environment variables:
{
"command": "npm run build",
"env_vars": {
"NODE_ENV": "production",
"API_KEY": "secret123"
}
}
Force synchronous execution (wait for completion):
{
"command": "cargo build --release",
"force_sync": true
}
With custom denylist:
{
"command": "docker run myimage",
"custom_denylist": ["docker rm", "docker system prune"]
}
With tags for job categorization:
{
"command": "cargo build --release",
"tags": ["build", "release"]
}
Token-bounded preview (GPT-5/o200k_base tokenizer):
{
"command": "cargo test",
"preview_tokens": 4000
}
preview_tokens defaults to 4096. Set it to 0 to disable token truncation for the bounded in-memory preview buffer.
Job IDs are readable adjective-noun-number handles such as brave-river-1, making them easier to copy and discuss than numeric IDs.
enhanced_terminal_job_status
Get full output. job_status returns the command summary by default; pass full_command: true only when you need the full command text:
{
"job_id": "brave-river-1",
"incremental": false,
"full_command": true
}
Get incremental output (only new since last check):
{
"job_id": "brave-river-1",
"incremental": true
}
Get paginated output (first 1000 bytes):
{
"job_id": "brave-river-1",
"offset_bytes": 0,
"limit_bytes": 1000
}
Get paginated output (next 1000 bytes):
{
"job_id": "brave-river-1",
"offset_bytes": 1000,
"limit_bytes": 1000
}
enhanced_terminal_job_list
List all jobs:
{
"max_jobs": 50
}
Filter by status:
{
"max_jobs": 50,
"status_filter": ["Running", "Completed"]
}
Filter by tag:
{
"max_jobs": 50,
"tag_filter": "build"
}
Filter by working directory:
{
"max_jobs": 50,
"cwd_filter": "/home/user/project"
}
Combined filters with sort order:
{
"max_jobs": 50,
"status_filter": ["Completed"],
"tag_filter": "test",
"sort_order": "oldest"
}
enhanced_terminal_job_cancel
{
"job_id": "brave-river-1"
}
enhanced_terminal_job_stdin
Write input to a running async job. Newlines are not appended automatically, so include \n when you want to submit a line:
{
"job_id": "brave-river-1",
"input": "yes\n"
}
detect_binaries
{
"filter_categories": ["rust_tools", "python_tools"],
"max_concurrency": 16,
"version_timeout_ms": 1500,
"include_missing": false
}
Binary Categories
The detect_binaries tool supports filtering by these categories:
package_managers- npm, pip, cargo, dnf, apt, snap, flatpak, brew, pnpm, uv, poetry, pipxrust_tools- cargo, rustc, rustfmt, clippy-driverpython_tools- python, python3, pip, pytest, black, ruff, mypy, uv, poetry, pipenv, pipx, pyright, pylint, flake8, isort, ipythonbuild_systems- make, cmake, ninja, gradle, maven, mvnc_cpp_tools- gcc, g++, clang, gdb, lldbjava_jvm_tools- java, javac, javadoc, jar, jarsigner, jconsole, jdeps, jlink, jshell, kotlin, kotlinc, scala, scalac, groovy, groovycmaven_tools- mvn, mvnw, mvndnode_js_tools- node, deno, bun, npm, yarn, pnpm, tsx, tsc, biome, prettier, eslintgo_tools- go, gofmteditors_dev- vim, nvim, emacs, code, hx, nano, microsearch_productivity- rg, fd, fzf, jq, bat, tree, exa, sd, zoxide, lsd, dust, btm, broot, choosesystem_perf- htop, ps, top, df, ducontainers- docker, podman, kubectl, helm, docker-compose, kind, minikube, skopeo, buildah, nerdctl, k9snetworking- curl, wget, dig, traceroute, http, nc, nmap, ss, ping, mtr, socatsecurity- openssl, gpg, ssh-keygen, age, sops, vault, passauth_helpers- zenity, ssh-askpass, sshaskpass, ksshaskpass, lxqt-openssh-askpass, gnome-ssh-askpass, x11-ssh-askpass, pinentry variantsdatabases- sqlite3, psql, mysql, redis-cli, mongosh, duckdb, clickhouse-client, redis-servervcs- git, gh, lazygit, tig, gitui, hg, svncloud_cli- aws, gcloud, az, doctl, fly, vercel, wrangleriac_tools- terraform, tofu, pulumi, ansible, ansible-playbook, vagrant, packermedia_tools- ffmpeg, ffprobe, convert, magick, exiftool, yt-dlp, soxai_ml_tools- ollama, huggingface-cli, nvidia-smi, nvcc, rocm-smi, dvc, mlflowdocs_tools- pandoc, sphinx-build, mkdocs, doxygen, asciidoctor, mdbookruby_tools- ruby, gem, bundle, rake, irb, railsdotnet_tools- dotnet, nuget, msbuildcad_utils- ODAFileConverter, dwg2svg, dwg2SVG, dwg2bmp, dwg2pdf, qcad, librecad, freecad, freecadcmd, openscad, dxf2gcode
Development
Building
cargo build
Testing
cargo test
Running Tests for Denylist
cargo test denylist
Running Locally
cargo run
Security
Command Denylist
The server includes a comprehensive denylist that blocks dangerous commands:
Destructive Operations:
rm -rf /,rm -rf /*,rm --no-preserve-rootmkfs,dd if=/dev/zero, filesystem formatting- Writes to
/dev/sda,/dev/hda
System Manipulation:
shutdown,reboot,halt,poweroffinit 0,init 6, systemctl power commands
Fork Bombs:
:(){:|:&};:and variants
Dangerous Permission Changes:
chmod 777 /,chmod -R 777 /chown -R root,chown root /
Package Manager Risks:
- Force uninstall commands across apt, yum, dnf, pacman
Other Risks:
- Kernel module manipulation
- Cron deletion (
crontab -r) - Moving system directories
Custom Denylist
You can add custom patterns via the custom_denylist parameter:
{
"command": "docker run myimage",
"custom_denylist": ["docker rm -f", "kubectl delete"]
}
Async Threshold
Commands that exceed the server async threshold (default: 50 seconds, configurable with ENHANCED_TERMINAL_ASYNC_THRESHOLD_SECS) automatically switch to background execution. This prevents:
- Long-running commands from blocking the MCP server
- Timeout issues with package installations
- Slow build processes hanging the interface
Set force_sync: true to disable this behavior for specific commands. Set force_async: true to return a job ID immediately without waiting for the threshold, which is the recommended flow before using enhanced_terminal_job_stdin.
Incremental Output
Use enhanced_terminal_job_status with incremental: true for efficient polling of long-running jobs:
- First call returns all output accumulated so far
- Subsequent calls return only new output since last check
- Read position tracked per job_id
- Reset by calling with
incremental: false
This enables streaming-like behavior without actual streaming infrastructure.
Interactive Job Input
Use enhanced_terminal_job_stdin to write to a running job's PTY stdin after it has switched to background. For commands that wait for input, start them with force_async: true so the first call returns a job ID immediately. The stdin tool writes exactly the provided input string and does not append a newline automatically.
Output Pagination
For very long outputs, use pagination mode in enhanced_terminal_job_status:
- Set
offset_bytesto starting byte position - Set
limit_bytesto number of bytes to select (0 = all remaining) - Returns
has_moreflag andtotal_length - Allows seeking into specific segments without retrieving full output
Example workflow:
// Get first 1000 bytes
{"job_id": "brave-river-1", "offset_bytes": 0, "limit_bytes": 1000}
// Get next 1000 bytes
{"job_id": "brave-river-1", "offset_bytes": 1000, "limit_bytes": 1000}
// Get all remaining
{"job_id": "brave-river-1", "offset_bytes": 2000, "limit_bytes": 0}
Job Tags and Filtering
Tag jobs when creating them for easier organization:
{
"command": "cargo test",
"tags": ["test", "ci"]
}
Filter jobs by various criteria in enhanced_terminal_job_list:
- status_filter: Match specific statuses (e.g., ["Running", "Completed"])
- tag_filter: Show only jobs with a specific tag
- cwd_filter: Show only jobs from a specific directory
- sort_order: "newest" (default) or "oldest"
All filters can be combined for powerful queries.
Architecture
This server uses a modular structure with Rust 2024 edition:
src/main.rs- Entry point and server initializationsrc/server.rs- MCP server implementation with tool handlerssrc/detection/- Binary and shell detection logicsrc/tools/- Terminal execution, job management, security denylist
Dependencies
- rmcp 0.8 - Official Rust SDK for Model Context Protocol
- tokio 1.x - Async runtime
- portable-pty 0.8 - Cross-platform PTY support for terminal emulation
- serde/serde_json 1.x - Serialization
- schemars 1.0 - JSON Schema generation for tool inputs
- anyhow 1.x - Error handling
- nix 0.29 - Unix signal handling (Unix only)
- tiktoken-rs 0.11 - GPT-5/o200k_base-compatible token counting for previews
- chrono 0.4 - UTC timestamps for call logging
- tracing/tracing-subscriber 0.1/0.3 - structured server logging
Performance
- 16 concurrent binary checks - Fast parallel tool detection (configurable)
- Smart async switching - Auto-background after 50s (configurable)
- Tokio background monitoring - Jobs continue running after smart async switching
- Incremental output capture - Poll new output with read position tracking; byte pagination is available for long logs
- No timeout by default - Set ENHANCED_TERMINAL_TIMEOUT_SECS environment variable to enable
Configuration
Default Values
- Shell:
bash - Working Directory:
.resolved from the MCP server process working directory supplied by the caller/client - Preview Tokens:
4096GPT-5/o200k_base tokens (0disables token truncation) - Async Threshold:
50seconds (ENHANCED_TERMINAL_ASYNC_THRESHOLD_SECS) - Timeout:
Noneby default (ENHANCED_TERMINAL_TIMEOUT_SECSenables a timeout) - Job IDs: readable
adjective-noun-numberhandles - Call Log: concurrent-safe JSONL at
enhanced_terminal_calls.jsonlin the repo root (ENHANCED_TERMINAL_CALL_LOG_PATHoverrides) - Max Binary Detection Concurrency:
16 - Version Probe Timeout:
1500ms
License
MIT License - see LICENSE file for details
Related Servers
Alpha Vantage MCP Server
sponsorAccess financial market data: realtime & historical stock, ETF, options, forex, crypto, commodities, fundamentals, technical indicators, & more
Chainlink Feeds
Provides real-time access to Chainlink's decentralized on-chain price feeds.
vcpkg Package README MCP Server
Fetch comprehensive information about vcpkg packages, including READMEs, metadata, and search results.
MCP‑Stack
A Docker Compose-based collection of MCP servers for LLM workflows, featuring centralized configuration and management scripts.
Agent Passport System
Cryptographic identity, scoped delegation, values governance, and deliberative consensus for AI agents. 11 tools, Ed25519 signatures, zero blockchain.
Domscribe
Build-time DOM-to-source mapping for coding agents
PromptOT
Manage, version, and publish LLM prompts via PromptOT. Build structured prompts from blocks, add variables, save drafts, publish versions, and deliver via API — all from chat.
Remote MCP Server (Authless)
An example of a remote MCP server deployable on Cloudflare Workers without authentication.
Tatara MCP Server
An MCP server for interacting with the Tatara blockchain ecosystem. Requires configuration for the Tatara RPC endpoint and a wallet private key.
PageSpeed Analyzer MCP
Analyzes website performance, SEO, and accessibility using PageSpeed Insights API.
Tenets
Offline MCP server that ranks & summarizes code using BM25, TF-IDF, embeddings & git signals; integrates with Cursor, Claude Desktop and Windsurf; privacy preserving.