Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3c447b7
py(deps[libtmux]) Pin to sibling chainable-commands worktree
tony Jun 20, 2026
fb9926a
mcp(feat[chain]): one-dispatch tmux command chains
tony Jun 20, 2026
0fb2bfa
mcp(feat[chain]): forward-layout pane id capture
tony Jun 20, 2026
39413cc
Middleware(test[logs]): Tolerate propagation
tony Jun 20, 2026
0291e14
py(deps[libtmux]): Pin public chain commit
tony Jun 20, 2026
dddec56
Chain(feat[tools]): Add typed op compiler
tony Jun 20, 2026
701c4bc
Chain(feat[tools]): Fold marked split refs
tony Jun 20, 2026
fc2fc6e
Chain(docs): Document operation chains
tony Jun 20, 2026
eae2f6e
Chain(fix[tools]): Use chain metadata
tony Jun 20, 2026
dca46d4
Chain(feat[tools]): Add dry-run plans
tony Jun 20, 2026
13c1d15
Chain(fix[tools]): Continue dry-run plans
tony Jun 20, 2026
5ebe0b7
Chain(feat[tools]): Bound dispatch waits
tony Jun 20, 2026
20e8f61
Chain(test[tools]): Cover error paths
tony Jun 20, 2026
8e3a402
Chain(feat[tools]): Roll back panes
tony Jun 20, 2026
0fc8ea1
py(deps): Pin libtmux control runner
tony Jun 21, 2026
0d90c2b
Chain(feat[tools]): Default control transport
tony Jun 21, 2026
0205414
Chain(fix[tools]): Pin split pane directory
tony Jun 21, 2026
daf854d
py(deps): Pin libtmux review fixes
tony Jun 21, 2026
19fd533
Chain(refactor[tools]): Run each operation over control
tony Jun 21, 2026
d12a9ab
Chain(feat[tools]): Type each operation result by kind
tony Jun 21, 2026
e88fa56
Chain(feat[tools]): Take a typed pane target
tony Jun 21, 2026
0809e46
Chain(refactor[tools]): Rename run_tmux_operations to run_tmux_plan
tony Jun 21, 2026
efd1a0a
Chain(feat[tools]): Add typed layout operations
tony Jun 21, 2026
932396c
Chain(feat[tools]): Gate a typed kill_pane on the destructive tier
tony Jun 21, 2026
de3eef8
Chain(feat[tools]): One generic batch tool and a plan history flag
tony Jun 21, 2026
07ba9e1
compat(rebase[libtmux]): Reconcile #98 error handling with the pinned…
tony Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@
_Notes on upcoming releases will be added here_
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

### What's new

**Typed tmux operation chains with {tooliconl}`run-tmux-plan`**

{tooliconl}`run-tmux-plan` accepts an ordered list of typed tmux
operations and runs each one over a persistent `tmux -C` control connection. It
returns one typed result per operation, discriminated by `kind`: `capture_pane`
returns its `lines`, `split_pane` returns the new `pane_id`, and the rest return
status only. With `on_error="stop"` (the default) it stops before the next
operation once one fails or its target cannot be resolved, marking the rest
skipped; with `on_error="continue"` it records each failure and runs the rest.
Each pane operation takes one typed `target`, discriminated by `kind`: a
concrete `pane_id` or a `ref` minted by an earlier split. Typed `split_evenly`
and `make_grid` operations build an even row or column or a tiled grid of panes
without hand-written layout strings. A typed `kill_pane` operation closes a
pane, and runs only when the server's safety tier is `destructive` so a
mutating-tier plan cannot smuggle a destructive command past the safety gate. It
returns concrete pane IDs captured from referenced splits so later operations
can target them, supports a dry-run mode that returns the planned steps without
touching tmux, applies a per-dispatch timeout, and can roll back panes created
by typed split refs when a later operation fails. Pass `explain=true` to attach
per-dispatch diagnostics under `diagnostics`. It keeps
{tooliconl}`call-tools-batch` available for workflows that need to call
arbitrary MCP tools instead of this tool's typed operation set.

