MCPconceptGuides

Choose documentation 3

v0.1.0.alpha.3

Current version

v0.1.0.alpha.3
English

Prerelease This site documents an alpha of libtmux. Its structure, URLs and APIs are subject to change.

MCP server guide

libtmux-mcp 0.1.0.alpha.3 · Source

Expose an existing tmux server over MCP stdio. Read snapshots, capture pane output and wait for events, or enable creation, input and shell commands one tool at a time.

API reference · Tools · Guides · RubyGems

Part of libtmux for Ruby. Built on libtmux and libtmux-async.

An MCP client starts libtmux-mcp and talks to it over stdin and stdout. The official MCP SDK handles the protocol, and bounded Async tasks handle transport.

Contents — Quick start · Connect a client · Choose the server · The tools · Read · Create · Run · Embed · Leaves out · Support · Documentation

Quick start

Install the alpha and its libtmux-mcp executable. It needs Ruby 3.3 or newer and tmux 3.3a or newer:

Terminal window
$ gem install libtmux-mcp --pre

The server borrows a tmux server that is already running. Start one on a socket of its own:

Terminal window
$ tmux -L agent new-session -d -s work

Add the server to Claude Code. Until you enable more tools, it serves tmux_capabilities and tmux_snapshot:

Terminal window
$ claude mcp add tmux -- libtmux-mcp --socket-name agent --endpoint dev

Run by hand, libtmux-mcp waits for input until EOF. libtmux-mcp --help lists every option.

Connect a client

claude mcp add: Claude Code

The command in the quick start registers the server with Claude Code. Start the tmux server first, as above.

codex mcp add: Codex CLI

Codex CLI takes the same command after --:

Terminal window
$ codex mcp add tmux -- libtmux-mcp --socket-name agent --endpoint dev

mcpServers: clients that read JSON

A client that reads a JSON configuration, such as Claude Desktop’s claude_desktop_config.json, takes the same command as an mcpServers entry:

{
"mcpServers": {
"tmux": {
"command": "libtmux-mcp",
"args": ["--socket-name", "agent", "--endpoint", "dev"]
}
}
}

If the client does not start servers with your shell’s PATH, set command to the absolute path that command -v libtmux-mcp prints.

Choose which tmux server it serves

--socket and --socket-name: pick the daemon

Set TMUX_SOCKET to an existing tmux socket. This command borrows that daemon and serves MCP on stdin and stdout:

Terminal window
$ libtmux-mcp \
--socket "$TMUX_SOCKET" \
--endpoint local

Use --socket-name NAME instead of --socket PATH to select a named socket, as the quick start does:

Terminal window
$ libtmux-mcp \
--socket-name agent
  • EOF retires the clients the server owns and leaves the daemon running.
  • --endpoint sets the public alias in discovery and resource URIs, and defaults to local.

The tools

Each tool name links to its page on libtmux.org, with the arguments, limits and result schema. Repeat --enable-tool for each additional tool.

ToolsDefaultPurpose
tmux_capabilities, tmux_snapshotEnabledDiscover capabilities and query captured metadata
tmux_capture, tmux_waitDisabledCapture a screen or wait for text/process exit
tmux_create, tmux_send, tmux_closeDisabledCreate entities, send text/keys and tear down exact targets
tmux_runDisabledRun a script in an explicitly enrolled zsh shell
  • Disabled tools are absent from discovery and denied on direct application calls.
  • An unknown tool name is refused when the server starts.

The complete protocol recipe exercises discovery, snapshots, default denial, enabled mutations, cancellation and EOF cleanup through actual pipes, including the installed executable.

Read the layout and the screen

tmux_snapshot: the layout, in pages

  • Pages come from one immutable capture, and a cursor continues that capture.
  • An expired, evicted or foreign cursor returns an error. The server never substitutes a fresh listing.

tmux_capture: a pane’s screen

Enable the screen tools together:

Terminal window
$ libtmux-mcp \
--socket "$TMUX_SOCKET" \
--enable-tool tmux_capture \
--enable-tool tmux_wait
  • It reads a bounded screen and keeps its line endings. Bytes that are not valid UTF-8 come back base64-encoded.
  • With tracking, a call returns a cursor, and later calls return a splice against that exact captured state. A cursor from another server generation or from a respawned pane is refused.
  • It refuses a pane whose effective after-capture-pane hook is not empty, including inherited entries.
  • On tmux 3.3a to 3.4 the hook is checked before the capture command runs, so keep hook configuration stable while observing.

tmux_wait: wait for text or exit

  • It waits for screen text, or for the pane process to exit. Screen text comes from control events on tmux 3.7 and newer, and from polling the pane every 50 ms on older daemons, which take no control client. Process exit uses process descriptors.
  • Wrapped rows are joined, so text that the pane width split across rows still matches.
  • If the pane, or the tmux server holding it, ends before the text appears, the call fails with pane_died, delivery: observed and the last rows it read in the error’s capture. If only the control stream ends while the pane lives, such as after the client detaches, the failure is observation_lost.
  • A cancelled or expired wait closes its observers and never signals the pane program.
  • Process tracking needs a native identity backend: Linux peer pidfds with matching PID namespaces, or Darwin kqueue. Without one, the call is refused.

