Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e219425
py(deps[libtmux]) Pin to sibling chainable-commands worktree
tony Jun 20, 2026
28fce41
mcp(feat[chain]): one-dispatch tmux command chains
tony Jun 20, 2026
054411e
mcp(feat[chain]): forward-layout pane id capture
tony Jun 20, 2026
bd4329c
Middleware(test[logs]): Tolerate propagation
tony Jun 20, 2026
005fd47
py(deps[libtmux]): Pin public chain commit
tony Jun 20, 2026
55d85e9
Chain(feat[tools]): Add typed op compiler
tony Jun 20, 2026
cffc57e
Chain(feat[tools]): Fold marked split refs
tony Jun 20, 2026
73a1462
Chain(docs): Document operation chains
tony Jun 20, 2026
61ca7e2
Chain(fix[tools]): Use chain metadata
tony Jun 20, 2026
5545e3d
Chain(feat[tools]): Add dry-run plans
tony Jun 20, 2026
35910c6
Chain(fix[tools]): Continue dry-run plans
tony Jun 20, 2026
970c2e3
Chain(feat[tools]): Bound dispatch waits
tony Jun 20, 2026
c9bbf5d
Chain(test[tools]): Cover error paths
tony Jun 20, 2026
ed772a5
Chain(feat[tools]): Roll back panes
tony Jun 20, 2026
8ab51da
py(deps): Pin libtmux control runner
tony Jun 21, 2026
05365ed
Chain(feat[tools]): Default control transport
tony Jun 21, 2026
240a266
Chain(fix[tools]): Pin split pane directory
tony Jun 21, 2026
ab44cef
py(deps): Pin libtmux review fixes
tony Jun 21, 2026
82f634a
Chain(refactor[tools]): Run each operation over control
tony Jun 21, 2026
f97abca
Chain(feat[tools]): Type each operation result by kind
tony Jun 21, 2026
5ad5e56
Chain(feat[tools]): Take a typed pane target
tony Jun 21, 2026
32505d7
Chain(refactor[tools]): Rename run_tmux_operations to run_tmux_plan
tony Jun 21, 2026
4375c63
Chain(feat[tools]): Add typed layout operations
tony Jun 21, 2026
ba93302
Chain(feat[tools]): Gate a typed kill_pane on the destructive tier
tony Jun 21, 2026
d8f12f8
Chain(feat[tools]): One generic batch tool and a plan history flag
tony Jun 21, 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,15 +6,50 @@
_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.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 @@ -119,6 +119,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 @@ -131,6 +132,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 @@ -161,6 +163,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`

[Browse all tools →](tools/index)

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