mcp-tmux 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. mcp_tmux-0.2.0/.github/FUNDING.yml +11 -0
  2. mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
  3. mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  4. mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  5. mcp_tmux-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  6. mcp_tmux-0.2.0/.github/workflows/ci.yml +38 -0
  7. mcp_tmux-0.2.0/.github/workflows/release.yml +45 -0
  8. mcp_tmux-0.2.0/.gitignore +19 -0
  9. mcp_tmux-0.2.0/CONTRIBUTING.md +59 -0
  10. mcp_tmux-0.2.0/LICENSE +21 -0
  11. mcp_tmux-0.2.0/PKG-INFO +309 -0
  12. mcp_tmux-0.2.0/README.md +267 -0
  13. mcp_tmux-0.2.0/TODO.md +445 -0
  14. mcp_tmux-0.2.0/pyproject.toml +58 -0
  15. mcp_tmux-0.2.0/scripts/install.sh +31 -0
  16. mcp_tmux-0.2.0/scripts/isolated_config.toml +5 -0
  17. mcp_tmux-0.2.0/scripts/try_client.py +56 -0
  18. mcp_tmux-0.2.0/scripts/uninstall.sh +20 -0
  19. mcp_tmux-0.2.0/src/mcp_tmux/__init__.py +3 -0
  20. mcp_tmux-0.2.0/src/mcp_tmux/__main__.py +55 -0
  21. mcp_tmux-0.2.0/src/mcp_tmux/capabilities.py +81 -0
  22. mcp_tmux-0.2.0/src/mcp_tmux/config.py +80 -0
  23. mcp_tmux-0.2.0/src/mcp_tmux/control.py +492 -0
  24. mcp_tmux-0.2.0/src/mcp_tmux/formats.py +117 -0
  25. mcp_tmux-0.2.0/src/mcp_tmux/register.py +49 -0
  26. mcp_tmux-0.2.0/src/mcp_tmux/resources.py +57 -0
  27. mcp_tmux-0.2.0/src/mcp_tmux/runner.py +152 -0
  28. mcp_tmux-0.2.0/src/mcp_tmux/server.py +52 -0
  29. mcp_tmux-0.2.0/src/mcp_tmux/targets.py +104 -0
  30. mcp_tmux-0.2.0/src/mcp_tmux/tools/__init__.py +47 -0
  31. mcp_tmux-0.2.0/src/mcp_tmux/tools/_capture.py +40 -0
  32. mcp_tmux-0.2.0/src/mcp_tmux/tools/_util.py +113 -0
  33. mcp_tmux-0.2.0/src/mcp_tmux/tools/clients.py +56 -0
  34. mcp_tmux-0.2.0/src/mcp_tmux/tools/copymode.py +107 -0
  35. mcp_tmux-0.2.0/src/mcp_tmux/tools/environment.py +98 -0
  36. mcp_tmux-0.2.0/src/mcp_tmux/tools/hooks.py +113 -0
  37. mcp_tmux-0.2.0/src/mcp_tmux/tools/io.py +90 -0
  38. mcp_tmux-0.2.0/src/mcp_tmux/tools/keys.py +83 -0
  39. mcp_tmux-0.2.0/src/mcp_tmux/tools/merged.py +185 -0
  40. mcp_tmux-0.2.0/src/mcp_tmux/tools/options.py +142 -0
  41. mcp_tmux-0.2.0/src/mcp_tmux/tools/panes.py +137 -0
  42. mcp_tmux-0.2.0/src/mcp_tmux/tools/passthrough.py +76 -0
  43. mcp_tmux-0.2.0/src/mcp_tmux/tools/plumbing.py +146 -0
  44. mcp_tmux-0.2.0/src/mcp_tmux/tools/sessions.py +89 -0
  45. mcp_tmux-0.2.0/src/mcp_tmux/tools/stream.py +124 -0
  46. mcp_tmux-0.2.0/src/mcp_tmux/tools/wait.py +173 -0
  47. mcp_tmux-0.2.0/src/mcp_tmux/tools/windows.py +63 -0
  48. mcp_tmux-0.2.0/src/mcp_tmux/toolsets.py +168 -0
  49. mcp_tmux-0.2.0/tests/test_annotations.py +57 -0
  50. mcp_tmux-0.2.0/tests/test_capabilities.py +34 -0
  51. mcp_tmux-0.2.0/tests/test_control.py +306 -0
  52. mcp_tmux-0.2.0/tests/test_formats.py +81 -0
  53. mcp_tmux-0.2.0/tests/test_functional.py +204 -0
  54. mcp_tmux-0.2.0/tests/test_integration.py +176 -0
  55. mcp_tmux-0.2.0/tests/test_p2.py +175 -0
  56. mcp_tmux-0.2.0/tests/test_p5.py +346 -0
  57. mcp_tmux-0.2.0/tests/test_runner.py +40 -0
  58. mcp_tmux-0.2.0/tests/test_targets.py +67 -0
  59. mcp_tmux-0.2.0/tests/test_tools_argv.py +965 -0
  60. mcp_tmux-0.2.0/tests/test_toolsets.py +80 -0
  61. mcp_tmux-0.2.0/tests/test_wait.py +51 -0
  62. mcp_tmux-0.2.0/uv.lock +1274 -0
