Skip to main content
Glama
hungtranbkit

Terminal MCP

by hungtranbkit

Terminal MCP

Purpose

Terminal MCP lets an MCP client discover every tmux session on the host, then observe and, when explicitly enabled, send constrained input to whitelisted (or explicitly per-session-granted -- see "Dashboard session grants" below) tmux sessions. It supports local STDIO and a loopback-only Streamable HTTP transport intended to sit behind an authenticated HTTPS tunnel.

Related MCP server: Ryan's Tmux MCP Server

Architecture

Claude / Codex / shell
        |
       tmux
        |
   Terminal MCP
        |
 MCP (STDIO or loopback HTTP)
        |
     ChatGPT

tmux is the source of truth. The server calls tmux with explicit argument arrays through Python subprocess; it never uses shell=True.

Quick start

cd /home/dell/workspace/terminal-mcp
python3 -m venv .venv
.venv/bin/pip install -e '.[test]'
.venv/bin/terminal-mcp

For the remote-capable, loopback-only Streamable HTTP mode:

.venv/bin/terminal-mcp-http
# MCP endpoint: http://127.0.0.1:8766/mcp
# Read-only session dashboard: http://127.0.0.1:8766/dashboard
# Session management table (grants, detach):   http://127.0.0.1:8766/dashboard/sessions
# Liveness/readiness/version/metrics (loopback-only, not tunnel-routed):
#   http://127.0.0.1:8766/health/live
#   http://127.0.0.1:8766/health/ready    -- tmux + every durable SQLite store; 503 if any is broken
#   http://127.0.0.1:8766/health/metrics  -- in-process counters (metrics.py); no external backend involved
#   http://127.0.0.1:8766/version         -- package version + running commit/dirty state

The dashboard lists every real tmux session on the host (not only whitelisted ones -- discovery is not access) and shows sanitized status and recent output for the ones it can actually read, refreshing every five seconds. A session outside the static whitelist is still listed (name/attached/windows/activity only, never content) but shows as restricted until an operator explicitly grants it read (and, separately, input) from the dashboard itself -- see "Dashboard session grants" below. It also has a text input box per readable session: sending from it calls the same guarded terminal_send_text(-shaped) path as the MCP tool, so it is still gated by permissions.terminal_input, input_policy (allowed/denied patterns, current pane command, max length), and is recorded in the same input audit log — the box is simply hidden client-side (and the server still enforces it) when a session doesn't pass those checks. Like the MCP endpoint, the dashboard remains bound to loopback and is intended for use directly on the Dell only; the Secure MCP Tunnel does not publish it as a general-purpose website.

Dashboard session grants

