Core Library0.xGuides

Choose documentation 2

latest

Current version

latest
English

Early release libtmux is released, and its APIs may still change between releases. This site's structure and URLs may change too.

Using the .NET MCP server

Use the separate LibTmux.Mcp .NET tool to let an MCP client inspect panes, wait for output and run commands in tmux. It uses the same .NET library as libtmux for PowerShell. It does not import the PowerShell modules or require a PowerShell launcher. Tool names link to the .NET MCP tool reference, which documents this server.

Install and select a server

Install the published 0.1.1 tool on the Unix host running tmux:

Terminal window
$ dotnet tool install LibTmux.Mcp \
--tool-path build/mcp \
--version 0.1.1 \
--framework net8.0

For shared .NET source changes, use the optional MCP development setup. A desktop-launched client may need DOTNET_ROOT in its server environment when .NET comes from a version manager.

For clients using an mcpServers configuration, replace these absolute paths with the installed tool, tmux executable and an existing server socket:

{
"mcpServers": {
"tmux": {
"command": "/absolute/tool-directory/libtmux-mcp",
"env": {
"LIBTMUX_TMUX": "/absolute/path/to/tmux",
"LIBTMUX_SOCKET_PATH": "/absolute/path/to/tmux/socket",
"LIBTMUX_TOOLSETS": "inspect"
}
}
}
}

Claude Code takes the same settings as -e flags and the executable after --:

Terminal window
$ claude mcp add tmux \
-e LIBTMUX_TMUX=/absolute/path/to/tmux \
-e LIBTMUX_SOCKET_PATH=/absolute/path/to/tmux/socket \
-e LIBTMUX_TOOLSETS=inspect \
-- /absolute/tool-directory/libtmux-mcp

Other clients use different configuration formats; see the .NET MCP client setup. Launch the executable directly: standard input and output carry MCP messages.

To name a socket instead of giving its path, replace LIBTMUX_SOCKET_PATH with LIBTMUX_SOCKET:

Terminal window
$ claude mcp add tmux \
-e LIBTMUX_SOCKET=work \
-e LIBTMUX_TOOLSETS=inspect \
-- /absolute/tool-directory/libtmux-mcp
  • To choose a socket, set LIBTMUX_SOCKET, a name, or LIBTMUX_SOCKET_PATH, an absolute path. With both, including when one is inherited, the path wins. A blank value stops the server, where a PowerShell handle ignores it. LIBTMUX_SOCKET_NAME selects nothing, and the server refuses to start with it alone.
  • With neither, the server drives the server tmux picks: the one TMUX names, otherwise your default socket. A client started from a tmux pane therefore works on the panes you are using. The server starts no server of its own and leaves teardown off, and tmux://capabilities reports the selector default. Set LIBTMUX_SOCKET to keep an assistant on a server of its own.
  • The choice is fixed at startup; calls cannot switch sockets.
  • Shut down the client connection when finished. The server leaves a borrowed tmux daemon running.

Discover before calling

After initialization:

  • Read the server instructions.
  • Call tools/list for schemas, descriptions and annotations. Each advertised tool also carries its capability row in _meta["com.git-pull.libtmux-mcp/capability"].
  • Read the tmux://capabilities resource. It identifies the selected socket, the effective tools and the observation policy.

Choose the tools your client sees:

  • The example selects inspect. Add manage, execute or teardown only for the operations your client needs, then restart it to change the selection.
  • Toolsets shape the interface; they are not an authorization boundary. Commands execute with the tmux user’s authority.
  • Tool selection has the full contract.
TaskStart with
Find a session, window or panelist_sessions, list_windows, list_panes
Read terminal textcapture_pane; snapshot_pane for structured state
Find text across panessearch_panes
Wait for text that has not appearedwait_for_text with a finite timeout
Read changes across later callscapture_since, retaining its returned cursor
Run your own command and observe its completionrun_shell_command from the execute toolset
Combine independent reads in one requestcall_read_tools_batch; its operations run serially

Know when a command is done:

  • Input acknowledgement does not prove a shell command finished.
  • Use the completion result of run_shell_command for a command you start. Cancellation or timeout can leave that command running in its pane.
  • For a program already running, wait for its output rather than repeatedly capturing the screen.

