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.
- mcp_tmux-0.2.0/.github/FUNDING.yml +11 -0
- mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- mcp_tmux-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- mcp_tmux-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- mcp_tmux-0.2.0/.github/workflows/ci.yml +38 -0
- mcp_tmux-0.2.0/.github/workflows/release.yml +45 -0
- mcp_tmux-0.2.0/.gitignore +19 -0
- mcp_tmux-0.2.0/CONTRIBUTING.md +59 -0
- mcp_tmux-0.2.0/LICENSE +21 -0
- mcp_tmux-0.2.0/PKG-INFO +309 -0
- mcp_tmux-0.2.0/README.md +267 -0
- mcp_tmux-0.2.0/TODO.md +445 -0
- mcp_tmux-0.2.0/pyproject.toml +58 -0
- mcp_tmux-0.2.0/scripts/install.sh +31 -0
- mcp_tmux-0.2.0/scripts/isolated_config.toml +5 -0
- mcp_tmux-0.2.0/scripts/try_client.py +56 -0
- mcp_tmux-0.2.0/scripts/uninstall.sh +20 -0
- mcp_tmux-0.2.0/src/mcp_tmux/__init__.py +3 -0
- mcp_tmux-0.2.0/src/mcp_tmux/__main__.py +55 -0
- mcp_tmux-0.2.0/src/mcp_tmux/capabilities.py +81 -0
- mcp_tmux-0.2.0/src/mcp_tmux/config.py +80 -0
- mcp_tmux-0.2.0/src/mcp_tmux/control.py +492 -0
- mcp_tmux-0.2.0/src/mcp_tmux/formats.py +117 -0
- mcp_tmux-0.2.0/src/mcp_tmux/register.py +49 -0
- mcp_tmux-0.2.0/src/mcp_tmux/resources.py +57 -0
- mcp_tmux-0.2.0/src/mcp_tmux/runner.py +152 -0
- mcp_tmux-0.2.0/src/mcp_tmux/server.py +52 -0
- mcp_tmux-0.2.0/src/mcp_tmux/targets.py +104 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/__init__.py +47 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/_capture.py +40 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/_util.py +113 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/clients.py +56 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/copymode.py +107 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/environment.py +98 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/hooks.py +113 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/io.py +90 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/keys.py +83 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/merged.py +185 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/options.py +142 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/panes.py +137 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/passthrough.py +76 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/plumbing.py +146 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/sessions.py +89 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/stream.py +124 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/wait.py +173 -0
- mcp_tmux-0.2.0/src/mcp_tmux/tools/windows.py +63 -0
- mcp_tmux-0.2.0/src/mcp_tmux/toolsets.py +168 -0
- mcp_tmux-0.2.0/tests/test_annotations.py +57 -0
- mcp_tmux-0.2.0/tests/test_capabilities.py +34 -0
- mcp_tmux-0.2.0/tests/test_control.py +306 -0
- mcp_tmux-0.2.0/tests/test_formats.py +81 -0
- mcp_tmux-0.2.0/tests/test_functional.py +204 -0
- mcp_tmux-0.2.0/tests/test_integration.py +176 -0
- mcp_tmux-0.2.0/tests/test_p2.py +175 -0
- mcp_tmux-0.2.0/tests/test_p5.py +346 -0
- mcp_tmux-0.2.0/tests/test_runner.py +40 -0
- mcp_tmux-0.2.0/tests/test_targets.py +67 -0
- mcp_tmux-0.2.0/tests/test_tools_argv.py +965 -0
- mcp_tmux-0.2.0/tests/test_toolsets.py +80 -0
- mcp_tmux-0.2.0/tests/test_wait.py +51 -0
- 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,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.
|
mcp_tmux-0.2.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://github.com/laszlopere/mcp-tmux/actions/workflows/ci.yml)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://www.python.org/)
|
|
48
|
+
[](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
|