Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
86ddba6
mcp(fix[spawn]): Resolve start_directory in Python
tony Aug 29, 2026
2253444
mcp(fix[search]): Bound search_panes match time
tony Aug 29, 2026
42ad48b
mcp(fix[hints]): Typed input is not additive
tony Aug 29, 2026
93f70c4
mcp(fix[hints]): Spawning a pane reaches past tmux
tony Aug 29, 2026
d29b9dc
mcp(fix[hints]): Replacing a value is not additive
tony Aug 29, 2026
748648f
mcp(fix[hints]): Pane text is open-world
tony Aug 29, 2026
2ea63fd
docs(topics[safety]): Gate the hint table
tony Aug 29, 2026
471ea02
docs(topics[architecture]): Name the fourth hint
tony Aug 29, 2026
48e6047
mcp(fix[spawn]): Escape tmux hash runs
tony Aug 29, 2026
4dd6d9c
mcp(fix[format]): Escape every name tmux expands
tony Aug 29, 2026
1298466
docs(tools): State the new contracts
tony Aug 29, 2026
35011a6
test(hints): Read a surface no env var can hide
tony Aug 29, 2026
c2d2a9d
mcp(fix[hints]): A wait-for channel is consumed
tony Aug 29, 2026
9fff401
docs(_utils): Attach each preset's comment to it
tony Aug 29, 2026
16779af
mcp(fix[format]): Validate the display format
tony Aug 29, 2026
40d849b
docs(display-message): Say what capability went
tony Aug 29, 2026
9c288c0
mcp(fix[hints]): Stored values run later
tony Aug 29, 2026
3cda504
mcp(fix[search]): Bound the pattern too
tony Aug 29, 2026
6b8fce4
test(format): Pin the escaper's boundary
tony Aug 29, 2026
46041fb
mcp(feat[toolsets]): Replace the tier ladder
tony Aug 29, 2026
6f9e243
mcp(feat[toolsets]): Retire the tiers tree-wide
tony Aug 29, 2026
66f0d1d
mcp(feat[toolsets]): Gate the old words out
tony Aug 29, 2026
d816d94
test(docs): Follow the retargeted refs
tony Aug 29, 2026
13e018a
docs(MIGRATION): Map the tiers onto the toolsets
tony Aug 29, 2026
fdaa2c6
docs(toolsets): Declare the vocabulary the badges need
tony Aug 29, 2026
b30adaa
docs(toolsets): Give each toolset a badge tone
tony Aug 29, 2026
dc1018b
mcp(fix[hints]): Treat tmux calls conservatively
tony Aug 30, 2026
a346770
mcp(fix[retry]): Run failed calls once
tony Aug 30, 2026
1ea073a
mcp(fix[lifespan]): Leave tmux buffers alone
tony Aug 30, 2026
17a820c
mcp(fix[toolsets]): Validate the wire surface
tony Aug 30, 2026
2953c38
mcp(fix[prompts]): Keep adapters visible
tony Aug 30, 2026
8314b1a
test(respawn): Wait for child exec
tony Aug 30, 2026
7a7f33e
docs(trust): Define the execution boundary
tony Aug 30, 2026
f06185c
test(toolsets): Ratchet retired names
tony Aug 30, 2026
fad85bd
docs(trust): Badge the toolset inventory
tony Aug 30, 2026
ca0eb79
mcp(fix[search_panes]): Exclude capture time
tony Aug 30, 2026
fec46cc
mcp(fix[display_message]): Accept hyphenated names
tony Aug 30, 2026
b35d955
mcp(fix[batch]): Honor named wrapper authority
tony Aug 30, 2026
ceabd51
docs(fix[redirects]): Preserve read batch URL
tony Aug 30, 2026
5d2d4ed
docs(mcp): Name the toolset grouping as an axis
tony Aug 30, 2026
f640a93
ci(docs): Invalidate every path after a full sync
tony Aug 30, 2026
784a3ea
py(deps[docs]) gp-sphinx packages to 0.1.0a38
tony Aug 30, 2026
d6e88e7
mcp(fix[mcp_swap]): Reject retired safety env
tony Aug 30, 2026
7150af9
docs(CHANGES): Capability toolsets and trust
tony Aug 30, 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
10 changes: 5 additions & 5 deletions .agents/skills/testing-mcp-with-cli-agents/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ tool list, a couple of representative calls, an error path. Use this to answer
"is the tool surface and result shape correct?" before spending a CLI on it.

