feat: opt-in engine-ops chained plan tools and watchers#87
Draft
tony wants to merge 9 commits into
Draft
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #87 +/- ##
==========================================
- Coverage 84.86% 84.85% -0.01%
==========================================
Files 43 45 +2
Lines 3198 3243 +45
Branches 438 442 +4
==========================================
+ Hits 2714 2752 +38
- Misses 352 358 +6
- Partials 132 133 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
why: the chained plan tools and watchers consume libtmux's unreleased experimental MCP adapter (libtmux.experimental.mcp / .engines). This branch-only [tool.uv.sources] pin makes them importable; not for release. what: - Add [tool.uv.sources] libtmux -> engine-ops git branch - uv lock
why: the hand-written tools are single-op. libtmux's engine-ops plan tier lets an agent chain multiple tmux operations that fold into a few `tmux a ; b` dispatches, previewing and explaining a plan and resolving forward-reference ids via a bindings map before executing. what: - Add tools/engine_plan.py: opt-in (LIBTMUX_MCP_ENGINE_OPS=1) registration of preview_plan/explain_plan/result_schema/execute_plan over an AsyncSubprocessEngine, using the operation registry (no op_* tool clutter) - Wire it into register_tools; default off so the shipped surface is intact - Cover enabled()/opt-in, tool presence, no op_* leak, and a folded forward-ref preview+explain round-trip via the in-process client
why: the shipped waiters poll capture_pane. libtmux's engine-ops event stream offers a needle-free wait_for_output settle monitor (returns when a pane goes quiet after a command, no sentinel) plus watch_events/poll_events over the control-mode notification stream -- non-blocking by construction. what: - Add tools/engine_watch.py: opt-in (same LIBTMUX_MCP_ENGINE_OPS flag) that builds one AsyncControlModeEngine and registers the event/monitor tools; astartup/ashutdown open and close it - Start/close that engine in the server _lifespan; wire register into register_tools before the visibility gate - Cover per-mode registration, the opt-in/no-op-leak gate, the off-path lifecycle no-op, and a live wait_for_output settle against a real pane
why: The libtmux engine-ops branch advanced (bug fixes + doc hygiene); re-resolve the pin so this branch builds against the current tip.
why: The libtmux engine-ops branch advanced (supervised reconnect, drain-restart error fix, reconnect backoff + proc-cleanup hardening, and the experimental facade->objects package rename), so re-resolve the git pin to pick it up. what: - uv.lock: libtmux engine-ops 4393b02a -> f52b2d3f.
why: Track the latest engine-ops branch tip so the pinned libtmux matches the branch HEAD used downstream. what: - Bump locked libtmux rev f52b2d3f -> be88f08d (scripts-only delta: hermetic engine benchmark grid + results)
why: Track the latest engine-ops branch tip so the pinned libtmux matches the branch HEAD used downstream. what: - Bump locked libtmux rev be88f08d -> aa6d822d (scripts-only delta: bench_engines mypy fix)
why: Track the latest engine-ops branch tip so the pinned libtmux matches the branch HEAD used downstream. what: - Bump locked libtmux rev aa6d822d -> 1fe8fc67 (ConcreteEngine renamed to MockEngine upstream; no downstream code references it)
why: libtmux's engine-ops branch was rebased onto master (absorbing the gp-sphinx docs-dep bump); the prior pinned commit is now unreachable and GC-eligible on the remote. what: - Bump locked libtmux rev 1fe8fc67 -> e88e87b6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an opt-in tier of MCP tools backed by libtmux's experimental engine-ops stack. Off by default; enabled with
LIBTMUX_MCP_ENGINE_OPS=1.What's here
Chained plan tools (
engine_plan.py): registers libtmux's plan tools over anAsyncSubprocessEngine, so a client can build and run a folded multi-step plan (record → resolve forward refs → execute) in a few;-chained dispatches instead of one tool call per step. Uses the operation registry +LazyPlan.from_list, so noop_*tools leak into the schema.Watchers (
engine_watch.py): registers libtmux's control-mode event/monitor tools over anAsyncControlModeEngine:wait_for_output— a needle-free settle monitor; folds a pane's live%outputand returns when the pane goes quiet, replacing sleep-then-capture_panepolling.watch_events(push) and/orpoll_events(pull) over the control-mode notification stream, selected byLIBTMUX_MCP_EVENTS.The engine is opened/closed from the server lifespan; streaming is non-blocking (a single reader fans out to bounded, drop-oldest per-subscriber queues).
Configuration
LIBTMUX_MCP_ENGINE_OPS=1— enable the tier (default: off)LIBTMUX_MCP_EVENTS—push|pull|both(default:push)LIBTMUX_MCP_EVENT_SOURCE— event source (default:subscription)Run it via uvx / uv tool
This branch pins
libtmuxto its unreleasedengine-opsbranch via[tool.uv.sources], and a recentuv(tested with 0.11.25) honors that pin when installing from git — so no extra flags are needed. Do not add--with git+…libtmux…: the pin already supplies it, and a second URL fails with a "conflicting URLs" error. The engine-ops tools stay off untilLIBTMUX_MCP_ENGINE_OPS=1.Run the server ephemerally over stdio:
Install it persistently as
libtmux-mcp:Add to your coding agent (uvx)
Each snippet below registers the branch as an MCP server named
tmuxwith the engine-ops tier enabled. Every one was verified to launch the server and expose this PR's 7 engine-ops tools (build_workspace,execute_plan,explain_plan,preview_plan,result_schema,wait_for_output,watch_events) on top of the 52 base tools — 59 total.Claude Code (
claude mcp add):Codex CLI (
codex mcp add):Gemini CLI (
gemini mcp add):Grok (
grok mcp add):Cursor CLI — Cursor has no
mcp add; add the server to~/.cursor/mcp.json(global) or.cursor/mcp.json(project):{ "mcpServers": { "tmux": { "command": "uvx", "args": ["--from", "git+https://github.com/tmux-python/libtmux-mcp.git@engine-ops", "libtmux-mcp"], "env": { "LIBTMUX_MCP_ENGINE_OPS": "1" } } } }then approve it:
cursor-agent mcp enable tmuxAntigravity (
agy) — add the server to~/.gemini/config/mcp_config.json(samemcpServersshape as Cursor):{ "mcpServers": { "tmux": { "command": "uvx", "args": ["--from", "git+https://github.com/tmux-python/libtmux-mcp.git@engine-ops", "libtmux-mcp"], "env": { "LIBTMUX_MCP_ENGINE_OPS": "1" } } } }Drop
LIBTMUX_MCP_ENGINE_OPS(or set it to0) to register only the 52 base tools. The engine-ops tools drive tmux, so a reachable tmux server is required; addLIBTMUX_SOCKET=<name>to theenvblock to isolate them on a dedicated socket instead of your default server.Dependency
Pins
libtmuxto theengine-opsbranch via[tool.uv.sources]— the engine-ops API is unreleased (libtmux.experimental). Draft until that lands.