Skip to content

Commit 7216f25

Browse files
committed
docs(CHANGES) Trim pre-release block to high-level deliveries
Replace the layered Features/Tests/Scripts/Docs/API decisions/Refactor/ Fixes block with three sub-section entries under What's new (one per shipped surface), three Documentation bullets, and one API-decisions bullet. Folds intra-branch corrections into the parent feature entries since they describe how the new surface ships, not separate deliveries. Drops the Refactor section (behavior-preserving). The introspection test that backstops the socket_name contract is internal infrastructure; the tightened wording is what users see.
1 parent 62a200a commit 7216f25

1 file changed

Lines changed: 19 additions & 164 deletions

File tree

CHANGES

Lines changed: 19 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -6,174 +6,29 @@
66
_Notes on upcoming releases will be added here_
77
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
88

9-
### Features
10-
11-
- {tooliconl}`respawn-pane` gains an ``environment`` parameter
12-
(``dict[str, str]``) that maps to tmux's ``respawn-pane -e
13-
KEY=VALUE`` flag (one ``-e`` per entry, single-arg ``-e<KEY>=<VAL>``
14-
form to mirror the upstream emitter). Closes the parity gap with
15-
``Pane.respawn(environment=)`` on libtmux's ``tmux-parity`` branch.
16-
The audit-log redaction policy is extended to recognise dict-shaped
17-
sensitive args: each value is replaced by a ``{len, sha256_prefix}``
18-
digest while keys (env var names like ``DATABASE_URL``) remain
19-
visible — keys are operator-debug-useful, values are the secret.
20-
Note: like ``shell``, env var values may briefly appear in the OS
21-
process table before the spawned shell inherits them; do not pass
22-
long-lived secrets when other tenants on the host could observe
23-
``ps``.
24-
25-
### Tests
26-
27-
- New ``test_registered_tools_accept_socket_name`` introspection test
28-
in ``tests/test_server.py`` enumerates every registered tool via
29-
``FastMCP.list_tools()`` and asserts each accepts a ``socket_name``
30-
parameter, with ``list_servers`` as the documented exception (it
31-
takes ``extra_socket_paths`` instead). Catches future tool drift
32-
before it silently makes ``_BASE_INSTRUCTIONS`` lie to agents. The
33-
``_BASE_INSTRUCTIONS`` text itself is tightened from "All tools
34-
accept an optional socket_name parameter" to "Targeted tmux tools
35-
accept an optional socket_name parameter (defaults to LIBTMUX_SOCKET
36-
env var); list_servers discovers sockets via TMUX_TMPDIR plus
37-
optional extra_socket_paths instead." A companion content test
38-
(``test_base_instructions_document_socket_name_contract``) locks the
39-
wording.
40-
41-
### Scripts
42-
43-
- ``scripts/mcp_swap.py``: ``use-local`` now preserves an existing
44-
entry's ``env`` dict on replacement. Previously a swap silently
45-
dropped client-side environment settings (``LIBTMUX_SAFETY``,
46-
``LIBTMUX_SOCKET``, custom dev knobs) because ``build_local_spec``
47-
returned a spec with empty env and ``set_server`` wrote it unchanged.
48-
The timestamped backup at ``.bak.mcp-swap-<ts>`` still captures the
49-
full original config for full recovery via ``revert``, but day-to-day
50-
swaps no longer require manual env restoration. The merge mirrors
51-
``_spec_from_entry`` (which round-trips env on the read side).
52-
Regression test:
53-
``tests/test_mcp_swap.py:test_use_local_preserves_existing_env_when_replacing``
54-
plus a paired ``test_use_local_with_no_prior_entry_writes_empty_env``
55-
to lock the Codex "added" path against accidental env synthesis.
56-
- ``scripts/mcp_swap.py``: ``_claude_project_node`` validates that
57-
Claude's undocumented ``projects.<abs>.mcpServers`` layout is still
58-
mapping-shaped before mutating it. If a future Claude release
59-
reshapes the structure, the script raises ``RuntimeError("Claude
60-
config layout appears to have changed...")`` *before* the atomic
61-
write — so the timestamped backup defense isn't asked to recover
62-
from a partial mutation. Three new tests in ``tests/test_mcp_swap.py``
63-
cover the rejection paths and the well-shaped happy path.
64-
- ``scripts/mcp_swap.py``: ``_claude_project_node`` now uses
65-
``@t.overload`` so ``create=True`` is statically narrowed to
66-
``dict[str, t.Any]``. ``set_server`` drops the runtime
67-
``assert node is not None`` (which would have been stripped under
68-
``python -O``) — mypy proves the invariant via the overload instead.
69-
Strict-typing-priority alignment.
70-
- ``scripts/mcp_swap.py`` PEP 723 ``requires-python`` lowered from
71-
``>=3.11`` to ``>=3.10`` to match the project floor in
72-
``pyproject.toml``. The script does not use any 3.11-only features
73-
(verified: no ``tomllib``, ``ExceptionGroup``, ``except*``, or
74-
structural ``match``); the previous bound made ``uv run scripts/
75-
mcp_swap.py`` provision a fresh 3.11 toolchain on contributor
76-
machines that otherwise share the project's 3.10 venv.
77-
78-
### Docs
79-
80-
- {ref}`safety` macOS ``TMUX_TMPDIR`` caveat now reflects shipped
81-
behaviour. ``_effective_socket_path`` already queries tmux's
82-
``display-message -p '#{socket_path}'`` first and only falls back to
83-
``$TMUX_TMPDIR`` reconstruction when the server is unreachable, but
84-
the doc still framed the structural fix as future work and told
85-
operators to set ``TMUX_TMPDIR`` explicitly. Replace with the actual
86-
three-step resolution order so doc readers don't write code or
87-
service files chasing a problem that's already solved.
88-
- Strip dangling internal-audit citations from production source
89-
comments. ``window_tools.py:106`` no longer references "the
90-
brainstorm-and-refine audit §7.1" — the architectural fence
91-
(the four-hierarchy-level rule) stays, the orphaned citation goes.
92-
``session_tools.py:74-76`` cross-reference still resolves. The
93-
``hook_tools.py`` module docstring drops "The brainstorm-and-refine
94-
plan deliberately excludes write-hooks" and keeps the rationale
95-
(hook persistence + lifespan teardown gap).
96-
- {tooliconl}`get-window-info` "Avoid when" guidance now points to
97-
{tooliconl}`list-windows` (not {tooliconl}`list-panes`) for whole-
98-
session window enumeration. The previous wording trained agents into
99-
the wrong tool: ``list_panes`` returns ``PaneInfo`` objects, while
100-
``list_windows`` is the window enumerator and accepts ``session_id``.
101-
- {tooliconl}`respawn-pane` docstring and topic page now include a tip
102-
to call {tooliconl}`get-pane-info` first if the agent needs to
103-
preserve ``pane_current_command`` across the respawn — tmux's default
104-
behaviour is to replay the original argv, but a custom split-time
105-
shell may differ.
106-
- {ref}`safety` adds a {tooliconl}`respawn-pane` subsection under
107-
"Footguns inside the `mutating` tier" alongside {tooliconl}`pipe-pane`
108-
and {tooliconl}`set-environment`. Documents the `kill=True` default,
109-
the non-idempotent retry semantics, the explicit-`pane_id`
110-
requirement, and the `pane_current_command` / OS-process-table
111-
visibility window for any `shell` argument. The per-tool annotation
112-
table picks up a `respawn-pane` row showing the unusual mutating-tier
113-
+ `destructiveHint=true` + `idempotentHint=false` combination.
9+
### What's new
11410

115-
### API decisions (pre-release)
11+
#### New tool: `respawn_pane`
11612

117-
- {tooliconl}`respawn-pane` parameter ``shell_command`` is renamed to
118-
``shell`` to align with {tooliconl}`split-window` and the upstream
119-
``Pane.respawn(shell=)`` signature on libtmux's ``tmux-parity``
120-
branch. Settled before the tool reached ``origin/main`` so no
121-
compatibility alias is shipped — alignment is the load-bearing
122-
reason, not a fix to a previously released name.
123-
- {tooliconl}`respawn-pane` signature drops the optional
124-
``session_name`` / ``session_id`` / ``window_id`` resolver fallbacks
125-
that sibling pane tools accept. The runtime guard would have raised
126-
on any combination missing ``pane_id`` anyway (the tool requires
127-
explicit ``pane_id`` to prevent silent-kill-via-resolver), so the
128-
parameters were dead surface area broadcasting targeting flexibility
129-
to LLMs that did not exist. Validation now lives at the FastMCP
130-
schema boundary via ``pane_id: str`` rather than an in-tool
131-
``ToolError`` raise. Settled before the tool reached ``origin/main``
132-
so no compat alias ships.
133-
134-
### Refactor
135-
136-
- Drop redundant ``["-t", pane.pane_id]`` argument prefixes at five
137-
``pane.cmd(...)`` sites (`pane_tools/lifecycle.py:132`,
138-
`copy_mode.py:58, 110`, `meta.py:64, 150`). libtmux's ``Pane.cmd``
139-
already injects ``-t self.pane_id`` via ``Server.cmd``, so the
140-
resulting wire form was ``tmux <verb> -t %X -t %X ...``; tmux's args
141-
parser kept the last ``-t`` so behaviour was identical, but the
142-
redundancy was confusing slop. The convention exemplar at
143-
`copy_mode.py:60-66` (``pane.cmd("send-keys", "-X", ...)``) shows the
144-
intended call shape.
13+
Restart a wedged pane in place — preserves `pane_id` and the window layout (the alternative `kill_pane` + `split_window` invalidates pane references and rearranges the window). Required `pane_id`; optional `kill` (default true), `shell`, `start_directory`, `environment`. Refuses to respawn the MCP server's own pane, mirroring the existing `kill_*` self-guards. Annotations honestly mark it `destructiveHint=true` / `idempotentHint=false` while staying in the `mutating` tier so default-profile agents can use it for shell recovery. Audit log redacts the `shell` argument and digests `environment` values (keys stay visible). (#27)
14514

146-
### Fixes
15+
#### New tools: `get_session_info`, `get_window_info`
16+
17+
Targeted single-object metadata reads — no more `list_*` + filter when you have an ID. Completes the four-level `get_*_info` hierarchy (server / session / window / pane); buffers, hooks, and options have existing read paths and are deliberately excluded. (#27)
18+
19+
#### New dev script: `scripts/mcp_swap.py`
20+
21+
Point Claude / Codex / Cursor / Gemini at a local checkout in one command. `just mcp-use-local` rewrites each CLI's global config to run the local repo via `uv`; `just mcp-revert` restores from a timestamped backup. `just mcp-detect` and `just mcp-status` report install state. Atomic writes, `--dry-run` mode, per-CLI state file, env preservation on replacement, layout-shape guard before mutating Claude's per-project config. Scope is global configs only — see `scripts/README.md`. (#27)
22+
23+
### Documentation
24+
25+
- {ref}`safety` adds a {tooliconl}`respawn-pane` "Footgun" subsection alongside {tooliconl}`pipe-pane` and {tooliconl}`set-environment`: `kill=true` default, non-idempotent retries, explicit-`pane_id` requirement, OS-process-table visibility for `shell` / `environment`. The macOS `TMUX_TMPDIR` caveat is rewritten to reflect shipped behaviour — tmux's three-step socket-path resolution is documented, so operators no longer need to set `TMUX_TMPDIR` explicitly to chase a problem that's already solved. (#27)
26+
- LLM-facing discoverability tightened: {tooliconl}`display-message` retitled "Evaluate tmux Format String" with a docstring that leads with read-only format expansion (the tool wraps `display-message -p` but `-p` expands rather than displays), {tooliconl}`pipe-pane` leads with the `/tmp/pane.log` logging use case, and the server instructions explain why hooks are read-only and why there is no `list_buffers` tool. The `socket_name` contract is tightened to acknowledge {tooliconl}`list-servers` as the documented exception. (#27)
27+
- Topic pages added for {tooliconl}`respawn-pane`, {tooliconl}`get-session-info`, and {tooliconl}`get-window-info`. (#27)
28+
29+
### API decisions (pre-release)
14730

148-
- Audit log now redacts the ``shell`` argument on
149-
{tooliconl}`respawn-pane` (and ``content`` on {tooliconl}`load-buffer`,
150-
which the code already redacted but the docs did not list). The
151-
``shell`` payload may carry credentials passed to a relaunched
152-
process; redacting the MCP audit log keeps them out of long-lived
153-
log archives. Note: ``shell`` may still appear briefly in the OS
154-
process table and tmux's ``pane_current_command`` metadata until the
155-
spawned shell takes over — do not pass credentials directly even
156-
with redaction.
157-
- {tooliconl}`respawn-pane` now requires an explicit ``pane_id``. Its
158-
signature still accepts ``session_name`` / ``session_id`` /
159-
``window_id`` for backwards-compatibility with the shared pane-target
160-
resolution surface, but a runtime guard raises before
161-
``_resolve_pane`` is invoked. Without the guard, calling
162-
``respawn_pane(session_name="dev")`` resolved through ``_resolve_pane``
163-
to the first pane of the first window — combined with default
164-
``kill=True`` that could silently kill a critical running process
165-
(e.g. an `npm run dev` server) instead of the intended wedged shell.
166-
Resolve the target via {tooliconl}`list-panes` first.
167-
- {tooliconl}`respawn-pane` now advertises honest MCP annotations.
168-
Previously the tool inherited ``ANNOTATIONS_MUTATING`` defaults
169-
(`destructiveHint=False`, `idempotentHint=True`) even though its
170-
default `kill=True` sends `SPAWN_KILL` to the running process and
171-
repeated calls kill repeated processes. The new
172-
`ANNOTATIONS_MUTATING_DESTRUCTIVE` preset keeps the tool in
173-
`TAG_MUTATING` (so it stays visible to default-profile agents for
174-
shell recovery) while exporting `destructiveHint=True` and
175-
`idempotentHint=False`. Agents reading the annotations can no longer
176-
conclude that respawn-retries are safe.
31+
- {tooliconl}`respawn-pane` settles on `shell` (renamed from `shell_command`) to align with {tooliconl}`split-window` and upstream `Pane.respawn(shell=)`, and drops the `session_name` / `session_id` / `window_id` resolver fallbacks — the runtime guard rejected any call missing `pane_id` anyway. Validation now lives at the FastMCP schema boundary. (#27)
17732

17833
## libtmux-mcp 0.1.0a3 (2026-04-19)
17934

0 commit comments

Comments
 (0)