Shape-normalization gotchas seen in practice — normalize before asserting:
- Match the current `LIBTMUX_SAFETY` tier: destructive-batch wrappers are hidden
at the default tier, so don't assert they're visible.
- Match the current `LIBTMUX_TOOLSETS` selection: excluded toolsets are hidden
when omitted from the selection, so don't assert they're visible.
- List-returning tools surface `structuredContent` as `{"result": [...]}`;
`capture_pane` output can come back under `result` as a string. Don't assume a
top-level `count`.
Expand All @@ -75,7 +75,7 @@ handshake, codex's `mcp get` only parses config, and agy has no proof short of a
model call. `references/cli-matrix.md` has the verified per-CLI invocation,
isolation lever, and approval-bypass flag for all six. Two things that surprise
people: some `mcp list`/`list-tools` subcommands read the *ambient* config and
ignore your isolated one, and a mutating tool call needs a per-CLI
ignore your isolated one, and a write-capable tool call needs a per-CLI
approval-bypass flag or it hangs on a no-TTY prompt. Flags drift — re-verify with
`--help`.

Expand Down Expand Up @@ -230,7 +230,7 @@ not add new keys, so inject `LIBTMUX_SOCKET=mcp-target` via each CLI's native
real CLI configs, so dry-run first, record the pre-existing swap state, and
revert only what you swapped.

**Prefer zero-mutation isolation for a test.** mcp_swap is for a swap you *want*
**Prefer no-config-write isolation for a test.** mcp_swap is for a swap you *want*
to persist. To just exercise a checkout, use each CLI's throwaway config-home /
project-config lever instead — `references/cli-matrix.md` gives the verified one
per CLI (codex `CODEX_HOME` or `-c` overrides, grok `GROK_HOME`, agy
Expand Down Expand Up @@ -275,7 +275,7 @@ Claude, which has two layers) and preview with `--dry-run`. State is keyed by
`(cli, scope)`, so two swaps of the same layer collapse into one entry — there
is no chain to unwind one step at a time. Re-run `status`/`doctor` and compare
against the state you recorded before starting. If you stayed on the
zero-mutation path there is nothing to revert.
no-config-write path there is nothing to revert.