Resource templates: the same data as URIs

tmux_metadata, tmux_metadata_page and tmux_screen expose metadata pages and pane screens under endpoint and generation URIs, with the same policy and limits as their tools.

Create, type and close

tmux_create: new sessions, windows and panes

Enable the three tools together:

Terminal window
$ libtmux-mcp \
--socket-name agent \
--enable-tool tmux_create \
--enable-tool tmux_send \
--enable-tool tmux_close
  • It takes argument arrays as argv.
  • Results carry the references tmux returned.

tmux_send: literal text or named keys

  • Text and keys are separate variants.
  • It reports dispatch_only: the input was dispatched, and the command has not necessarily finished.
  • After a cancellation, the effect stays unknown and is not reported as none.

tmux_close: an exact target

  • It takes an exact session, window or pane reference, and never selects by name.

Run a script in a pane

tmux_run: authored commands in an enrolled zsh

tmux_run runs a POSIX script in a pane’s interactive zsh 5.9. It needs both the tool and an enrolled pane.

List the pane IDs of the server:

Terminal window
$ tmux -L agent list-panes -a -F '#{pane_id}'
%0

Enable the tool and write a setup file for one exact pane:

Terminal window
$ libtmux-mcp \
--socket-name agent \
--enable-tool tmux_run \
--enroll-pane %0=enroll.zsh

Then, in that pane’s zsh, run source enroll.zsh. A relative path resolves from the directory where the server starts.

  • --enroll-pane requires --enable-tool tmux_run, and each pane and each file may appear once.
  • The server writes a private file (mode 0600) and never types into the terminal. It refuses an existing file or a symlink.
  • Setup files expire. --enrollment-timeout SECONDS sets the lifetime, at most 300.
  • EOF retires pending enrollment and removes only the files the server wrote.
  • A call takes an exact pane target, a POSIX script, and optional stdout_limit and stderr_limit byte counts.
  • The script runs with the shell’s working directory and exported environment.
  • Stdout and stderr come back separately, as UTF-8 or base64.
  • A nonzero exit or a signal is a completion result. Output over its limit is an error with completion unobserved, and never a truncated success.
  • Cancelling a run does not prove that its child processes stopped.

Embed it in Ruby

Require libtmux/mcp. Imports start no tmux process, scheduler or MCP server. The execution guide covers result and ownership boundaries.

LibTmux::MCP::Application: serve from your own program

Application borrows an application-owned LibTmux::Async::Server. Its sdk_server supplies the SDK server that StdioTransport consumes:

application = LibTmux::MCP::Application.new(server: scope.server, endpoint_name: "example")
sdk = application.sdk_server
input, client_input = IO.pipe
client_output, output = IO.pipe
transport = LibTmux::MCP::StdioTransport.new(server: sdk, parent: parent, input: input, output: output)
runner = parent.async { transport.run }

The complete program runs this excerpt inside an Async task with an open LibTmux::Async scope. It sends real JSON-RPC messages through the pipes, checks the replies, and prints PASS mcp_protocol.

Application#invite_shell: enroll a shell from Ruby

Call invite_shell with a pane reference, timeout: and expires_in:, then pass the invitation to accept_shell.

  • The invitation’s shell_arguments make up the setup command to source in that pane’s zsh.
  • timeout: bounds acquisition, and expires_in: sets the enrollment lifetime.
  • The application owns invitations and accepted connections until close. Direct enrollment calls enforce tool policy.

What it leaves out

  • Tools you have not enabled. Disabled tools are absent from discovery, and a direct application call is denied. See The tools.
  • Resource subscriptions. The server does not advertise them. See the resource templates.
  • Typing into your terminal. The server writes a private setup file and never types into a pane’s terminal to enroll it. See tmux_run.
  • A guaranteed stop. Cancelling a run does not prove that its child processes stopped. See tmux_run.

Support and status

Alpha. APIs may change between releases.

  • Ruby 3.3 or newer. The gem depends on libtmux and libtmux-async at the same version, the mcp SDK 1.5, digest and optparse.
  • tmux 3.3a or newer. The server refuses an older daemon when it starts, before it serves any tool, and prints the reason and the remedy on stderr.
  • A daemon that changes while it runs. A tool call returns an unsupported result with delivery: not_sent. The result names the floor and says to upgrade tmux and restart the server.
  • The screen_contains wait. It uses control events on tmux 3.7 and newer and polls the pane screen on older daemons, which take no control client.
  • Linux, including WSL, and macOS. The core compatibility workflow runs Ruby 3.3, 3.4 and 4.0 with tmux 3.3a through 3.8 on Ubuntu 24.04. macOS is supported on a best-effort basis, and CI coverage for it is highly limited.

Documentation

ReferenceThe embedding types and methods
ToolsOne page per tool, with arguments, limits and result schema
GuidesConnect a client to a private tmux server and select its tools
ResourcesMetadata pages and pane screens as URIs
Public behaviorThe application, transport and command
Esc

Type to search.

Reference preview Open reference