Read output without losing it:

  • On tmux 3.7 and newer, pane text waits require control observation by default. Older releases attach no control client and poll.
  • Setting LIBTMUX_MCP_ALLOW_POLLING_FALLBACK=true allows repeated captures if control observation fails. Capabilities disclose the policy and affected results report pollingFallback.
  • Give text waits a finite timeout. The server bounds their returned tail.
  • For captures, select maxLines. Check eventsDropped and the truncation fields before treating observed output as complete.
  • A capture_since cursor tracks observation between calls; it is not a background job.
  • Progress notifications and optional MCP Tasks are separate protocol features, available only where the discovered tool declares them.
  • Waits and captured output has the full contract.

To allow the polling fallback, add it to the server’s environment:

Terminal window
$ claude mcp add tmux \
-e LIBTMUX_SOCKET=work \
-e LIBTMUX_MCP_ALLOW_POLLING_FALLBACK=true \
-- /absolute/tool-directory/libtmux-mcp

Inspect a pane and wait for readiness

After discovery, select a session from list_sessions. These are tools/call parameter objects: replace $0 and %0 with IDs returned by your server. The inspect toolset includes all three calls.

List the selected session’s panes with list_panes:

{
"name": "list_panes",
"arguments": { "session": "$0" }
}

The response’s structuredContent.result contains pane records with paneId, windowId, sessionId, dimensions and the current command. Select one pane and read its rendered screen with capture_pane:

{
"name": "capture_pane",
"arguments": { "paneId": "%0", "maxLines": 64 }
}

For this object result:

If the application prints Service ready, wait for that whole line with wait_for_text:

{
"name": "wait_for_text",
"arguments": {
"paneId": "%0",
"patterns": ["^Service ready$"],
"timeoutSeconds": 1,
"ignoreCase": false
}
}

The result:

  • An existing line returns structuredContent.outcome = "PresentAtEntry"; a later line returns "Matched".
  • matchedPattern identifies the condition, and tail contains bounded rendered text.
  • Stop-pattern, timeout and pane-death outcomes remain distinct.
  • A ready-line match is an application-specific condition; it does not establish shell exit status or continuing health.

The official SDK client reads these three request examples, substitutes discovered IDs and executes them against an installed tool. Its owned fixture prints the ready line before the calls, then checks capture, PresentAtEntry, an untruncated tail, no event drops, polling only on tmux before 3.7, stdio shutdown and preservation of the borrowed tmux state.

Cancel with care:

  • Use a finite server timeout even when your client can cancel.
  • With the pinned SDK 2.2.0, cancelling a local token does not reliably send notifications/cancelled; it can leave the remote wait running until its timeout. A client that knows its request ID can send that notification itself.
  • For negotiated MCP Tasks, cancel the task with tasks/cancel using its returned task ID. Task records are temporary and disappear on expiry or restart.
  • Neither cancellation stops the application in the pane.

Check the client workflow from this checkout

After building the discovery probe, set MCP_COMMAND to the installed executable’s absolute path and MCP_VERSION to its exact version. Select the desired tmux on PATH, then run:

Terminal window
$ pwsh -NoLogo -NoProfile -File eng/Test.ps1 \
-Suite Mcp \
-McpCommand "$MCP_COMMAND" \
-McpVersion "$MCP_VERSION"

The official SDK client source initializes the installed tool, compares discovery with capabilities, then executes list_sessions and the three request examples above. The suite also:

  • starts the tool with every LIBTMUX_ setting that the claude mcp add commands and JSON configuration on this page and in the repository README show, and checks that capabilities report each setting;
  • checks that both socket variables together are refused;
  • checks that every tool name on those pages is a link to its tool page and that every linked tool exists on the installed server;
  • checks that the session and pane survive MCP shutdown, then removes the owned fixture.

It neither imports the PowerShell modules nor builds, installs or downloads anything during the test. Broader task, cancellation, loss and concurrent-wait acceptance belongs to the .NET protocol suite.

Esc

Type to search.

Reference previewOpen reference