Skip to main content
Glama
borod108

tmux-mcp-claude

by borod108
README.md
# tmux-mcp-claude

MCP server that gives [Claude Code](https://docs.anthropic.com/en/docs/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` |

## Quick start

```bash
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`):

```jsonc
{
  "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

## Safety classifier hook (optional but recommended)

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)

```jsonc
{
  "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)

```jsonc
{
  "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:**

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

**Re-inject tmux state after context compaction:**

```jsonc
{
  "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

TDQS

A3.7/5.0

Scored across 11 tools

Disambiguation5/5

Each tool clearly targets a distinct tmux resource and action: sessions, windows, panes with list, create, select, kill, send, capture. There is no overlap in purpose, and descriptions reinforce the boundaries.

Naming Consistency5/5

All tools follow the exact `tmux_verb_noun` pattern, with verbs like list, send, capture, new, split, select, kill. The naming is perfectly uniform and predictable.

Tool Count5/5

With 11 tools, the server covers the core tmux operations without bloat. Every tool handles a distinct operation, making the set feel well-scoped and purposeful.

Completeness4/5

The set covers listing sessions/windows/panes, creating windows, splitting panes, sending keys, capturing output, selecting windows/panes, and killing panes/windows. Missing session-level operations like kill session or rename are minor gaps that can be worked around via existing tools.

Maintenance

ActivityInactive
ResponsivenessNo issues