Skip to content

Commit 58908a7

Browse files
committed
libtmux-mcp(feat): Initial package — MCP server for tmux, powered by libtmux
why: libtmux PR #642 added MCP support as an optional extra, but the decision is to ship it as a standalone package (libtmux-mcp) that depends on libtmux and fastmcp rather than merging into libtmux core. what: - 25 MCP tools across 6 modules (server, session, window, pane, options, env) - 6 tmux:// URI resources for browsing tmux hierarchy - Safety tier middleware (readonly, mutating, destructive) - Pydantic models for all tool outputs - Socket isolation via LIBTMUX_SOCKET / LIBTMUX_SOCKET_PATH env vars - Agent self-awareness via TMUX_PANE detection - Server caching with is_alive() eviction - 147 tests (all passing, mypy strict, ruff clean) - Full Sphinx/Furo documentation site (port 8024) - GitHub Actions CI with tmux matrix and PyPI trusted publishing - Fix clear_pane to split send-keys -R and clear-history into separate cmd() calls, working around libtmux <= 0.55.0 bug where \; separator is not interpreted by subprocess (libtmux#650)
1 parent b8e6a43 commit 58908a7

83 files changed

Lines changed: 11286 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.codecov.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
codecov:
2+
notify:
3+
require_ci_to_pass: no
4+
5+
coverage:
6+
precision: 2
7+
round: down
8+
range: "70...100"
9+
status:
10+
project:
11+
default:
12+
target: auto
13+
threshold: 1%
14+
base: auto
15+
patch: off

