Repository navigation
mcp(feat): Confirm kill_session, add pane viewer and icons - #149
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #149 +/- ##
==========================================
+ Coverage 86.81% 86.97% +0.16%
==========================================
Files 46 47 +1
Lines 3899 3947 +48
Branches 587 596 +9
==========================================
+ Hits 3385 3433 +48
Misses 354 354
Partials 160 160 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Nov 7, 2026
why: Nothing caught an unintended change to what clients receive for a tool: a reworded description, a schema field, a moved toolset tag. Each costs every agent session context, and none failed a test. what: - Add test_tool_surface_matches_snapshot, a syrupy JSON snapshot of every registered tool, disabled ones included, keyed by name with a short sha256 of its wire form and its size in bytes - Build the server in a subprocess with LIBTMUX_* scrubbed, since several of those variables reshape schemas at import - Regenerate after review with --snapshot-update
why: Hosts that show a server logo, such as VS Code, had none to show. what: - Declare the docs site's SVG mark and its 192px PNG on the FastMCP constructor; clients must render PNG and should render SVG - Use one mark for both themes, as the docs sidebar does - Host the images rather than embed them, so the wheel carries no image data and the handshake stays small - Test that both protocol eras receive both icons
why: On the 2026-07-28 protocol a tool that asks the user a question returns an InputRequiredResult and is called again with the answer. AuditMiddleware logged that first round as outcome=ok, identical to the round that performed the action, so the audit log could not tell "asked to kill" from "killed". what: - Log outcome=input_required when the result is fastmcp's InputRequiredToolResult - Test that such a round never records outcome=ok
why: kill_session ends every process in the session's panes, and the
server had no way to involve the user. MCP elicitation lets the server
ask, but the two protocol eras ask differently: 2025-11-25 sends a
request mid-call, 2026-07-28 returns the question and is called again.
what:
- Ask through ctx.elicit() on handshake-era connections and through an
InputRequiredResult on 2026-07-28 ones; a decline returns an error
result and leaves the session
- Keep the kill unconfirmed when the client declares no elicitation
capability, so non-eliciting clients behave as before
- Identify the target by #{pid}:#{start_time}:#{session_id}. tmux
numbers sessions from $0 on every server start, so a same-name
session recreated while the user is asked can carry the same id;
start_time has one-second resolution and pid tells the two servers
apart. The 2026-07-28 path carries the identity in request_state and
the handshake path rechecks it after the await
- Make kill_session async for the Context it needs; the wire schema
is unchanged, as the tool-surface snapshot confirms
- Test accept, decline, no capability, and a recreated target on both
eras
why: Hosts that render MCP Apps (Claude Desktop, VS Code) could show a pane's screen beside the conversation instead of a JSON blob, and keep it current while a command runs. what: - Add ui://libtmux-mcp/pane-viewer.html, a page that renders a snapshot_pane result as a terminal screen with the cursor marked and re-runs snapshot_pane every 2s while visible and "live" is checked - Link it from snapshot_pane through AppConfig; clients without the Apps extension ignore the link and receive the same result as before - Load the ext-apps SDK pinned at 2.0.3 from unpkg, the one origin the page's CSP admits - Pad rows out to the cursor row: snapshot content drops trailing blank rows, where the cursor often sits - Use fastmcp's low-level apps API, which needs no Prefab or fastmcp[apps] extra - Test the link, the resource's MIME type and CSP; the tool-surface snapshot records snapshot_pane's added _meta
why: sphinx-autodoc-fastmcp collects tools by calling each module's register() with a stand-in whose tool() accepts title, annotations, tags and meta only. The app= keyword on snapshot_pane raised a TypeError there, so the docs build skipped every pane tool. The build still exited 0, with a single warning. what: - Pass the viewer link as meta["ui"], the dict FastMCP's app= writes; clients receive the same tool, as the unchanged tool-surface snapshot confirms - just build-docs is warning-free again
why: The server gained a confirmation step on kill_session, an MCP Apps pane viewer and icons, and the docs still said every resource is a tmux:// template. what: - Add What's new entries for kill_session confirmation, the pane viewer and server icons, and a Development entry for the tool-surface snapshot - Document the ui:// pane viewer in the resources page, limiting the template and tmux-query statements to tmux:// resources - Note the confirmation on the kill-session page and the viewer on the snapshot-pane page
tony
added a commit
that referenced
this pull request
Nov 7, 2026
why: #149 made kill_session ask the user before killing, which left kill_window, kill_pane and kill_server, each as destructive, acting on the agent's word alone (#152). A tool that asks also exposed that call_read_tools_batch would report a nested question as a successful row with empty content (#150), and the prompting guide told agents to confirm only kill_session and kill_server themselves (#153). what: - Ask the user before kill_window, kill_pane and kill_server kill, on both protocol eras, as kill_session does. Each question names what would be lost: the window's name and id, the pane's id and running command, or the server's socket and session count. A decline returns an error and leaves the target in place. A client without the elicitation capability kills without asking, as before. - Share one confirmation flow in _utils.py. _confirm_teardown runs the two-era elicitation: ctx.elicit() on 2025-11-25 connections, and an InputRequiredResult carrying the target's identity in request_state on 2026-07-28 connections. _ask_before_teardown adds the decline error and rechecks the identity before the kill, and _tmux_identity reads #{pid}:#{start_time}:<id> from any tmux object. kill_session moves onto the same helper. - Key each tool's identity on its own id (#{window_id}, #{pane_id}), and kill_server on the server's #{pid}:#{start_time} alone, since it has no object id. A target replaced while the user is being asked, including a server restarted on the same socket, is left alone. - Leave every tool schema unchanged: FastMCP injects ctx and hides it from the schema, and the tool-surface snapshot does not move. - Fail a call_read_tools_batch row whose tool asks for user input, with a hint to call the tool directly, since a row has no channel back to the user. The batch admits only inspect tools and none of them ask today, so the guard covers the first one that does. - Pin with a test that the response limiter passes a question through as input-required. fastmcp's ResponseLimitingMiddleware returns an InputRequiredToolResult untouched, and the tail-preserving subclass rebuilds only results it trimmed. - Test accept, decline and client-cannot-ask on both eras for each of the three tools, and the batch refusal with its hint. - Add a Confirmation note to the kill-window, kill-pane and kill-server pages, name all four kill tools in the prompting guide's sample prompt with a note that its confirm-first sentence can be dropped in clients that ask through elicitation, and widen the CHANGES entry to all four tools.
This was referenced Nov 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kill_sessionon both MCP protocol eras.snapshot_paneresults.outcome=input_required.Changes by area
Teardown confirmation
src/libtmux_mcp/tools/session_tools.py:kill_sessionbecomes async and asks throughctx.elicit()on 2025-11-25 connections and through anInputRequiredResulton 2026-07-28 connections. A decline returns an error result and leaves the session in place.src/libtmux_mcp/tools/session_tools.py: the target is identified by#{pid}:#{start_time}:#{session_id}, carried inrequest_stateon 2026-07-28 and rechecked after the await on 2025-11-25.src/libtmux_mcp/middleware.py:AuditMiddlewarelogsoutcome=input_requiredwhen the result is fastmcp'sInputRequiredToolResult.Pane viewer
src/libtmux_mcp/resources/viewer.py:ui://libtmux-mcp/pane-viewer.htmldraws a snapshot as a terminal screen with the cursor marked, and re-runssnapshot_paneevery 2s while visible and "live" is checked.src/libtmux_mcp/tools/pane_tools/__init__.py:snapshot_panelinks the page throughmeta["ui"]. Clients without the Apps extension receive the same result as before.Server
src/libtmux_mcp/server.py: SVG and 192px PNG icons hosted on the docs site.Tests and docs
tests/test_server.py:test_tool_surface_matches_snapshot, a syrupy JSON snapshot holding a hash and byte size per tool, andtest_server_icons_reach_client.tests/test_session_tools.py: confirmation cases on both eras.tests/test_resources_integration.py,tests/test_middleware.py: the viewer link and CSP, and the audit outcome.docs/resources.md,docs/tools/session/kill-session.md,docs/tools/pane/snapshot-pane.md,CHANGES.Design decisions
$0on every server start, so killing the last session and creating one with the same name reuses the id, and#{start_time}has one-second resolution.meta["ui"], the dict FastMCP'sapp=writes.sphinx-autodoc-fastmcp's collector rejectsapp=and would drop every pane tool from the docs. The tool-surface snapshot shows the wire output is identical.fastmcp.appsAPI, so it adds no Prefab orfastmcp[apps]dependency. It loads the ext-apps SDK pinned at 2.0.3 from unpkg, the one origin its CSP admits.data:URIs, so the wheel carries no image data and the handshake stays small. One mark serves both themes, as in the docs sidebar.Test plan
kill_sessionaccept, decline, no elicitation capability, and target recreated while asking, on 2025-11-25 and 2026-07-28 (test_kill_session_confirmation)test_server_icons_reach_client)snapshot_panelinks the viewer, served astext/html;profile=mcp-appwith its CSP (test_pane_viewer_linked_from_snapshot_pane)outcome=ok(test_audit_middleware_marks_input_required)test_tool_surface_matches_snapshot)callServerToolin a browser host built on the ext-appsAppBridgeruff format --check,ruff check, andmypy .just build-docswith no warnings; pane tool pages and the pane viewer section render