0.1.0-alpha.2
Pre-releaseAdded
- Waiting that is driven by tmux rather than by a timer.
Server.waitForOutput(in:matching:stoppingAt:requiringFreshOutput:timeout:tailLimit:)
blocks on a pane's%outputand matches against the rendered grid, so a
quiet pane costs nothing while it waits. The condition is checked before it
is blocked on — a pattern already showing returns at once with
matchedAtEntryset, because "wait until it is listening" is answered by
something already listening;requiringFreshOutputis the opposite reading,
for re-running a command whose output looks identical. Each read takes a
bounded lookback above the visible rows, so output that scrolls past between
two reads is still found.FormatSubscriptionand
ControlSession.watch(_:)register arefresh-client -Bsubscription, which
reports a format's value changing without reading any scrollback at all —
#{pane_current_command}answers "is my command done?" exactly.
A newWaitingarticle in the DocC catalogue covers which to reach for. LibTmuxMCPgrew from six tools to twenty-five, and gained the surfaces an
MCP client expects:tmux://resources, workflow prompts, server
instructions, per-tool JSON Schema with behaviour annotations,
structuredContent, and protocol-revision negotiation from2024-11-05
through2025-11-25.- Safety tiers on the MCP server.
LIBTMUX_SAFETYselectsreadonly,
mutatingordestructive, and anything above the tier is hidden from
tools/listas well as refused. - The MCP server recognises the pane it is running in.
list_panesmarks it,
describe_servernames it, and the kill tools refuse it unless
confirm_selfis passed. Identity is the tmux server's process id rather
than its socket path, so a pane id repeated on another tmux is not mistaken
for the caller's. apply_workspace,snapshot,run_shell,run_commands,search_panes,
capture_paneanddescribe_serveras MCP tools.
Changed
-
Thirteen more MCP tools, chosen to close the gaps an agent actually hits:
list_servers(the one question no other tool can answer),show_options,
show_environmentandshow_hooks(reading configuration, where only
writing it existed),rename,select,resize_pane,select_layout,
respawn_pane,paste_text(text that must not be read as key names),
set_environmentandkill_server. -
TmuxServers.discover(in:tmuxExecutable:)finds the tmux servers running on
a machine. A socket file is not a server — tmux leaves the file behind when
it exits — so each candidate is asked whether it answers. -
Server.tmuxExecutableandServer.shellInvocation, for a consumer that
must spawn or compose a tmux command reaching this same server. -
Server.capture(_:since:limit:)and thecapture_sincetool read only what
a pane has printed since a cursor, so watching something across turns stops
re-sending the screen. The cursor remembers what the last row said as well as
where it was, because a row rewritten in place — a spinner, a progress bar —
is new content at an old position. A respawned pane is reported rather than
read as a continuation of the program it replaced. -
MCP tools declare an
outputSchemawherever the answer's shape is
guaranteed, and listings answer under a name —{"panes": [...]}rather
than a bare array. MCP typesstructuredContentas an object, so an array
was not a result a validating client had to accept. The schemas are checked
against what the tools really return rather than against each other. -
Long MCP calls report progress when the client asks for it with a
_meta.progressToken, which the Codex CLI sends on every call. -
ControlSession.notificationshands every observer its own stream. It was a
singleAsyncStream, and two iterators of one of those divide the elements
rather than each receiving all of them — so a waiter and a watcher on the
same connection each silently missed about half of what they asked for.
Anything that arrived before the first observer is replayed to it. -
The MCP server serves requests concurrently, and honours
notifications/cancelled. It read one line, answered it, and only then read
the next, so a single blocking call stopped everything — including the
pingthat would have shown it was alive. -
Every MCP wait is clamped to a ceiling
(LIBTMUX_MCP_WAIT_MAX_SECONDS, itself capped at 300 seconds) and reports
the value actually enforced. -
MCP tools reject an argument they do not declare, naming the ones they
accept. Silently ignoring a misspeltpatternmade a wait look like a quiet
pane. -
run_commandrefuses the tmux commands that cannot return without a
terminal —wait-for,attach-session,command-prompt,choose-*— and
names the tool that does the same job safely.
Fixed
read_formatcould not be called over MCP. The protocol layer built its
request by naming the arguments it carried and did not carrytemplateor
target, so every call failed as though the client had sent nothing. Tool
arguments now travel as one object and are read through the same declaration
that generates the schema, which is what makes the two impossible to
disagree.