Skip to main content
Glama
borod108

tmux-mcp-claude

by borod108

tmux-mcp-claude

MCP server that gives Claude Code full control over tmux sessions, windows, and panes — plus an optional AI safety classifier that uses Claude Sonnet to gate destructive commands.

What's included

Component

Description

server.mjs

MCP server exposing 11 tmux tools

hooks/notify-attention.sh

Desktop notification when Claude needs input

hooks/tmux-compact-hook.sh

Re-injects tmux workspace state after context compaction

Settings snippets (below)

Sonnet-based safety classifier for tmux_send_keys

Related MCP server: Tmux MCP Server

Quick start

git clone <repo-url> ~/.claude/mcp-servers/tmux-mcp
cd ~/.claude/mcp-servers/tmux-mcp
npm install

Then add the MCP server to your Claude Code settings (~/.claude/settings.json):

{
  "mcpServers": {
    "tmux": {
      "command": "node",
      "args": ["<path-to>/tmux-mcp-claude/server.mjs"]
    }
  }
}

Restart Claude Code — the 11 tmux_* tools will appear automatically.

Tools

Listing

Tool

Description

Parameters

tmux_list_sessions

List all tmux sessions

(none)

tmux_list_windows

List windows in a session

session?

tmux_list_panes

List panes in a window

target?

Sending commands and reading output

Tool

Description

Parameters

tmux_send_keys

Send keys/command to a pane

target, keys, enter? (default: true)

tmux_capture_pane

Capture visible content of a pane

target, start?, end?

Creating windows and panes

Tool

Description

Parameters

tmux_new_window

Create a new window

session?, name?, command?

tmux_split_window

Split a pane

target?, horizontal?, percent?, command?

Navigation and cleanup

Tool

Description

Parameters

tmux_select_window

Switch to a window

target

tmux_select_pane

Switch to a pane

target

tmux_kill_pane

Kill a pane

target

tmux_kill_window

Kill a window

target

Target syntax

Tmux targets follow the format session:window.pane:

  • mysession — the session

  • mysession:0 — window 0

  • mysession:0.1 — pane 1 in window 0

The MCP server itself has no guardrails — it will happily rm -rf / if asked. The safety comes from a PreToolUse hook that intercepts every tmux_send_keys call and uses Claude Sonnet to classify the command before it executes:

  • Safe commands (reads, builds, navigation) → auto-allowed, no prompt

  • Destructive commands (rm, kill, force-push, DROP, etc.) → asks for user permission

Setup

Add these sections to your ~/.claude/settings.json:

1. Auto-allow the tmux tools (so Claude doesn't ask permission for each call)

{
  "permissions": {
    "allow": [
      "mcp__tmux__tmux_list_sessions",
      "mcp__tmux__tmux_list_windows",
      "mcp__tmux__tmux_list_panes",
      "mcp__tmux__tmux_capture_pane",
      "mcp__tmux__tmux_new_window",
      "mcp__tmux__tmux_split_window",
      "mcp__tmux__tmux_select_window",
      "mcp__tmux__tmux_select_pane",
      "mcp__tmux__tmux_kill_pane",
      "mcp__tmux__tmux_kill_window",
      "mcp__tmux__tmux_send_keys"
    ]
  }
}

2. Sonnet safety classifier (PreToolUse hook)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__tmux__tmux_send_keys",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are a security classifier for tmux_send_keys. The tool input is: $ARGUMENTS\n\nExtract the \"keys\" field from tool_input. Classify the command that is about to be sent to a tmux pane.\n\nBLOCK (return permissionDecision: \"ask\") if the command would:\n- Delete files or directories (rm, rmdir, shred)\n- Overwrite or truncate files (>, tee without -a on important files)\n- Kill processes (kill, killall, pkill, xkill)\n- Modify system state dangerously (systemctl stop/disable, shutdown, reboot, halt)\n- Drop databases/tables or delete data (DROP, DELETE without WHERE, TRUNCATE)\n- Force-push, hard-reset, or destructive git ops (git push --force, git reset --hard, git clean -f, git checkout .)\n- Modify permissions broadly (chmod -R 777, chown -R)\n- Send mutating requests to external services (curl -X POST/PUT/DELETE/PATCH, wget --post)\n- Remove packages (dnf remove, apt remove/purge, pip uninstall)\n- Run anything else that is hard to reverse or could cause data loss\n\nALLOW (return permissionDecision: \"allow\") if the command:\n- Only reads data (ls, cat, grep, find, git status/log/diff, kubectl get/describe, oc get)\n- Runs builds or tests (make, go build/test, npm test/build, mvn, gradle)\n- Navigates (cd, pushd, popd)\n- Sets environment variables or exports\n- Echoes/prints output\n- Creates new files/dirs without overwriting (touch, mkdir -p)\n- Installs packages (generally reversible)\n- SSH, scp, rsync (read-like or additive)\n- Any other read-only or easily reversible operation\n\nRespond with a JSON object. For safe commands: {\"hookSpecificOutput\": {\"hookEventName\": \"PreToolUse\", \"permissionDecision\": \"allow\"}}. For destructive commands: {\"hookSpecificOutput\": {\"hookEventName\": \"PreToolUse\", \"permissionDecision\": \"ask\", \"permissionDecisionReason\": \"<brief reason>\"}}.",
            "model": "claude-sonnet-4-6",
            "statusMessage": "Classifying tmux command safety..."
          }
        ]
      }
    ]
  }
}

3. Optional companion hooks

Desktop notification when Claude stops and waits for input:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "<path-to>/tmux-mcp-claude/hooks/notify-attention.sh"
          }
        ]
      }
    ]
  }
}

Re-inject tmux state after context compaction:

{
  "hooks": {
    "PostCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "<path-to>/tmux-mcp-claude/hooks/tmux-compact-hook.sh"
          }
        ]
      }
    ]
  }
}

Security

  • server.mjs uses execFile (no shell) — immune to command injection

  • Arguments passed as arrays, never string-interpolated

  • Only calls tmux subcommands, no arbitrary shell execution

  • The Sonnet safety hook adds a semantic layer on top, catching destructive intent even in complex or piped commands

How it works

Claude Code ──▶ tmux_send_keys("rm -rf /tmp/old")
                       │
                       ▼
              PreToolUse hook fires
                       │
                       ▼
              Sonnet classifies: "destructive — file deletion"
                       │
                       ▼
              User prompted: "Allow rm -rf /tmp/old?"
                       │
                  ┌────┴────┐
                  ▼         ▼
               Allow     Deny
                  │
                  ▼
           tmux send-keys executes

Example workflow

# Claude discovers your tmux sessions
tmux_list_sessions()
tmux_list_windows(session="work")

# Creates a window for a task
tmux_new_window(session="work", name="build")

# Runs a command (auto-allowed by Sonnet — it's a build)
tmux_send_keys(target="work:build", keys="make test")

# Reads the output
tmux_capture_pane(target="work:build")

# Tries something destructive (Sonnet asks permission)
tmux_send_keys(target="work:build", keys="rm -rf ./dist")
# → User prompted before execution

Toggle/disable

Action

How

Disable all hooks

"disableAllHooks": true in settings.json

Re-enable all hooks

Remove/set false "disableAllHooks"

Review hooks in UI

/hooks inside Claude Code

Remove safety hook only

Delete the PreToolUse entry from settings.json

License

MIT

F
license - not found
-
quality - not tested
D
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

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/borod108/tmux-mcp-claude'

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