**One generic batch tool with {tooliconl}`call-tools-batch`**

{tooliconl}`call-tools-batch` runs an ordered list of existing MCP tools in a
single call and returns a per-operation result for each, preserving every
nested tool's structured output. Each nested call still runs through the
server's safety tier, and an optional `max_tier` caps the batch below that tier
(`readonly` refuses mutating or destructive nested calls, `mutating` refuses
destructive ones). It replaces the separate readonly, mutating, and destructive
batch tools with one tool plus the `max_tier` argument.

## libtmux-mcp 0.1.0a18 (2026-07-12)

libtmux-mcp 0.1.0a18 raises the libtmux floor to 0.62.0 and tightens how the server handles lookups that cannot succeed. A readonly call carrying a stale or ambiguous id now fails on the first attempt instead of paying a second tmux round-trip and a backoff window to fail identically, and an ambiguous target — a window shared between sessions, matched by name or index — now comes back with a recovery hint that says to target it by id instead of an unexplained error. The floor bump is what makes both fixes possible: libtmux 0.62.0 folds its query errors into the {exc}`libtmux.exc.LibTmuxException` hierarchy the server keys on.
Expand Down Expand Up @@ -88,13 +123,13 @@ The dev and test dependency groups now require pytest 9.1.0 or newer. (#83)

## libtmux-mcp 0.1.0a14 (2026-06-14)

libtmux-mcp 0.1.0a14 adds tier-aware tool batching. {tooliconl}`call-readonly-tools-batch`, {tooliconl}`call-mutating-tools-batch`, and {tooliconl}`call-destructive-tools-batch` run an ordered list of existing MCP tools in a single call and return a per-operation result for each, preserving every nested tool's own structured output. Each wrapper caps the safety tier of the calls it will make — regardless of the server's `LIBTMUX_SAFETY` tier — and `on_error` selects stop-at-first-failure or continue-and-report handling. Aggregate results stay within the server's response limit.
libtmux-mcp 0.1.0a14 adds tier-aware tool batching. `call_readonly_tools_batch`, `call_mutating_tools_batch`, and `call_destructive_tools_batch` run an ordered list of existing MCP tools in a single call and return a per-operation result for each, preserving every nested tool's own structured output. Each wrapper caps the safety tier of the calls it will make — regardless of the server's `LIBTMUX_SAFETY` tier — and `on_error` selects stop-at-first-failure or continue-and-report handling. Aggregate results stay within the server's response limit.

### What's new

**Tier-aware tool batching**

{tooliconl}`call-readonly-tools-batch`, {tooliconl}`call-mutating-tools-batch`, and {tooliconl}`call-destructive-tools-batch` run an ordered list of existing MCP tools in a single call and return a per-operation result for each, preserving every nested tool's own structured output. Each wrapper caps the safety tier of the calls it will make — the readonly wrapper refuses mutating or destructive operations, and the mutating wrapper refuses destructive ones — regardless of the server's `LIBTMUX_SAFETY` tier. Nested calls keep their normal schema validation, middleware, and safety checks, and `on_error` selects stop-at-first-failure or continue-and-report handling. Large aggregate results stay within the server's response limit — oversized nested payloads are dropped (with the truncation flagged in the result), and very large operation lists are rejected rather than allowed to overflow it. (#79)
`call_readonly_tools_batch`, `call_mutating_tools_batch`, and `call_destructive_tools_batch` run an ordered list of existing MCP tools in a single call and return a per-operation result for each, preserving every nested tool's own structured output. Each wrapper caps the safety tier of the calls it will make — the readonly wrapper refuses mutating or destructive operations, and the mutating wrapper refuses destructive ones — regardless of the server's `LIBTMUX_SAFETY` tier. Nested calls keep their normal schema validation, middleware, and safety checks, and `on_error` selects stop-at-first-failure or continue-and-report handling. Large aggregate results stay within the server's response limit — oversized nested payloads are dropped (with the truncation flagged in the result), and very large operation lists are rejected rather than allowed to overflow it. (#79)

## libtmux-mcp 0.1.0a13 (2026-06-13)

Expand Down
19 changes: 19 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def _patched_tool_collector_tool(self: ToolCollector, **kwargs: t.Any) -> t.Any:

conf["fastmcp_tool_modules"] = [
"libtmux_mcp.tools.batch_tools",
"libtmux_mcp.tools.chain_tools",
"libtmux_mcp.tools.server_tools",
"libtmux_mcp.tools.session_tools",
"libtmux_mcp.tools.window_tools",
Expand All @@ -136,6 +137,7 @@ def _patched_tool_collector_tool(self: ToolCollector, **kwargs: t.Any) -> t.Any:
]
conf["fastmcp_area_map"] = {
"batch_tools": "batch/index",
"chain_tools": "chain/index",
"server_tools": "server/index",
"session_tools": "session/index",
"window_tools": "window/index",
Expand Down Expand Up @@ -166,6 +168,23 @@ def _patched_tool_collector_tool(self: ToolCollector, **kwargs: t.Any) -> t.Any:
"SendKeysOperation",
"SendKeysOperationResult",
"SendKeysBatchResult",
"PaneIdTarget",
"RefTarget",
"SplitPaneOperation",
"TmuxSendKeysOperation",
"ResizePaneOperation",
"SelectLayoutOperation",
"SetOptionOperation",
"CapturePaneOperation",
"SplitEvenlyOperation",
"MakeGridOperation",
"KillPaneOperation",
"SplitPaneStepResult",
"CapturePaneStepResult",
"OperationStepResult",
"TmuxOperationDispatchResult",
"RunTmuxDiagnostics",
"RunTmuxPlanResult",
"ToolCallOperation",
"ToolCallOperationResult",
"ToolCallBatchResult",
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,19 @@ Config blocks for Claude Desktop, Claude Code, Cursor, and others.

Read tmux state without changing anything.

{toolref}`list-sessions` · {toolref}`capture-pane` · {toolref}`capture-since` · {toolref}`snapshot-pane` · {toolref}`get-pane-info` · {toolref}`find-pane-by-position` · {toolref}`search-panes` · {toolref}`wait-for-text` · {toolref}`wait-for-content-change` · {toolref}`display-message` · {toolref}`call-readonly-tools-batch`
{toolref}`list-sessions` · {toolref}`capture-pane` · {toolref}`capture-since` · {toolref}`snapshot-pane` · {toolref}`get-pane-info` · {toolref}`find-pane-by-position` · {toolref}`search-panes` · {toolref}`wait-for-text` · {toolref}`wait-for-content-change` · {toolref}`display-message` · {toolref}`call-tools-batch`

### Act (mutating)

Create or modify tmux objects.

{toolref}`create-session` · {toolref}`send-keys` · {toolref}`send-keys-batch` · {toolref}`run-command` · {toolref}`paste-text` · {toolref}`create-window` · {toolref}`split-window` · {toolref}`select-pane` · {toolref}`select-window` · {toolref}`move-window` · {toolref}`resize-pane` · {toolref}`pipe-pane` · {toolref}`set-option` · {toolref}`call-mutating-tools-batch`
{toolref}`create-session` · {toolref}`send-keys` · {toolref}`send-keys-batch` · {toolref}`run-command` · {toolref}`paste-text` · {toolref}`create-window` · {toolref}`split-window` · {toolref}`select-pane` · {toolref}`select-window` · {toolref}`move-window` · {toolref}`resize-pane` · {toolref}`pipe-pane` · {toolref}`set-option`

### Destroy (destructive)

Tear down tmux objects. Not reversible.

{toolref}`kill-session` · {toolref}`kill-window` · {toolref}`kill-pane` · {toolref}`kill-server` · {toolref}`call-destructive-tools-batch`
{toolref}`kill-session` · {toolref}`kill-window` · {toolref}`kill-pane` · {toolref}`kill-server`

### Example: keep test runs out of persistent history

Expand Down
9 changes: 9 additions & 0 deletions docs/reference/api/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
:show-inheritance:
```

## Chain tools

```{eval-rst}
.. automodule:: libtmux_mcp.tools.chain_tools
:members:
:undoc-members:
:show-inheritance:
```

## Server tools

```{eval-rst}
Expand Down
32 changes: 0 additions & 32 deletions docs/tools/batch/call-destructive-tools-batch.md

This file was deleted.

41 changes: 0 additions & 41 deletions docs/tools/batch/call-mutating-tools-batch.md

This file was deleted.

34 changes: 0 additions & 34 deletions docs/tools/batch/call-readonly-tools-batch.md

This file was deleted.

39 changes: 39 additions & 0 deletions docs/tools/batch/call-tools-batch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Call tools batch

```{fastmcp-tool} batch_tools.call_tools_batch
```

**Use when** you need an ordered workflow made from existing typed MCP
tools, such as renaming and splitting a known window, while preserving
each tool's own schema and safety checks.

**Avoid when** the steps are tmux pane or window operations; prefer the
typed {tooliconl}`run-tmux-plan` tool. For shell commands with completion
and output, prefer {tooliconl}`run-command`.

**Safety:** Each nested call still runs through the server's safety tier,
so the batch can never run a nested tool the tier hides. Set `max_tier` to
cap the batch below the server tier: `readonly` refuses any mutating or
destructive nested call, and `mutating` refuses destructive ones. The
default permits every tier the server already allows.

**Example:**

```json
{
"tool": "call_tools_batch",
"arguments": {
"operations": [
{"tool": "rename_window",
"arguments": {"window_id": "@2", "new_name": "logs"}},
{"tool": "split_window",
"arguments": {"window_id": "@2", "direction": "right"}}
],
"max_tier": "mutating",
"on_error": "stop"
}
}
```

```{fastmcp-tool-input} batch_tools.call_tools_batch
```
16 changes: 3 additions & 13 deletions docs/tools/batch/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,8 @@ including `socket_name` when needed.
::::{grid} 1 1 2 3
:gutter: 2 2 3 3

:::{grid-item-card} {tooliconl}`call-readonly-tools-batch`
Call readonly tools in order.
:::

:::{grid-item-card} {tooliconl}`call-mutating-tools-batch`
Call readonly or mutating tools in order.
:::

:::{grid-item-card} {tooliconl}`call-destructive-tools-batch`
Call readonly, mutating, or destructive tools in order.
:::{grid-item-card} {tooliconl}`call-tools-batch`
Call existing MCP tools in order, with an optional safety-tier cap.
:::

::::
Expand All @@ -25,7 +17,5 @@ Call readonly, mutating, or destructive tools in order.
:hidden:
:maxdepth: 1

call-readonly-tools-batch
call-mutating-tools-batch
call-destructive-tools-batch
call-tools-batch
```
23 changes: 23 additions & 0 deletions docs/tools/chain/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Chain tools

Chain tools run a typed list of tmux operations over a persistent tmux
control connection, one dispatch per operation, and return one typed
result per step. They are different from batch tools: batch tools call
existing MCP tools one by one, while chain tools take a typed tmux
operation list directly.

::::{grid} 1 1 2 3
:gutter: 2 2 3 3

:::{grid-item-card} {tooliconl}`run-tmux-plan`
Run a typed plan of tmux operations, one result per step.
:::

::::

```{toctree}
:hidden:
:maxdepth: 1

run-tmux-plan
```
Loading
Loading