## When NOT to reach for the full harness

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ below.
the CLI inherits them and launches the `uv` server fine. The alternate-socket-pane
PATH gap (a `-L` pane's non-login shell lacks the mise shims) only bites when you
launch a CLI **TUI inside a harness pane** (Layer 2).
7. **Non-interactive mutating tool calls need an approval-bypass flag** — different per
CLI (table). Without it, a mutating call blocks on an approval prompt with no TTY
7. **Non-interactive write-capable tool calls need an approval-bypass flag** — different per
CLI (table). Without it, a write-capable call blocks on an approval prompt with no TTY
and the harness hangs.
8. **Interactive send-keys submit:** send the prompt text and `Enter` as **separate
`send-keys` events** — then a single Enter submits. The "needs a double Enter"
Expand Down
16 changes: 8 additions & 8 deletions .github/WRITING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The most useful editing operation is deleting the introductory sentence.
Lead with verbs and name concrete things. Put identifiers in backticks.
Prefer short declarative sentences, one operational fact each. Do not
explain Python to Python developers; do explain this project's semantics —
what a safety tier does, what a tool will and will not touch, what a stale
what a toolset does, what a tool will and will not touch, what a stale
pane object means.

Type annotations describe shape. Documentation describes meaning. A
Expand Down Expand Up @@ -77,7 +77,7 @@ Rules that follow:
common call, then the one argument a few will tune, then the lower-level
primitive. Each step is for a smaller audience than the last.
- **Name the trade-off.** If a call costs something — an extra tmux
round-trip, a stale object needing a refresh, a wider safety tier — say
round-trip, a stale object needing a refresh, a wider tool surface — say
so, and say what it buys. State it; do not sell it.

## README
Expand Down Expand Up @@ -164,7 +164,7 @@ its title is chrome, not disambiguation. The title is not part of the
search corpus above; it is a human-readable label only.

**`anthropic/alwaysLoad` is a scarce hint, not a default.** A handful of
high-traffic, read-only tools (list/inspect operations a session usually
high-traffic `inspect` tools (list/inspect operations a session usually
starts with) carry this per-tool `meta` flag so a client can keep a small
tmux vocabulary visible without preloading every tool's schema. Reserve it
for tools nearly every session needs early; adding it broadly defeats the
Expand All @@ -187,7 +187,7 @@ recovery suggestion rather than folding it into the sentence — "call
model has to parse for the actionable part. Reserve the loud, unrecoverable
category (a missing `tmux` binary, a genuine bug in this server) for
failures an operator — not the calling agent — has to fix; an
agent-correctable failure like a bad id or a tier denial should not read as
agent-correctable failure like a bad id or a toolset denial should not read as
loud as a crash.

## Documentation site voice
Expand Down Expand Up @@ -226,12 +226,12 @@ answer the operator's questions:
- **Use when** describes the practical workflow.
- **Avoid when** names the common wrong turn and points to the better
tool.
- **Side effects** states the safety consequence plainly.
- **Side effects** states the operational consequence plainly.
- **Examples** stay copyable, minimal, and realistic.

### What stays precise

Warm the framing, never the facts. Safety tiers, exact tool names,
Warm the framing, never the facts. Toolsets, exact tool names,
parameter names, environment variables, error strings, tmux targets,
format strings, JSON/TOML examples, and class or function
cross-references carry meaning in their exact form. Leave them exact and
Expand All @@ -244,12 +244,12 @@ of any symbol that has a useful destination on that page:

- `{class}`, `{meth}`, `{func}`, `{mod}`, `{exc}`, `{attr}` — Python
objects.
- `{tool}` — code chip + full safety badge (text + icon). Use in headers,
- `{tool}` — code chip + full toolset badge (text + icon). Use in headers,
bulleted lists, and tables where the badge gives scannable context.
- `{tooliconl}` — code chip + small colored icon (left). Use in inline
paragraph text where the full badge is too heavy.
- `{toolref}` — code chip only, no badge. Use for dense inline sequences
or where the safety tier is already established.
or where the toolset is already established.
- `{tooliconil}` / `{tooliconir}` — bare emoji inside a code chip. Use for
compact lists and scan-heavy surfaces.
- `{ref}` / `{doc}` — documentation pages and section anchors.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
run: |
aws cloudfront create-invalidation \
--distribution-id "${{ secrets.LIBTMUX_MCP_DOCS_DISTRIBUTION }}" \
--paths "/index.html" "/objects.inv" "/searchindex.js"
--paths "/*"

- name: Purge cache on Cloudflare
if: env.PUBLISH == 'true'
Expand Down
10 changes: 6 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ what was asked for.
| Path | What it is |
| ---- | ---------- |
| `src/libtmux_mcp/server.py` | FastMCP instance: construction, instructions, lifespan |
| `src/libtmux_mcp/middleware.py` | Safety-tier gating, response limiting, error mapping |
| `src/libtmux_mcp/middleware.py` | Toolset gating, response limiting, error mapping |
| `src/libtmux_mcp/_utils.py` | Server cache, object resolvers/serializers, `handle_tool_errors` |
| `src/libtmux_mcp/models.py` | Pydantic models for tool outputs |
| `src/libtmux_mcp/tools/` | MCP tool implementations: one module per tmux object, plus batch/buffer/hook/wait_for |
Expand Down Expand Up @@ -49,9 +49,11 @@ be stated twice, the file listed above is the one that governs.
- A passing gate is evidence only once it has been shown capable of
failing. Pair a new test with a deliberate break that proves it bites.

Tools are tagged `readonly`, `mutating`, or `destructive`; `LIBTMUX_SAFETY`
caps which tier is exposed (default `mutating`), and the middleware denies
any tool whose tag it does not recognize. All tmux access goes through
Tools are grouped into the unordered toolsets `inspect`, `manage`,
`execute`, and `teardown`; `LIBTMUX_TOOLSETS` selects which are exposed
(default `inspect,manage,execute`), and the middleware refuses any tool
carrying none of them. Filtering shapes what is advertised, not what a
pane can run. All tmux access goes through
libtmux's `cmd()` on `Server`/`Session`/`Window`/`Pane`, returning a
`CommandResult` with `stdout`/`stderr`; a libtmux object can go stale when
tmux state changes externally, so call `.refresh()` before trusting a
Expand Down
Loading