@@ -0,0 +1,11 @@
1
+ # GitHub Sponsors / funding configuration.
2
+ # This enables the "Sponsor" button on the repository.
3
+ # Sponsorship tiers (and their perks) are configured in the GitHub Sponsors
4
+ # dashboard, not in this file.
5
+
6
+ github: [laszlopere]
7
+
8
+ # Other funding platforms are also supported, e.g.:
9
+ # ko_fi: your_kofi_name
10
+ # liberapay: your_liberapay_name
11
+ # custom: ["https://example.com/donate"]
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report something that doesn't work as expected
4
+ title: ""
5
+ labels: bug
6
+ assignees: ""
7
+ ---
8
+
9
+ **What happened**
10
+ A clear description of the bug.
11
+
12
+ **What you expected**
13
+ What you expected to happen instead.
14
+
15
+ **Reproduction**
16
+ Steps or a minimal tool call / command sequence that triggers it.
17
+
18
+ **Environment**
19
+ - mcp-tmux version: <!-- `mcp-tmux --version` or the installed version -->
20
+ - Python version: <!-- `python --version` -->
21
+ - tmux version (server host): <!-- `tmux -V` -->
22
+ - Target: <!-- local / `user@host` / named profile -->
23
+ - OS: <!-- e.g. Ubuntu 24.04 -->
24
+
25
+ **Logs / output**
26
+ Any relevant error output (please redact secrets).
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an improvement or a new capability
4
+ title: ""
5
+ labels: enhancement
6
+ assignees: ""
7
+ ---
8
+
9
+ **The problem**
10
+ What are you trying to do that's hard or impossible today?
11
+
12
+ **Proposed solution**
13
+ What you'd like to see — a new tool, a flag, a behavior change, etc.
14
+
15
+ **Alternatives considered**
16
+ Other approaches you've thought about, including raw `tmux_command` workarounds.
17
+
18
+ **Additional context**
19
+ Anything else relevant (tmux version, target setup, links).
@@ -0,0 +1,14 @@
1
+ <!-- Thanks for contributing to mcp-tmux! -->
2
+
3
+ **What this changes**
4
+ A short description of the change and why.
5
+
6
+ **Related issues**
7
+ Closes #<!-- issue number -->, if applicable.
8
+
9
+ **Checklist**
10
+ - [ ] `ruff check .` and `ruff format --check .` pass
11
+ - [ ] `mypy` passes
12
+ - [ ] `pytest` passes
13
+ - [ ] Docs/README updated if behavior or tools changed
14
+ - [ ] New tools/flags work against the supported tmux range (1.8+) where relevant
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v7
21
+
22
+ - name: Install the project
23
+ # Let uv manage a project venv; installing into the runner's system
24
+ # Python fails under PEP 668 (externally managed).
25
+ run: uv sync --all-extras --python ${{ matrix.python-version }}
26
+
27
+ - name: Lint (ruff)
28
+ run: uv run ruff check .
29
+
30
+ - name: Format check (ruff)
31
+ run: uv run ruff format --check .
32
+
33
+ - name: Type-check (mypy)
34
+ run: uv run mypy
35
+
36
+ - name: Test (pytest)
37
+ # tmux is present on ubuntu-latest, so integration tests run too.
38
+ run: uv run pytest -v
@@ -0,0 +1,45 @@
1
+ name: Release
2
+
3
+ # Publishes to PyPI via Trusted Publishing (OIDC) on a version tag, e.g. v0.2.0.
4
+ # No API tokens are stored: PyPI verifies the GitHub OIDC identity instead.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v7
17
+
18
+ - name: Build sdist and wheel
19
+ run: uv build
20
+
21
+ - name: Check distributions
22
+ run: uvx twine check dist/*
23
+
24
+ - name: Upload dist artifact
25
+ uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ publish:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ # The trusted publisher on PyPI is bound to this environment name.
34
+ environment: pypi
35
+ permissions:
36
+ id-token: write # required for OIDC
37
+ steps:
38
+ - name: Download dist artifact
39
+ uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ - name: Publish to PyPI
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # OS / editor
18
+ .DS_Store
19
+ *.swp
@@ -0,0 +1,59 @@
1
+ # Contributing to mcp-tmux
2
+
3
+ Thanks for your interest in improving **mcp-tmux**! Contributions of all kinds
4
+ are welcome — bug reports, feature requests, docs, and code.
5
+
6
+ ## Where things live
7
+
8
+ - Source: [`github.com/laszlopere/mcp-tmux`](https://github.com/laszlopere/mcp-tmux)
9
+ - Issues & feature requests: <https://github.com/laszlopere/mcp-tmux/issues>
10
+ - Package source lives under `src/mcp_tmux/`; tests under `tests/`.
11
+
12
+ ## Development setup
13
+
14
+ ```bash
15
+ python -m venv .venv && . .venv/bin/activate
16
+ pip install -e ".[dev]"
17
+ ```
18
+
19
+ Or with [uv](https://docs.astral.sh/uv/):
20
+
21
+ ```bash
22
+ uv pip install -e ".[dev]"
23
+ ```
24
+
25
+ ## Checks before opening a PR
26
+
27
+ The CI runs these on Python 3.10–3.13; please run them locally first:
28
+
29
+ ```bash
30
+ ruff check . # lint
31
+ ruff format --check . # formatting
32
+ mypy # type-check (src/)
33
+ pytest # unit tests always run; integration tests need tmux
34
+ ```
35
+
36
+ The integration tests drive a real tmux server and are skipped automatically if
37
+ `tmux` isn't on `PATH`. Install tmux (`apt install tmux`, `brew install tmux`, …)
38
+ to run the full suite.
39
+
40
+ ## Guidelines
41
+
42
+ - **Universality matters.** The server targets tmux **1.8+** (≈2013). Gate any
43
+ newer flags/format variables behind a version check (see `capabilities.py`)
44
+ rather than assuming a modern tmux.
45
+ - Keep curated tools ergonomic; anything exotic can go through the
46
+ `tmux_command` passthrough instead of growing the surface area.
47
+ - Match the surrounding code style — type hints, no stray `type: ignore`.
48
+ - Update the README's tool table and docs when you add or change a tool.
49
+
50
+ ## Reporting bugs
51
+
52
+ Use the issue templates. Please include your mcp-tmux version, Python version,
53
+ the tmux version on the server host (`tmux -V`), and the target (local /
54
+ `user@host` / named profile).
55
+
56
+ ## License
57
+
58
+ By contributing, you agree that your contributions are licensed under the
59
+ project's [MIT License](LICENSE).
mcp_tmux-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 László Pere
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,309 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-tmux
3
+ Version: 0.2.0
4
+ Summary: A comprehensive, universal MCP server for driving tmux (local and over SSH).
5
+ Project-URL: Homepage, https://github.com/laszlopere/mcp-tmux
6
+ Project-URL: Repository, https://github.com/laszlopere/mcp-tmux
7
+ Project-URL: Issues, https://github.com/laszlopere/mcp-tmux/issues
8
+ Project-URL: Funding, https://github.com/sponsors/laszlopere
9
+ Author-email: László Pere <226979892+laszlopere@users.noreply.github.com>
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 László Pere
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: automation,mcp,terminal,tmux
33
+ Requires-Python: >=3.10
34
+ Requires-Dist: mcp>=1.2.0
35
+ Requires-Dist: tomli>=2.0.0; python_version < '3.11'
36
+ Provides-Extra: dev
37
+ Requires-Dist: mypy>=1.11; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
39
+ Requires-Dist: pytest>=8.0; extra == 'dev'
40
+ Requires-Dist: ruff>=0.6; extra == 'dev'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # mcp-tmux
44
+
45
+ [![CI](https://github.com/laszlopere/mcp-tmux/actions/workflows/ci.yml/badge.svg)](https://github.com/laszlopere/mcp-tmux/actions/workflows/ci.yml)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
47
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
48
+ [![Sponsor](https://img.shields.io/badge/Sponsor-%E2%9D%A4-db61a2.svg)](https://github.com/sponsors/laszlopere)
49
+
50
+ A comprehensive, universal [MCP](https://modelcontextprotocol.io) server for
51
+ driving **tmux** — sessions, windows, panes, sending keystrokes, and reading
52
+ pane output — on the local machine or on remote hosts over SSH.
53
+
54
+ Source: **<https://github.com/laszlopere/mcp-tmux>**
55
+
56
+ ## Shared, visible sessions — pair with the AI
57
+
58
+ This is the whole point, not a caveat: the agent drives **real** tmux sessions,
59
+ not a private sandbox. When you attach to a session the agent is using, you see
60
+ its keystrokes and command output **live**, and you can type into the very same
61
+ pane. Nothing the agent does is hidden from an attached human — by design.
62
+
63
+ That makes tmux a natural medium for **pair programming with the AI**: open a
64
+ shared session, watch it work, take the keyboard when you want to step in, and
65
+ hand it back. The agent and you cooperate in one place instead of the agent
66
+ operating out of sight. (Because writes into an attached session are visible and
67
+ real, be deliberate with destructive commands — you're both driving the same
68
+ terminal.)
69
+
70
+ ## Design goals
71
+
72
+ - **Comprehensive.** Curated tools cover the common operations ergonomically,
73
+ and a raw `tmux_command` passthrough runs *any* tmux subcommand — so whatever
74
+ your tmux supports, this server supports.
75
+ - **Universal.** Works against tmux **1.8+** (≈2013 — covers virtually every
76
+ live distro). The server detects the target's tmux version and only uses
77
+ flags/format variables that version understands.
78
+ - **Local + remote.** Any tool can run against the local tmux or a remote host
79
+ over SSH (ad-hoc `user@host` or a named profile from the config file). Old or
80
+ minimal boxes only need `tmux` + `ssh`; the server itself runs on a modern
81
+ host with Python 3.10+.
82
+
83
+ ## Install
84
+
85
+ ```bash
86
+ uvx mcp-tmux # run directly with uv (no install)
87
+ # or install it as an isolated, easily-removable tool:
88
+ uv tool install mcp-tmux
89
+ # or
90
+ pipx install mcp-tmux
91
+ ```
92
+
93
+ Requires Python 3.10+ on the host running the server, plus the `tmux` binary
94
+ (and `ssh` for remote targets).
95
+
96
+ > **Installing ≠ registering.** Installing the package only puts the `mcp-tmux`
97
+ > executable on your PATH — it does **not** tell any MCP client about it. Python
98
+ > wheels can't run post-install code, so registration is always a separate step
99
+ > (see below). If a client "can't find" the server after install, it just hasn't
100
+ > been registered yet.
101
+
102
+ ## Register with Claude Code
103
+
104
+ The package can register itself — no hand-editing of config files:
105
+
106
+ ```bash
107
+ mcp-tmux register # add to Claude Code at *user* scope
108
+ mcp-tmux unregister # remove it again
109
+ ```
110
+
111
+ **User scope matters.** `mcp-tmux register` defaults to `--scope user`, so the
112
+ server is visible from **every** directory/session. The plain
113
+ `claude mcp add tmux -- mcp-tmux` defaults to `local` (project) scope, which is
114
+ the usual reason a server "doesn't show up" in another session — it was only
115
+ added for the directory you ran it in. To pick a scope explicitly:
116
+
117
+ ```bash
118
+ mcp-tmux register --scope user # everywhere (default)
119
+ mcp-tmux register --scope project # shared via this repo's .mcp.json
120
+ mcp-tmux register --scope local # just this directory
121
+ ```
122
+
123
+ After registering, confirm with `claude mcp list` (you should see
124
+ `tmux: mcp-tmux - ✓ Connected`). A client session already running must be
125
+ restarted to pick up a newly registered server.
126
+
127
+ Equivalent manual form, if you prefer the raw CLI:
128
+
129
+ ```bash
130
+ claude mcp add -s user tmux -- mcp-tmux # for an installed tool
131
+ claude mcp add -s user tmux -- uvx mcp-tmux # without installing
132
+ ```
133
+
134
+ ### One-shot install + register (and clean removal)
135
+
136
+ From a checkout, the helper scripts do install **and** registration together —
137
+ the closest thing to "it happens at install time":
138
+
139
+ ```bash
140
+ scripts/install.sh # build wheel, `uv tool install`, then `mcp-tmux register`
141
+ scripts/uninstall.sh # `mcp-tmux unregister`, then `uv tool uninstall`
142
+ ```
143
+
144
+ To remove everything by hand:
145
+
146
+ ```bash
147
+ mcp-tmux unregister # drop it from Claude Code
148
+ uv tool uninstall mcp-tmux # remove the isolated tool (or: pipx uninstall mcp-tmux)
149
+ ```
150
+
151
+ ## Run from a checkout (development)
152
+
153
+ ```bash
154
+ python -m mcp_tmux # stdio server
155
+ ```
156
+
157
+ ## Tools (overview)
158
+
159
+ Tools are grouped into **toolsets** so a session only pays the schema cost of
160
+ what it needs. `core` is always loaded; the rest are opt-in (see
161
+ [Selecting toolsets](#selecting-toolsets)). A default session loads
162
+ `core` + `automation` (~18 tools); `["all"]` loads the full surface (60).
163
+
164
+ | Toolset | Tools |
165
+ |---|---|
166
+ | **core** *(always loaded)* | `tmux_command`, `tmux_query`, `tmux_version`, `tmux_list_targets`, `tmux_has_session`, `tmux_new_session`, `tmux_send_keys`, `tmux_capture_pane`, `tmux_new_window`, `tmux_list_panes`, `tmux_split_window`, `tmux_list`, `tmux_kill`, `tmux_rename`, `tmux_select` |
167
+ | `automation` *(default)* | `tmux_wait_for_text`, `tmux_wait_for_idle`, `tmux_run` |
168
+ | `layout` | `tmux_next_layout`, `tmux_move_window`, `tmux_select_layout`, `tmux_resize_pane`, `tmux_set_pane_title`, `tmux_clear_history`, `tmux_swap`, `tmux_last`, `tmux_respawn`, `tmux_link_window`, `tmux_unlink_window`, `tmux_break_pane`, `tmux_join_pane`, `tmux_find_window`, `tmux_pipe_pane` |
169
+ | `buffers` | `tmux_set_buffer`, `tmux_paste_buffer`, `tmux_delete_buffer`, `tmux_save_buffer`, `tmux_load_buffer` |
170
+ | `config` | `tmux_set_option`, `tmux_show_options`, `tmux_set_environment`, `tmux_show_environment`, `tmux_set_hook`, `tmux_show_hooks`, `tmux_run_shell`, `tmux_if_shell` |
171
+ | `keybindings` | `tmux_list_keys`, `tmux_bind_key`, `tmux_unbind_key` |
172
+ | `copymode` | `tmux_copy_mode`, `tmux_copy_scroll`, `tmux_copy_search` |
173
+ | `clients` | `tmux_server_info`, `tmux_display_message` |
174
+ | `stream` | `tmux_stream_start`, `tmux_stream_resize`, `tmux_stream_read`, `tmux_stream_send`, `tmux_stream_list`, `tmux_stream_stop` |
175
+
176
+ Every tool accepts an optional `target` (omit / `"local"`, a named profile, or
177
+ `user@host`). For anything not covered by a dedicated tool — including anything
178
+ gated out of the active toolsets — use `tmux_command(args=[...])`, which is in
179
+ `core` and reaches every tmux subcommand.
180
+
181
+ ### Selecting toolsets
182
+
183
+ Pick toolsets with the `toolsets` config key or the `MCP_TMUX_TOOLSETS`
184
+ environment variable (comma-separated; env wins over config). `core` is always
185
+ included. The special value `all` loads every toolset. An unknown name is a
186
+ startup error listing the valid toolsets.
187
+
188
+ ```toml
189
+ # ~/.config/mcp-tmux/config.toml
190
+ toolsets = ["core", "automation", "stream"] # or ["all"] for the full surface
191
+ ```
192
+
193
+ ```jsonc
194
+ // or in the MCP server env, e.g. Claude Code's mcp config:
195
+ "env": { "MCP_TMUX_TOOLSETS": "core,layout,stream" }
196
+ ```
197
+
198
+ With no setting, the default is `["core", "automation"]`.
199
+
200
+ The **consolidated** tools take a `kind` discriminator instead of having one
201
+ tool per entity — e.g. `tmux_kill(kind="window", id="dev:2")`,
202
+ `tmux_kill(kind="server")`, `tmux_swap(kind="pane", src="%1", dst="%2")`,
203
+ `tmux_rename(kind="session", id="old", new_name="new")`,
204
+ `tmux_list(kind="window", scope="dev")`. Valid kinds: `kill` →
205
+ session/window/pane/server; `rename` → session/window; `select`/`last`/`swap` →
206
+ window/pane; `respawn` → pane/window; `list` → session/window/client/buffer
207
+ (`tmux_list` returns `{items, kind}`; panes have their own `tmux_list_panes`
208
+ because they scope by window *or* session).
209
+
210
+ ### Live streaming (opt-in)
211
+
212
+ The one-shot CLI is the universal default. For *watching* a pane as it
213
+ produces output — a build, a `tail`, a long job — `tmux_stream_*` opens a
214
+ persistent **control-mode** (`tmux -C`) connection and lets you long-poll its
215
+ event stream instead of repeatedly calling `tmux_capture_pane`:
216
+
217
+ ```
218
+ tmux_stream_start(session="work") # -> {"stream_id": "cm-1a2b3c4d", ...}
219
+ tmux_stream_read("cm-1a2b3c4d", timeout=10, kinds=["output"])
220
+ # -> blocks until output, then {"events": [{"type":"output","pane":"%0",
221
+ # "data":"...","seq":42}], "cursor":42}
222
+ tmux_stream_stop("cm-1a2b3c4d") # detaches; the session keeps running
223
+ ```
224
+
225
+ `tmux_stream_read` auto-advances a cursor, so just call it again for the next
226
+ batch; filter by `pane` and/or `kinds` (`"output"`, `"window-add"`,
227
+ `"layout-change"`, …). One connection is shared per (target, session) and
228
+ `tmux_stream_start` is idempotent.
229
+
230
+ Read-only state is also exposed as MCP **resources**: `tmux://sessions`,
231
+ `tmux://{session}/windows`, `tmux://{window}/panes` (local), plus target-aware
232
+ variants `tmux://{target}/sessions`, `tmux://{target}/{session}/windows`,
233
+ `tmux://{target}/{window}/panes`.
234
+
235
+ A typical agent flow:
236
+
237
+ ```
238
+ tmux_new_session(detached=True) # -> {"id": "$0", "name": "0"}
239
+ tmux_send_keys("0", text="echo hi", enter=True)
240
+ tmux_capture_pane("0") # -> {"content": "... hi ..."}
241
+ ```
242
+
243
+ ### Where `send_keys` text is evaluated
244
+
245
+ `tmux_send_keys` types its `text` **into the pane** — it is not a local shell
246
+ command. So any shell syntax in it (`$(...)`, backticks, `$VAR`, `~`, globs, …)
247
+ is expanded by the **shell running in that pane**, at the moment the keys are
248
+ executed — *not* on the machine running this MCP server. For an SSH target that
249
+ means the **remote** pane's shell does the expansion; the server only ships the
250
+ literal text across (the SSH layer shell-quotes the tmux argv so it survives the
251
+ hop intact).
252
+
253
+ ```
254
+ # `$(hostname)` runs in the pane, so it prints the *target's* hostname,
255
+ # not the server's:
256
+ tmux_send_keys("work", text="echo $(hostname)", enter=True)
257
+ ```
258
+
259
+ If you need a value from the server side instead, interpolate it yourself before
260
+ calling `send_keys`.
261
+
262
+ ## Configuration
263
+
264
+ Optional TOML at `~/.config/mcp-tmux/config.toml` (override with
265
+ `MCP_TMUX_CONFIG`):
266
+
267
+ ```toml
268
+ # toolsets = ["core", "automation"] # which tool groups to load; ["all"] = full
269
+ # # (also via MCP_TMUX_TOOLSETS, env wins)
270
+
271
+ [defaults]
272
+ timeout = 15 # seconds per tmux invocation
273
+ # socket_name = "work" # default `tmux -L`
274
+ # socket_path = "/tmp/sock" # default `tmux -S`
275
+
276
+ [targets.prod]
277
+ host = "user@prod-db"
278
+ ssh_options = ["-J", "bastion", "-p", "2222"]
279
+ # socket_name = "work"
280
+ ```
281
+
282
+ With no config file the server still works against local tmux and any ad-hoc
283
+ `user@host` target (SSH options come from your `~/.ssh/config`).
284
+
285
+ ## Development
286
+
287
+ ```bash
288
+ python -m venv .venv && . .venv/bin/activate
289
+ pip install -e ".[dev]"
290
+ pytest # unit tests always run; integration tests run if tmux exists
291
+ ```
292
+
293
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full checklist (ruff, mypy,
294
+ pytest) and contribution guidelines.
295
+
296
+ ## Contributing
297
+
298
+ Bug reports, feature requests, and pull requests are welcome on GitHub:
299
+ **<https://github.com/laszlopere/mcp-tmux>**. Please read
300
+ [CONTRIBUTING.md](CONTRIBUTING.md) first.
301
+
302
+ ## Sponsor
303
+
304
+ If this project is useful to you, consider sponsoring its development via
305
+ [GitHub Sponsors](https://github.com/sponsors/laszlopere). ❤️
306
+
307
+ ## License
308
+
309
+ [MIT](LICENSE) © László Pere