A session outside allowed_session_patterns/input_policy.allowed_session_ patterns is still discoverable everywhere (the dashboard, and terminal_list_sessions for any MCP client) but starts fully restricted: no content, no input. From the dashboard, an operator can explicitly grant it read (its output becomes visible immediately, no restart) and, separately, input (requires read already granted; still gated by the global permissions.terminal_input, input_policy's deny patterns, and the same sensitive-current-command check every other input path uses). An input grant pins the session's tmux identity at grant time and re-verifies it on every send, exactly like a binding — a session recreated under the same name never silently keeps a prior grant; re-grant explicitly to accept the new identity. Both are revocable independently (revoking read also revokes input). Every grant/revoke is audited. This mechanism is dashboard-only: there is no MCP tool to grant or revoke — an MCP client only ever sees the result (a session's read_allowed/read_granted/input_allowed/input_granted fields in terminal_list_sessions), never a way to create one itself.

Every one of these controls -- a lock icon on each session's row/tab, a compact entry point in its own open card, and a bulk-select bar for several at once -- opens the same "Quyền truy cập" modal, exposing just two ideas (Xem output / Gửi prompt) with three one-click presets (Xem + gửi / Chỉ xem / Thu hồi). A newly-discovered session is never hidden, only badged "Chưa cấp quyền" until granted.

Session management screen

/dashboard/sessions (linked from the main dashboard's "⚙ Quản lý" button) is a dedicated, table-shaped view of every real tmux session -- useful once the session count grows past what the sidebar comfortably shows. It reads the exact same /dashboard/api/sessions data and drives the exact same grant-read/grant-input routes as the main page (same "Quyền truy cập" modal, same bulk bar) -- a second view of the same capability, not a new privilege surface. It adds a name filter, a "chỉ hiện session chưa whitelist" toggle, and a detach control per row (the same client-side, localStorage-only concept the main page's tabs already use -- toggling it here is immediately reflected in the main page's tab strip in the same browser, and vice versa, since both read/write the same key).

Password login

The dashboard is also reachable through a second, independent path for whoever cannot complete Cloudflare Access: a local username/password login at https://terminal-login.mesflow.net/login, backed by webauth.py/ webauth_dashboard.py. It is a completely separate mechanism from Cloudflare Access -- neither trusts the other's signal, and a forged Cloudflare header sent to this hostname does nothing. Its own tunnel ingress rule allow-lists only /login, /logout, and /app(?:/.*)? on this hostname; /mcp, /health/*, /version, and /dashboard/* are not reachable through it at all -- the old terminal-dashboard.mesflow.net Cloudflare-Access-gated URL is completely unaffected and still required for that path.

Logging in here never grants a tmux session read/input on its own -- once authenticated, /app is the exact same dashboard (session tabs, detach, grants) reachable at /dashboard, under the exact same allowed_session_patterns/grant/input-policy rules as every other entry point.

The first time the server starts with no local account yet, it creates one (admin) with a strong random password, written once to a mode-600 file next to webauth.db (state directory, e.g. ~/.local/state/terminal-mcp/webauth-bootstrap.txt) -- never logged, never printed, never committed. Logging in with it immediately forces a short in-app password-change form (/app/password) before anything else is reachable; changing it there deletes the bootstrap file automatically and issues a fresh session. To manage accounts from a local shell instead:

terminal-mcp-webauth list-users
terminal-mcp-webauth create-user <username>   # prompts via getpass
terminal-mcp-webauth set-password <username>  # prompts via getpass; invalidates that user's other sessions

Session cookies are HttpOnly/Secure/SameSite=Strict, last 12 hours, and are stored server-side only as a SHA-256 hash. Login and every mutation under /app/api/* require a same-origin Origin/Referer header (the same CSRF posture as the /dashboard/api/* path). Repeated failed logins back off exponentially (capped at 15 minutes), keyed by source IP -- never a lasting lockout an operator would need to manually clear.

The HTTP bind address is deliberately fixed to 127.0.0.1. Do not expose it directly or change it to 0.0.0.0; use an authenticated HTTPS tunnel that maps only its MCP route.

The installed user service can be managed with:

systemctl --user status terminal-mcp-http.service
systemctl --user restart terminal-mcp-http.service

Its unit is ~/.config/systemd/user/terminal-mcp-http.service, runs as the current user, and uses Restart=on-failure. Authentication is intentionally not implemented as an ad-hoc MCP wrapper: it must be enforced by the HTTPS tunnel/identity provider before traffic reaches the loopback endpoint.

For private ChatGPT Developer Mode connectivity, use OpenAI Secure MCP Tunnel. The credential-free deployment runbook and inactive service template are in deploy/secure-tunnel. No Cloudflare/public ingress is required for this mode.

Equivalent module command:

.venv/bin/python -m terminal_mcp.server

Example MCP client command configuration:

{
  "command": "/home/dell/workspace/terminal-mcp/.venv/bin/terminal-mcp",
  "args": [],
  "env": {
    "TERMINAL_MCP_CONFIG": "/home/dell/workspace/terminal-mcp/config.yaml"
  }
}

Config

config.yaml controls read/input permissions, allowed session patterns, and capture limits. Input is disabled by default.

permissions:
  terminal_read: true
  terminal_input: false
allowed_session_patterns:
  - "claude-*"
  - "codex-*"
  - "agent-*"
  - "test-*"
max_capture_lines: 2000
default_tail_lines: 200

Security model

  • Discovery (terminal_list_sessions, the dashboard's session list) shows every real tmux session on the host -- name/attached/windows/created/activity only, tmux metadata, never pane content. Content and input remain denied unless a session's name matches a whitelist pattern, or it has been explicitly granted read/input from the dashboard (see "Dashboard session grants") -- discovery never grants either.

  • Sensitive names containing root, ssh, password, secret, or database require an exact literal whitelist entry, and can never be dashboard-granted either.

  • Output is sanitized for common API keys, bearer/authorization values, passwords, and tokens.

  • There is no shell execution, arbitrary filesystem access, environment disclosure, process killing, scheduler, or autonomous agent.

  • HTTP mode listens only on loopback and cannot change permissions or whitelist through requests.

  • terminal_send_text uses tmux literal mode. terminal_send_keys accepts only a fixed V1 key allowlist.

  • Errors for a denied/ungranted session's content or input reveal nothing beyond that denial (no pane content, no reason tied to its content).

  • Identity model is single-operator by design, not role-based. Cloudflare Access (when configured -- see the tunnel setup) authenticates who is allowed to reach the dashboard/tunnel at all and that identity is recorded in the audit log for every mutation it makes, but every authenticated caller shares the same operator-level capability -- there is no separate viewer/operator/approver permission tier. This is an intentional scope boundary for a personal/small-team single-operator deployment, not an unaddressed gap: adding real RBAC would mean new grant/policy infrastructure this deployment does not need. A future multi-operator deployment should treat this as the first thing to add.

Create an agent tmux session

tmux new -s claude-mesflow
claude
tmux new -s codex-mesflow
codex

Detach with Ctrl-b d and reattach with tmux attach -t SESSION.

Mouse-wheel scrolling in a Codex session

Any session whose name matches codex* gets tmux's per-session mouse option turned on automatically (a host-level ~/.tmux.conf hook, applied the moment the session is created -- see that file's own comments for the full root-cause writeup). This is scoped to Codex sessions only: it is a per-session tmux option, not the server-wide default (which stays off), so a Claude Code session's mouse/scroll behavior is unaffected either way. With it on, tmux's own stock wheel bindings route Codex's scroll into tmux's native copy-mode -- a fixed 5 lines per wheel step, real pager behavior, instead of erratic jumps -- because Codex (unlike Claude Code) runs in the primary screen and does not request its own mouse tracking, so tmux would otherwise never mediate its scrolling at all. Exit copy-mode (return to the live view) by scrolling all the way back down, pressing q, or tmux send-keys -t SESSION -X cancel; while in copy-mode, terminal_send_text/terminal_send_bound correctly refuse input with PANE_IN_COPY_MODE rather than ever falsely reporting a send as confirmed (unchanged from the existing copy-mode guard). A session created before this hook existed (or if the hook is ever disabled) can be opted in by hand: tmux set-option -t SESSION mouse on. Rollback: see ~/.tmux.conf's own comment block.

Known limitation, confirmed against the installed Codex CLI (v0.151.0): tmux copy-mode (above) freezes and scrolls the whole pane and takes keyboard focus away from Codex entirely -- there is no tmux-level way to scroll only an output region while the composer stays live, because tmux copy-mode has no concept of two independent regions within one pane. Codex's own closest native feature, Ctrl+T ("view transcript" -- customizable via /keymap or tui.keymap.global.open_transcript in ~/.codex/config.toml), is a real, useful, separate full-screen pager (its own alt-screen, deterministic ↑/↓/PgUp/PgDn/Home/End scrolling) -- but it is modal: opening it hides the composer, and q/Esc are needed to return to it. Neither tmux copy-mode nor Codex's own transcript pager currently provides Claude Code's exact experience (composer permanently visible and typable while the output above it scrolls independently) -- that is an upstream Codex TUI architecture limitation, not something fixable from tmux config or from terminal-mcp. The dashboard's own per-session view (see "Quick start" above) already provides exactly that split layout -- a persistently scrollable output pane plus a separate, always-focusable input box -- as an existing, no-new-code alternative for anyone who wants it.

Enable terminal input

Edit only the permission in config.yaml:

permissions:
  terminal_read: true
  terminal_input: true

Restart the MCP child process after changing configuration. Enabling input lets the MCP client type into allowed sessions; review this change carefully.

Tools

  • terminal_list_sessions

  • terminal_tail

  • terminal_capture

  • terminal_status

  • terminal_send_text

  • terminal_send_keys

  • terminal_bind

  • terminal_get_binding

  • terminal_list_bindings

  • terminal_unbind

  • terminal_tail_bound

  • terminal_status_bound

  • terminal_send_bound

  • terminal_list_input_audit

  • terminal_input_context

  • supervisor_watch, supervisor_set_verifier_policy, supervisor_unwatch, supervisor_list_watches, supervisor_status, supervisor_list_events, supervisor_ack_event, supervisor_run_once — see "Supervisor Loop v1" below, and "Independent completion verification" for supervisor_set_verifier_policy

Chat ↔ tmux logical binding

A logical binding gives a chat or work context a stable name without relying on an internal ChatGPT conversation ID. Bindings persist in SQLite at ~/.local/state/terminal-mcp/bindings.db (or the path in TERMINAL_MCP_BINDINGS_DB) and resolve to tmux, which remains the source of truth.

Create an agent session:

tmux new -s claude-mesflow
claude

From ChatGPT, bind mesflow-dev to claude-mesflow with terminal_bind, then requests such as “xem agent này đang làm gì”, “đọc 300 dòng cuối”, or “kiểm tra nó đang chờ input không” can use:

terminal_tail_bound(binding="mesflow-dev")
terminal_tail_bound(binding="mesflow-dev", lines=300)
terminal_status_bound(binding="mesflow-dev")

Binding names contain only lowercase letters, digits, -, _, or ., with a maximum of 64 characters. A target must exist and pass the session whitelist; sensitive session names are never bindable. Existing bindings require replace=true for an explicit remap. If a tmux session disappears, its binding is retained and status becomes MISSING.

New bindings use read_enabled=true and input_enabled=false. Bound input is allowed only when both the local global permission and the binding permission are true. Creating a binding never enables global terminal input.

Backup & Restore

All durable state is SQLite, under $XDG_STATE_HOME/terminal-mcp/ (default ~/.local/state/terminal-mcp/) unless a TERMINAL_MCP_*_DB environment variable overrides an individual store's path:

File

Store

Contents

audit.db

AuditStore

Every input attempt (hash/length/redacted preview, never full text)

bindings.db

BindingStore

Chat↔tmux logical bindings and their input_enabled state

grants.db

SessionGrantStore

Dynamic, time-boxed read/input grants outside the static whitelist

leases.db

PaneLeaseStore

Short-lived per-pane send leases used for the submit-guarantee path

supervisor.db

SupervisorStore / SupervisorV2Store

Watch state, policy decisions, and v2 action/approval history (v1 and v2 share one file)

Plus config.yaml (whitelist, permissions, input policy, supervisor config — not itself in a state directory; wherever --config/the default lookup points it).

All five stores are opened with PRAGMA journal_mode=WAL, so a backup taken while the service is running can still capture a mid-write -wal/-shm sidecar file — always copy all three files for a given *.db (the .db plus any .db-wal / .db-shm next to it) together, never the .db alone.

Backup (safe to run live; SQLite's own consistency guarantees make a plain file copy of .db+.db-wal+.db-shm correct, but the built-in .backup command is preferred since it doesn't depend on the sidecar files being copied atomically with the main file):

STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/terminal-mcp"
BACKUP_DIR="/path/to/backups/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$BACKUP_DIR"
for db in audit bindings grants leases supervisor; do
  sqlite3 "$STATE_DIR/$db.db" ".backup '$BACKUP_DIR/$db.db'"
done
cp config.yaml "$BACKUP_DIR/"

Restore (stop the service first — restoring into a live WAL-mode database while the process holds an open connection can be rejected or, in the worst case, silently ignored):

sudo systemctl stop terminal-mcp   # or however the service is run
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/terminal-mcp"
for db in audit bindings grants leases supervisor; do
  rm -f "$STATE_DIR/$db.db" "$STATE_DIR/$db.db-wal" "$STATE_DIR/$db.db-shm"
  cp "/path/to/backups/<timestamp>/$db.db" "$STATE_DIR/$db.db"
done
cp /path/to/backups/<timestamp>/config.yaml ./config.yaml   # review before overwriting a live config
sudo systemctl start terminal-mcp
curl -s http://127.0.0.1:8766/health/ready | python3 -m json.tool   # confirm every store re-opens clean
curl -s http://127.0.0.1:8766/version

Restoring an older grants.db/leases.db can resurrect grants or leases that a newer backup had already revoked/expired; review the restored file's contents (or accept the small window of over-permissiveness until natural expiry) rather than assuming restore always narrows access.

A missing store file is not an error at startup — every store creates its schema on first open — so restoring a subset of files (e.g. audit.db only, after a disk incident that spared the others) is safe.

Known limitations

  • Status detection is heuristic and intentionally returns UNKNOWN when evidence is weak.

  • Only the active pane of each tmux session is inspected.

  • Capture is line-based and capped; it does not stream incremental events.

  • Redaction covers common secret shapes, not full DLP.

  • HTTP mode is local-only until a separately authenticated HTTPS tunnel is configured.

Safe Input

Terminal input is deny-by-default. Setting permissions.terminal_input: true only opens the global gate; a target must also match input_policy.allowed_session_patterns, must not match a denied pattern, and must pass the action and current-command guards. Logical bindings add another independent gate and default to input_enabled: false.

permissions:
  terminal_read: true
  terminal_input: true

input_policy:
  allowed_session_patterns: ["claude-*", "codex-*"]
  denied_session_patterns: ["ssh-*", "prod-shell-*"]
  allow_send_text: true
  max_text_length: 12000

Enable one binding explicitly with terminal_bind(binding="mesflow-dev", session="claude-mesflow", input_enabled=true). Use terminal_input_context to inspect the command and last 20 sanitized lines first. Both terminal_send_text and terminal_send_bound accept dry_run=true; this validates every guard, records DRY_RUN, and sends nothing.

Text is passed to tmux send-keys -l as one literal argument. It is never parsed as a shell command, interpolated into a command string, or executed with shell=True. press_enter=true sends Enter separately. Key input is restricted to the configured allowlist. C-c and C-d require confirm_sensitive=true; unknown keys return KEY_NOT_ALLOWED.

Every successful, blocked, and dry-run input attempt is appended to ~/.local/state/terminal-mcp/audit.db (mode 0600 where supported). The audit stores the full text's SHA-256, length, and a short redacted preview—never the full prompt. terminal_list_input_audit returns sanitized metadata and supports binding/session filters.

Safe Input does not add an arbitrary command or file-read facility. Input cannot bypass the session policy, and panes whose current command is ssh, mysql, psql, sudo, or passwd are denied unless locally allowed. (Supervisor Loop v1, below, is a separate, detection-only facility — it never calls terminal_send_text/terminal_send_keys itself.)

  • tmux sessions must run under the same Unix user as Terminal MCP.

Supervisor Loop v1

What v1 solves: local, automatic detection of a watched session/binding transitioning into a state that needs attention (waiting for input, an error, or a defensible completion signal), persisted as a durable, queryable event — so a human (or a future automation) doesn't have to keep polling by hand.

What v1 deliberately does not do: it never sends text/keys to a watched session, never executes a shell command, and never bypasses terminal_input/input_policy/binding/confirmation/audit — those gates are completely unchanged. It also does not itself wake up or message ChatGPT; see "v2" below for what's still needed for that.

Disabled by default. Enable in config.yaml:

supervisor:
  enabled: true
  poll_interval_seconds: 20   # minimum enforced: 5
  idle_threshold_seconds: 45
  max_iterations: 20          # a watch auto-disables itself after this many polls
  same_failure_limit: 2       # ...or after this many *identical* consecutive errors
  event_retention: 500
  watched_session_patterns: ["claude-*"]   # matched against currently allowed sessions each poll
  watched_bindings: ["mesflow-dev"]        # must already exist via terminal_bind

Restart terminal-mcp-http after changing supervisor.enabled — the background poll thread is only started (as a daemon thread inside that process) when it is true, and only for the HTTP service (not the per-client STDIO server, which would start/stop a loop with every client connection). One loop per process; supervisor_status reports whether it is actually running.

Watches can also be created dynamically at any time via supervisor_watch, independent of the config-seeded patterns/bindings above, and work even with supervisor.enabled: false (only the automatic timer is gated — the tools themselves, including supervisor_run_once for a single manual/deterministic pass, are always available). A watch can never be created for, or continue polling, a session outside the existing whitelist.

State machine. Reuses classify_status() (the same heuristic terminal_status already applies) for RUNNING/IDLE/WAITING_INPUT/ UNKNOWN, and layers two more states on top from explicit evidence only: DONE (an explicit completion marker — never inferred from ordinary silence, which maps to IDLE via idle_threshold_seconds instead) and ERROR (a traceback/fatal/exit-code-style marker). An event is persisted only on a meaningful transition — identical repeated state/output is deduplicated, never re-alerted.

Stop policy. A watch auto-disables itself (an event with event_type: "stalled" is recorded) when either limit is hit, and stays disabled until explicitly resumed (call supervisor_watch again for the same target):

  • same_failure_limit — the same ERROR with unchanged output repeats this many times in a row.

  • max_iterations — a hard poll-count ceiling per watch, regardless of state.

A denied, since-excluded, or vanished session/binding emits event_type: "watch_target_missing" and also auto-disables the watch rather than retrying it.

Event types: state_changed, attention_required (entering WAITING_INPUT), completed (entering DONE), error_detected (entering ERROR), stalled, watch_target_missing.

Event schema (also in terminal_mcp/supervisor.py's EVENT_SCHEMA_VERSION docstring — the stable JSON shape a future v2 webhook forwarder can build against):

{
  "schema_version": 1, "id": 1, "timestamp": "2026-...Z",
  "watch_key": "session:claude-mesflow", "kind": "session", "target": "claude-mesflow",
  "previous_state": "RUNNING", "state": "WAITING_INPUT",
  "event_type": "attention_required",
  "reason": "recent prompt matched ... at bottom offset 0",
  "output_preview": "Do you want to continue? [y/N]",
  "output_hash": "sha256...", "iteration_count": 3,
  "acknowledged_at": null, "metadata": {"source": "manual"}
}

output_preview is redacted (the same redact_text) and truncated before it is ever written to SQLite — never the full/raw pane output.

Persisted in SQLite at ~/.local/state/terminal-mcp/supervisor.db (or TERMINAL_MCP_SUPERVISOR_DB), same pattern as bindings.db/audit.db: a watches table (state/iteration/failure bookkeeping per target) and a supervisor_events table.

The dashboard shows a compact "🛰" badge (hidden entirely when there are zero watches) with per-state counts and unacknowledged events in a small overlay panel; acknowledging an event from there only stamps acknowledged_at in SQLite — it is not a terminal-input path.

Supervisor Loop v2

What v2 solves: when v1 emits an actionable, unacknowledged event (WAITING_INPUT/ERROR), v2 provides a safe, auditable, restart-safe claim → decide → approve → send pipeline that can continue a watched session without a human re-typing "check"/"continue" every time — while still going through the exact same guarded send path as manual input.

What v2 does not build: no ChatGPT/webhook callback exists to invoke, and none is faked here. v2 is the local queue/claim/decide/send contract an external caller (ChatGPT, a script, a human) drives via the supervisor2_* MCP tools — it never invents a way to wake an external agent on its own. It also never adds a second send path: execute_send calls the exact same terminal_send_text/terminal_send_bound methods terminal_send_text/ terminal_input already use, so terminal_input, whitelist, binding input_enabled, input_policy, confirmation, sensitive-target, redaction, audit, and length limits all still apply unchanged.

Policy modes (per watch, via supervisor2_set_policy; default for every watch is observe_only — nothing is ever auto-sent unless a watch is explicitly opted in):

  • observe_only (default) — v2 never offers, claims, or sends anything for this watch; supervisor2_list_actionable_events never returns its events.

  • suggest_only — a decision/prompt can be claimed and submitted, but always needs an explicit supervisor2_review_action(decision="approve") before supervisor2_execute_send will do anything.

  • approved_auto_continue — requires an approved_template string set on the policy. A submitted prompt auto-approves only if it is byte-for-byte equal (after redaction) to that template — no free-form filling, no partial match. This is the whole mechanism that keeps auto-continue inside the scope the watch owner pre-approved; anything else falls back to needing supervisor2_review_action.

Hard stop conditions — any of these halts the action (state blocked or failed) and surfaces the reason rather than guessing: max_auto_actions, wall_clock_timeout_seconds (since the watch's first v2 action), same_prompt_repeat_limit (identical prompt sent too many times in a row), no_progress_limit (output hash unchanged across repeated post-send checks), a stale/expired claim lease, and content screening against ATTENTION_STOP_PATTERNS — password/API-key/credential/token requests, confirmation prompts ("are you sure", "irreversible", "cannot be undone"), and destructive-looking commands (rm -rf, force-push, drop table, sudo, ...) — checked at both claim time (against the triggering output) and decision time (against the proposed prompt and the freshly re-fetched current output). A match blocks the action and the watch's policy (blocked_reason set) so no repeated attempt can slip through.

Idempotency & concurrency. A small SQLite compare-and-swap (UPDATE supervisor_actions SET state=? WHERE id=? AND state=<expected>) is the only concurrency primitive — no external queue/broker. execute_send CASes approved → sent before calling the guarded send, so a retry, duplicate call, or a restart mid-send always finds state != approved and is a safe no-op — it can never send twice. Only one open (non-terminal) action is allowed per watch at a time, so two workers can't double-claim or double-decide the same watch concurrently.

Recovery. Nothing is replayed on restart: an action already in sent or beyond is never re-sent (the CAS guard above), and a claimed/decided action past its 5-minute lease is treated as expired and can be reclaimed rather than resumed blindly.

Reconciliation → DONE. After every supervisor_run_once/poll cycle, v2 checks every observing (post-send) action: if the watch's output hash changed, the action completes and links resulting_event_id; if the watch also reached DONE, the watch's v2 counters (auto_action_count, repeat/ no-progress counters) reset — "the loop stops cleanly at DONE". If output never changes within no_progress_limit checks, the action is blocked instead.

MCP tools: supervisor2_set_policy, supervisor2_get_policy, supervisor2_list_actionable_events, supervisor2_claim_event, supervisor2_submit_decision, supervisor2_review_action, supervisor2_execute_send, supervisor2_list_actions. Persisted in the same supervisor.db as v1, in two new tables: supervisor_policies (one row per watch opted into v2) and supervisor_actions (the full claim → decision → approval → send → outcome record per action, linking back to the triggering supervisor_events.id). Never stores secrets or raw unredacted output — prompts are redacted before storage and before send, and send_result only ever holds terminal_send_text/terminal_send_bound's own return value (a character count, never the text itself).

Dashboard. The existing 🛰 Supervisor overlay gained a compact per-watch v2 section (policy badge, auto-action count, latest action's state/blocked reason/send result, and a one-click "Pause (observe only)" button) — it does not touch or resize the main terminal viewer.

Still fully manual/opt-in end to end: a fresh install defaults every watch to observe_only, and even approved_auto_continue only ever sends the one exact template a human configured for that watch.

Independent completion verification

For a watch under approved_auto_continue policy (with v2's global supervisor.v2_enabled also on — both gates, same as execute_send requires), prose/marker "done" evidence alone is not sufficient to reach VERIFIED_DONE and reset the auto-continue chain: quiet-window/nonce evidence that would promote any other watch instead moves this one through a new VERIFYING state while a real, independent verifier runs outside the target pane. Every other watch (the default) is completely unaffected — unchanged, direct promotion, exactly as described above.

Configure the verifier once per watch with supervisor_set_verifier_policy:

supervisor_set_verifier_policy(
  session="claude-mesflow",
  worktree="/home/you/project",      # real subprocess cwd, `git -C` target
  require_git_clean=true,            # fail if `git status --porcelain` is non-empty
  require_commit_matches="<sha>",    # optional: pin to a specific commit
  test_command=["pytest", "-q"],     # a literal argv list -- never a shell string
  timeout_seconds=300,
)

Only git rev-parse/git status/git diff --stat (read-only) and, if configured, that one fixed test_command ever run — always subprocess.run(..., shell=False), always a fixed argument list, never anything parsed out of what the watched pane printed. An autonomous watch with no verifier policy configured goes to BLOCKED rather than ever reaching VERIFIED_DONE on prose alone — this is the actual enforcement of "independent verification required", not an oversight to work around.

New states: VERIFYING (a real verifier run in progress — durable, survives a process restart mid-run, the next poll safely re-verifies), FAILED (the verifier ran and rejected the claim -- e.g. a failing test, a dirty worktree, a commit mismatch), BLOCKED (autonomous, but no verifier configured, or one that couldn't even run). Both FAILED and BLOCKED disable the watch (no repeated re-verification against unchanged pane output) and set the v2 policy's blocked_reason, so no further autonomous send happens until an operator fixes the underlying issue and explicitly supervisor_watchs the target again.

terminal-mcp

Available Tools

13 tools
terminal_bindC

Persist a logical binding to an existing, allowed tmux session.

ParametersJSON Schema
NameRequiredDescriptionDefault
bindingYes
replaceNo
sessionYes
read_enabledNo
input_enabledNo

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It does reveal that the tool creates a persistent side effect and restricts binding to existing/allowed sessions, but it does not explain what happens on conflict, whether 'replace' is needed, how read/input flags affect behavior, or what errors may occur. This is insufficient for a state-changing operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no wasted words, and the main action is front-loaded with 'Persist'. It is appropriately compact, though it achieves concision at the cost of explanatory depth.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 5 parameters, no output schema, no annotations, and persistent side effects, this description is too thin. It omits crucial guidance about parameter semantics, lifecycle behavior, error conditions, and return values, so an agent cannot reliably invoke it correctly without further investigation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, but it only vaguely maps to 'binding' and 'session'. It adds the constraint that the session must be existing and allowed, but it does not explain 'replace', 'read_enabled', 'input_enabled', or how these interact. Most parameter semantics remain unexplained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Persist a logical binding') and a resource ('existing, allowed tmux session'), distinguishing it from siblings like terminal_unbind or terminal_list_bindings. However, the meaning of 'logical binding' is not elaborated, leaving some ambiguity about what exactly is being bound.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool—when you want to persist a logical binding to a tmux session—but it provides no explicit guidance about when not to use it, nor does it mention alternatives such as terminal_send_bound or terminal_tail_bound. The agent must infer usage context from the sibling list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_captureB

Return a larger sanitized scrollback capture, capped by configuration.

ParametersJSON Schema
NameRequiredDescriptionDefault
sessionYes
start_lineNo

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations, so the description carries the full burden. It does disclose useful behavioral traits: the capture is sanitized and capped by configuration, and 'Return' suggests a read-only operation. But it omits details about side effects, authentication needs, or what 'sanitized' actually covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with no filler. The key qualifiers ('larger', 'sanitized', 'capped by configuration') are front-loaded and every word contributes to the meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With two parameters, no annotations, no output schema, and a description that ignores parameter semantics, this is too thin. The description leaves start_line semantics, session format, the configuration cap, and the return shape unexplained, and it doesn't clearly position the tool among its siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description adds no meaningful detail about the 'session' or 'start_line' parameters. The parameter names are somewhat self-explanatory, but the description does not explain how start_line affects the capture or what values are valid, so it fails to compensate for the schema's lack of descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Return') and a clearly scoped resource ('sanitized scrollback capture'). The qualifiers 'larger' and 'capped by configuration' add useful distinguishing detail, but it doesn't explicitly reference a sibling alternative, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The word 'larger' implies a comparison to some smaller/default capture and gives a mild hint about when this tool is appropriate. However, the description never explicitly states when to prefer terminal_capture over terminal_tail or other siblings, nor does it give any exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_get_bindingC

Return binding metadata and current effective permissions.

ParametersJSON Schema
NameRequiredDescriptionDefault
bindingYes

TDQS

C2.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It does convey that the operation is a non-mutating 'Return' and that it reveals 'current effective permissions,' which is useful context beyond the bare tool name. However, it omits prerequisites, error behavior, and what exactly 'metadata' includes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no filler or redundancy. Every word contributes to the core meaning, though its brevity leaves some contextual gaps.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has no output schema and no annotations, so the description must compensate for that missing context, but it only states the return surface ('binding metadata and current effective permissions'). It does not explain how the binding argument should be specified, what the returned metadata structure is, or how 'effective permissions' are represented, leaving the description incomplete for reliable invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, 'binding', has 0% schema description coverage, and the tool description never mentions the parameter or its expected format. An agent receives no help understanding what value to provide for 'binding' beyond the schema's type and title.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Return') and a clear object ('binding metadata and current effective permissions'), so an agent can tell this is a read operation for a single binding's details. It does not explicitly contrast with terminal_list_bindings, but the singular 'binding' plus the 'get' naming implies a targeted lookup rather than a list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided about when to use this tool versus siblings like terminal_list_bindings, terminal_bind, or terminal_unbind. The description only states what the tool does, not when it should be selected or when an alternative would be more appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_list_bindingsB

List persistent logical bindings and current session state.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It only says 'List', implying read-only, but does not explain whether it shows both persistent bindings and transient session state, whether authentication is required, or whether any side effects occur. The two-part output is a hint but is not elaborated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single eight-word sentence with no filler and a front-loaded verb. Every word earns its place, though the meaning is slightly under-specified for a tool family with many closely related siblings.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With zero parameters and an output schema present, the description covers the core invocation: calling the tool requires no arguments. However, without annotations or context clarifying what 'current session state' includes and how it differs from terminal_status, the entry is not fully complete for an agent choosing among many terminal tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

This tool has zero parameters and an empty input schema, so there are no parameter semantics to describe. The baseline of 4 for zero-parameter tools applies; the description correctly avoids inventing parameter details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') and resource ('persistent logical bindings' and 'current session state'), clearly indicating a read operation. It is not a tautology and distinguishes from tools like terminal_get_binding and terminal_bind, though it does not explicitly contrast with terminal_status or terminal_list_sessions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus siblings such as terminal_list_sessions or terminal_status. There is no mention of use cases, prerequisites, or when not to use it, leaving the agent to infer context from the tool name and sibling list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_list_sessionsA

List whitelisted tmux sessions without exposing denied session details.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the burden. It discloses important behavioral constraints: only whitelisted sessions are returned and denied session details are hidden, which is non-obvious and safety-relevant. It doesn't mention output format or error behavior, but for a read-only list tool this is adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with no filler; the key behavioral scope is front-loaded. Every word contributes meaning, making it exceptionally efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only list operation the description is mostly complete: it defines the result set (whitelisted sessions) and a privacy guarantee. Lacking an output schema, it doesn't describe the per-session fields returned, which is a minor gap, but not enough to penalize heavily.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, and the schema's 100% coverage makes further elaboration unnecessary. Baseline for zero-param tools is 4; description doesn't need to add anything.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('List') and resource ('whitelisted tmux sessions'), and adds a scope limiter ('without exposing denied session details'). This distinguishes it from generic listing tools and sibling tools like terminal_status or terminal_capture.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies its use for enumerating sessions but gives no explicit when-to-use vs alternatives. With siblings like terminal_status or terminal_list_bindings, an agent could benefit from a hint about when to prefer this tool, but the purpose is clear enough to infer.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_send_boundB

Send literal text only when global and binding input are enabled.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
bindingYes
press_enterNo

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. It usefully discloses that sending only works when global and binding input are enabled and that input is literal. However, it does not explain what happens if those inputs are disabled, whether press_enter is applied, or any side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single tight sentence that front-loads the core action, covers the literal-text behavior, and states the enabling condition. There is no redundant wording.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with three parameters, no annotations, and no output schema, this description is too thin. It omits the role of 'binding', the press_enter option, the behavior when the enabling condition is false, and any return value or confirmation information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all three parameters. It clarifies that 'text' is literal and suggests 'binding' relates to the binding-enabled condition, but it gives no meaningful explanation of 'binding' as a parameter and completely omits 'press_enter'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action and resource: 'Send literal text' subject to a clear condition ('only when global and binding input are enabled'). It distinguishes the tool from key-sequence senders by emphasizing 'literal text', though it does not explicitly name sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'only when global and binding input are enabled' provides a clear precondition, and 'literal text' hints at when this tool is appropriate. However, it does not explicitly contrast with terminal_send_text or terminal_send_keys, nor does it give when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_send_keysB

Send only allowlisted tmux keys when terminal_input is enabled in local config.

ParametersJSON Schema
NameRequiredDescriptionDefault
keysYes
sessionYes

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the behavioral burden. It discloses the allowlist restriction and the enablement gate, which are meaningful behavioral constraints. However, it does not say what happens if a key is not allowlisted or if terminal_input is disabled, nor any side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no unnecessary words. It communicates the essential constraint and condition without fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with zero annotations, no output schema, and no parameter explanations, the description leaves important gaps: how to identify allowlisted keys, what happens when terminal_input is disabled, and expected return behavior. The condition provided is useful but not sufficient for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for the schema's bare parameter names. It gives a hint that 'keys' are tmux keys, but it does not explain allowed key formats, what 'session' refers to, or how the allowlist is determined.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Send'), the object ('tmux keys'), and the key scope ('only allowlisted'), plus a condition. This distinguishes it from sibling tools like terminal_send_text, though it does not explicitly name the alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a prerequisite ('when terminal_input is enabled in local config') but gives no guidance on when to choose this tool over siblings such as terminal_send_text or terminal_send_bound. No alternatives or exclusions are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_send_textB

Send literal text only when terminal_input is enabled in local config.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
sessionYes
press_enterNo

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It does disclose two key traits: it sends literal text (not interpreted sequences) and requires terminal_input to be enabled locally. However, it does not explain what happens when the config is disabled, whether press_enter affects the sent text, or whether this is a mutating action with side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The entire definition is one sentence with zero filler. It front-loads the action ('Send literal text') and then states the key precondition. Every word adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that there is no output schema, no annotations, and 3 parameters, the description is too minimal. It does not tell the agent what a successful call returns, how press_enter behaves, what session refers to, or what error occurs when terminal_input is disabled. This is insufficient for an agent to invoke the tool confidently in diverse scenarios.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for missing parameter docs. The phrase 'literal text' clarifies the text parameter. But session and press_enter remain entirely unexplained, leaving the agent to guess at their meaning and usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Send') and resource ('literal text' to a terminal), and adds the qualifier 'only when terminal_input is enabled.' This makes the tool's purpose clear. However, it does not explicitly distinguish it from the sibling terminal_send_keys, so the differentiation is only implied through the word 'literal.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear precondition: use this tool only when terminal_input is enabled in local config. It does not mention when not to use it or name alternatives like terminal_send_keys, so an agent has to infer when the other tool would be more appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_statusC

Classify an allowed tmux session with an explicit heuristic reason.

ParametersJSON Schema
NameRequiredDescriptionDefault
sessionYes

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral disclosure burden. It mentions an 'explicit heuristic reason' as an output trait, but it does not state whether the tool reads only, what side effects it may have, what the classification categories are, or how it handles disallowed sessions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single compact sentence with no redundant filler. It front-loads the action and resource, though some terms like 'allowed' and 'explicit heuristic reason' could be clearer.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description must fully explain the operation and its result. It gives only a high-level notion of classification, leaving out the return format, possible classification values, and input requirements. An agent could infer intent but would struggle to call it correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description needs to compensate. It identifies the parameter as a tmux session, but it does not clarify whether the session value should be a name, an ID, a path, or an index, nor does it explain the 'allowed' constraint.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description does state a verb ('classify') and a resource ('an allowed tmux session'), so the basic purpose is present. However, 'allowed' is undefined, 'classify' is vague about what dimensions are being classified, and the description does not distinguish terminal_status from its sibling terminal_status_bound.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance about when to use this tool versus alternatives such as terminal_status_bound or terminal_list_sessions. The phrase 'an allowed tmux session' implies some restriction, but it does not explain what makes a session allowed or how this relates to sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_status_boundC

Classify the tmux session resolved by a logical binding.

ParametersJSON Schema
NameRequiredDescriptionDefault
bindingYes

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure, but it only says 'classify.' It does not state whether the operation is read-only, whether it can modify session state, how invalid or missing bindings are handled, or what side effects might occur. This is insufficient for an agent to predict the tool's behavior safely.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no filler words, and the key idea of 'logical binding' is front-loaded. However, the brevity comes at the cost of necessary detail, so it earns high marks for conciseness but not a perfect score.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no output schema, the description should explain what 'classify' returns or what status categories exist, but it does not. It also lacks mention of error cases, resolution behavior, or how this compares with sibling tools. The tool is simple (one parameter), but the description still leaves critical operational gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides only the parameter name 'binding' with no description, and the description also fails to explain what forms a logical binding can take, how it should be formatted, or how it resolves to a session. The phrase 'logical binding' adds minimal context but does not compensate for 0% schema description coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource (tmux session) and a distinguishing mode (resolved by a logical binding), which helps separate it from plain terminal_status. However, the verb 'classify' is vague and never explains what classification is being performed or what the outcome looks like. It is better than a tautology but leaves the core action ambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'resolved by a logical binding' implies this tool is for cases where a binding, rather than a direct session reference, is available. However, there is no explicit guidance about when to choose terminal_status_bound over terminal_status, terminal_get_binding, or terminal_tail_bound, and no exclusions are stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_tailC

Return sanitized recent output from an allowed tmux session.

ParametersJSON Schema
NameRequiredDescriptionDefault
linesNo
sessionYes

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It discloses that output is sanitized and limited to allowed sessions, which is useful, but it does not explain what sanitization removes, what happens for disallowed sessions, or whether this is a pure read operation beyond the word 'Return'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no unnecessary words, and the purpose is front-loaded. It sacrifices detail for brevity, but as a concise statement of the tool's core action it is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and no output schema, the description is too thin. It omits the meaning of `lines`, the behavior of sanitization, how to identify an allowed session, and how this differs from sibling tools. An agent would struggle to invoke it correctly with only this description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description needed to compensate. It relates `session` to a tmux session and implies recency, but it does not explain the `lines` parameter, the default value of 200, or any constraints. The description adds little beyond what the parameter names already communicate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Return') and identifies a distinct resource: sanitized recent output from an allowed tmux session. It distinguishes the tool from a raw capture at a basic level, but it does not explicitly contrast it with siblings like terminal_capture or terminal_tail_bound.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool instead of terminal_capture, terminal_status, or terminal_tail_bound. The description implies a use case (viewing recent output) but does not state exclusions or alternatives, so an agent would have to infer the appropriate selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_tail_boundC

Return sanitized output after resolving a logical binding.

ParametersJSON Schema
NameRequiredDescriptionDefault
linesNo
bindingYes

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It only reveals that the output is sanitized, with no mention of whether resolution can fail, what sanitization strips, whether the operation has side effects, or what happens when the binding does not exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no filler, and the main verb and object are front-loaded. It loses points only because the unexplained term 'logical binding' makes the sentence less informative than it appears.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter tool with no annotations and no output schema, the description is under-specified. It does not explain the meaning of 'lines', the nature of the binding, the returned output format, or how this tool relates to terminal_tail and terminal_get_binding — all of which an agent would need to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It clarifies that 'binding' is resolved logically, but it says nothing about the 'lines' parameter — not even that it controls the number of output lines — and does not specify the binding's format or namespace.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies a specific operation — returning sanitized output after resolving a binding — but relies on the unexplained term 'logical binding' and does not distinguish this tool from siblings like terminal_tail or terminal_get_binding. It is not a tautology, but the purpose is too vague for reliable selection.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is given for when to use this tool versus alternatives. The name hints that it tails output for a bound target, but the description never states a use case, precondition, or exclusion, leaving the agent to infer the appropriate context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_unbindA

Delete a logical binding without changing its tmux session.

ParametersJSON Schema
NameRequiredDescriptionDefault
bindingYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations, so the description carries the full burden. It discloses one meaningful behavioral trait: the deletion does not change the tmux session, which is valuable context. However, it remains silent on side effects, whether the deletion is reversible, idempotency, or what happens to active bound commands.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no filler words. It delivers the core action, target resource, and an important qualifier in minimal space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and no output schema, the one-line description is too sparse. It does not define what constitutes a logical binding, where to obtain one, whether deletion is permanent, or how the tool interacts with related bind/unbind siblings. An agent is left to infer too much from the name and schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has only a required 'binding' string with no description, and the description does not explain the parameter beyond calling it a 'logical binding'. It adds a small amount of meaning by labeling the binding as logical rather than a tmux construct, but offers no format, examples, or guidance on how to specify the binding value, leaving a 0% schema-coverage gap largely uncompensated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Delete a logical binding'. It also adds a distinctive scope condition, 'without changing its tmux session', which separates it from direct tmux-level unbinding operations and aligns with the delete/inverse counterpart of terminal_bind.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool should be used when a logical binding needs to be removed, but it does not explicitly mention alternatives or when-not-to-use conditions. Sibling tools like terminal_bind and terminal_get_binding are present but not referenced, so an agent must infer the appropriate context from the name and one-line description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 13 tool updatesv0.4.0
    • First observedterminal_bind
    • First observedterminal_capture
    • First observedterminal_get_binding
    • First observedterminal_list_bindings
    • First observedterminal_list_sessions
    • First observedterminal_send_bound
    • First observedterminal_send_keys
    • First observedterminal_send_text
    • First observedterminal_status
    • First observedterminal_status_bound
    • First observedterminal_tail
    • First observedterminal_tail_bound
    • First observedterminal_unbind

TDQS

B3.2/5.0
Disambiguation4/5

Tools are mostly distinct, with a clear split between direct session operations and logical binding operations. The only mild ambiguity is between terminal_tail and terminal_capture, but their descriptions differentiate recent output from larger scrollback.

Naming Consistency4/5

All tools share a terminal_ prefix and use snake_case, with a consistent _bound suffix for binding-resolved variants. Minor inconsistencies exist, such as get_binding vs list_bindings and send_bound omitting the _text qualifier used by send_text, but the overall pattern is predictable.

Tool Count5/5

Thirteen tools is well-scoped for a terminal/tmux MCP server. The surface is organized into two coherent families—session inspection/control and persistent bindings—without obvious redundancy or bloat.

Completeness4/5

The set covers core listing, reading, status, input, and binding lifecycle operations. There are minor gaps such as no bound capture variant and no explicit binding update, but most expected workflows for a restricted terminal interface are supported.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    MCP server for SSH and local terminal access. Supports interactive commands, long-running processes, and TUI apps like tmux/zellij
    6
    3
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    A persistent, stateful MCP server that exposes a detached tmux session to clients, enabling shell command execution, terminal buffer reading, and control signal sending via JSON-RPC over stdio.
    3
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Exposes a Windows or Linux host terminal to remote MCP clients via Streamable HTTP, enabling command execution, tunnel management, and privileged operations with security features like OAuth and audit logging.
    39
    2
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hungtranbkit/terminal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server