.github/contributing.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Contributing
2+
3+
When contributing to this repository, please first discuss the change you wish to make via issue,
4+
email, or any other method with the maintainers of this repository before making a change.
5+
6+
Please note we have a code of conduct, please follow it in all your interactions with the project.
7+
8+
## Pull Request Process
9+
10+
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11+
build.
12+
2. This project uses flake8 to conform with common Python standards. Make sure
13+
to run your code through linter using latest version of flake8, before pull request.
14+
3. Bad documnentation is a Bug. If your change demands documentation update, please do so. If you
15+
find an issue with documentation, take the time to improve or fix it.
16+
4. pytest is used for automated testing. Please make sure to update tests that are needed, and to run
17+
`make test` before submitting your pull request. This should prevent issues with CI and
18+
make the review and merging process easier and faster.
19+
5. Update the README.md with details of changes to the interface, this includes new environment
20+
variables, exposed ports, useful file locations and container parameters.
21+
6. Increase the version numbers in any examples files and the README.md to the new version that this
22+
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
23+
7. You may merge the Pull Request in once you have the sign-off of one other developer. If you
24+
do not have permission to do that, you may request reviewer to merge it for you.
25+
26+
## Decorum
27+
28+
- Participants will be tolerant of opposing views.
29+
- Participants must ensure that their language and actions are free of personal
30+
attacks and disparaging personal remarks.
31+
- When interpreting the words and actions of others, participants should always
32+
assume good intentions.
33+
- Behaviour which can be reasonably considered harassment will not be tolerated.
34+
35+
Based on [Ruby's Community Conduct Guideline](https://www.ruby-lang.org/en/conduct/)

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
# Check for updates to GitHub Actions every week
7+
interval: "weekly"

.github/stale.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Number of days of inactivity before an issue becomes stale
2+
daysUntilStale: 60
3+
# Number of days of inactivity before a stale issue is closed
4+
daysUntilClose: 7
5+
# Issues with these labels will never be considered stale
6+
exemptLabels:
7+
- pinned
8+
- security
9+
# Label to use when marking an issue as stale
10+
staleLabel: stale
11+
# Comment to post when marking an issue as stale. Set to `false` to disable
12+
markComment: |
13+
This issue has been automatically marked as stale because it has not had
14+
recent activity. It will be closed if no further activity occurs. Thank you
15+
for your contributions.
16+
17+
This bot is used to handle issues where the issue hasn't been discussed or
18+
has gone out of date. If an issue isn't resolved and handled in a certain
19+
period of time, it may be closed. If you would like your issue re-opened,
20+
please create a fresh issue with the latest, up to date information and
21+
mention this issue in it.
22+
unmarkComment: |
23+
Thank you for updating this issue. It is no longer marked as stale.
24+
25+
# Comment to post when closing a stale issue. Set to `false` to disable
26+
closeComment: false
27+
28+
# limit to only 'issues' or 'pulls'
29+
only: issues

.github/workflows/tests.yml

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
# Don't run twice for internal PRs from our own repo
8+
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
9+
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
python-version: ['3.14']
14+
tmux-version: ['3.2a', '3.3a', '3.4', '3.5', '3.6', 'master']
15+
steps:
16+
- uses: actions/checkout@v6
17+
18+
- name: Install uv
19+
uses: astral-sh/setup-uv@v7
20+
with:
21+
enable-cache: true
22+
23+
- name: Set up Python ${{ matrix.python-version }}
24+
run: uv python install ${{ matrix.python-version }}
25+
26+
- name: Test runtime dependencies
27+
run: |
28+
uv run --no-dev -p python${{ matrix.python-version }} -- python -c '
29+
from libtmux_mcp import main
30+
from libtmux_mcp.__about__ import __version__
31+
print("libtmux-mcp version:", __version__)
32+
'
33+
34+
- name: Install dependencies
35+
run: uv sync --all-extras --dev
36+
37+
- name: Setup tmux build cache for tmux ${{ matrix.tmux-version }}
38+
id: tmux-build-cache
39+
uses: actions/cache@v5
40+
with:
41+
path: ~/tmux-builds/tmux-${{ matrix.tmux-version }}
42+
key: tmux-${{ matrix.tmux-version }}
43+
44+
- name: Build tmux ${{ matrix.tmux-version }}
45+
if: steps.tmux-build-cache.outputs.cache-hit != 'true'
46+
run: |
47+
sudo apt install libevent-dev libncurses5-dev libtinfo-dev libutempter-dev bison
48+
mkdir ~/tmux-builds
49+
mkdir ~/tmux-src
50+
git clone https://github.com/tmux/tmux.git ~/tmux-src/tmux-${{ matrix.tmux-version }}
51+
cd ~/tmux-src/tmux-${{ matrix.tmux-version }}
52+
git checkout ${{ matrix.tmux-version }}
53+
sh autogen.sh
54+
./configure --prefix=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }} && make && make install
55+
export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH
56+
cd ~
57+
tmux -V
58+
59+
- name: Lint with ruff check
60+
run: uv run ruff check .
61+
62+
- name: Format with ruff format
63+
run: uv run ruff format . --check
64+
65+
- name: Lint with mypy
66+
run: uv run mypy .
67+
68+
- name: Print python versions
69+
run: |
70+
python -V
71+
uv run python -V
72+
73+
- name: Test with pytest
74+
continue-on-error: ${{ matrix.tmux-version == 'master' }}
75+
run: |
76+
sudo apt install libevent-2.1-7
77+
export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH
78+
ls $HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin
79+
tmux -V
80+
uv run py.test --cov=./ --cov-append --cov-report=xml -n auto --verbose
81+
env:
82+
COV_CORE_SOURCE: .
83+
COV_CORE_CONFIG: .coveragerc
84+
COV_CORE_DATAFILE: .coverage.eager
85+
- uses: codecov/codecov-action@v5
86+
with:
87+
token: ${{ secrets.CODECOV_TOKEN }}
88+
89+
release:
90+
runs-on: ubuntu-latest
91+
needs: build
92+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
93+
permissions:
94+
id-token: write # Required for OIDC trusted publishing
95+
attestations: write # Required for generating attestations
96+
97+
strategy:
98+
matrix:
99+
python-version: ['3.14']
100+
101+
steps:
102+
- uses: actions/checkout@v6
103+
104+
- name: Install uv
105+
uses: astral-sh/setup-uv@v7
106+
with:
107+
enable-cache: true
108+
109+
- name: Set up Python ${{ matrix.python-version }}
110+
run: uv python install ${{ matrix.python-version }}
111+
112+
- name: Install dependencies
113+
run: uv sync --all-extras --dev
114+
115+
- name: Build package
116+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
117+
run: uv build
118+
119+
- name: Publish package
120+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
121+
uses: pypa/gh-action-pypi-publish@release/v1
122+
with:
123+
attestations: true
124+
skip-existing: true

.prettierrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"printWidth": 100
3+
}

.tool-versions

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
just 1.47
2+
uv 0.10.12
3+
python 3.14 3.13 3.12 3.11 3.10

0 commit comments

Comments
 (0)