tmux
Drive one tmux server: sessions, windows, panes, and the output they produce.
Install
The installation steps are in the documentation below. Documentation
Documentation
From the project's README on GitHub, refreshed weekly. Links open on GitHub.
Contents
libtmux for Go
Drive tmux from Go: [sessions][tmux-session], [windows][tmux-window], and [panes][tmux-pane] as typed values, every tmux [option and hook as a typed accessor][topic-options-and-hooks], and [errors classified][topic-errors] by what tmux actually refused.
[Run a command][tmux-session-run] in a real terminal and read what it printed, [find][tmux-server-sessions] and [filter][tmuxq-matching] what a tmux server holds, [type into a pane and wait][tmux-paneobservation-waitfor] for its answer, and [hear about changes][tmux-session-opennotifications] as tmux makes them.
It suits tools that drive a terminal program, test suites that need a real tmux, and agents that share a terminal.
Add it to your module:
$ go get github.com/libtmux/libtmux-go/tmux@latest
Alpha software. Releases carry an -alpha prerelease tag and the API is not settled. Pin an exact version.
It needs Go 1.26 or newer and tmux 3.3a or newer, on a Unix host. Against an
older daemon, an operation returns [tmux.ErrVersionTooLow][tmux-errversiontoolow]
and sends nothing.
To fix it, upgrade tmux and restart its server, or pin the previous release. [The contract][contract-version] covers unrecognized versions and the calls that skip the check.
A tmux server for AI agents and a tmuxp-style workspace builder ship beside the library, under Packages.
Contents — Quick start · Find things · Type and wait · Watch tmux · Choosing a path · Testing · Packages · Leaves out · Support · Documentation
Quick start
Run a command in tmux and print what its terminal showed. This is the whole program:
// Command quickstart runs a command in tmux and prints what it showed.
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"github.com/libtmux/libtmux-go/tmux"
)
func main() {
if err := run(context.Background()); err != nil {
log.Fatal(err)
}
}
func run(ctx context.Context) (err error) {
// A private server: a socket and an empty config of its own, so this
// neither touches nor is changed by a tmux you already use.
server, err := tmux.NewServer(tmux.ServerOptions{
SocketName: "libtmux-quickstart",
ConfigFile: os.DevNull,
})
if err != nil {
return fmt.Errorf("configure tmux: %w", err)
}
session, err := server.NewSession(ctx, tmux.NewSessionRequest{Name: "demo"})
if err != nil {
return fmt.Errorf("create session: %w", err)
}
// Cleanup runs even when ctx is the reason the program is leaving.
defer func() {
err = errors.Join(err, server.Kill(context.WithoutCancel(ctx)))
}()
// The command runs in a window of its own, with a terminal.
result, err := session.Run(ctx, "echo hello from tmux", tmux.RunOptions{})
if err != nil {
return fmt.Errorf("run command: %w", err)
}
for _, line := range result.Lines {
fmt.Println(line)
}
fmt.Println("exit status:", result.Status)
return nil
}
It prints:
hello from tmux
exit status: 0
To run it, save it as main.go in an empty directory, with tmux 3.3a or newer
on PATH:
$ go mod init demo && \
go get github.com/libtmux/libtmux-go/tmux@latest && \
go run .
The program starts a tmux server on a socket and config of its own and kills
it on the way out, so it never touches a tmux you have open. With
[ServerOptions][tmux-serveroptions] at its zero value, it drives the same
server your tmux command does.
[Session.Run][tmux-session-run] is os/exec for a program that needs a
terminal. The command runs in a window of its own with a tty, and you get back
its exit status and the screen tmux rendered.
A nonzero exit status arrives in the result.
examples/run-to-completion shows one, on a tmux
server of its own.
Every Go block on this page comes from a program or test that runs on every supported release, against a real tmux wherever the block uses one, and each output shown is what that run printed.
Find sessions, windows, and panes
[Server.Sessions][tmux-server-sessions]: what tmux has now
One call asks tmux for every session. [Server.Windows][tmux-server-windows]
and [Server.Panes][tmux-server-panes] are its siblings:
// Given: ctx context.Context; server tmux.Server
sessions, err := server.Sessions(ctx)
if err != nil {
return fmt.Errorf("list sessions: %w", err)
}
for _, session := range sessions {
name, _ := session.Name()
fmt.Println("session:", name)
}
Each block on this page is the body of a function that returns an error, with the values its first line names already in hand.
examples/filter-query runs this and the two blocks
below on a tmux server of its own. Like every example, it runs from a clone
of the repository, with go -C examples run ./filter-query;
examples/README.md has the commands to clone it.
This one prints session: libtmux-filter.
What comes back is a record: the [Session][tmux-session] you hold is what
tmux said when you asked, and it stays as it was when tmux changes.
With no tmux server running, a listing returns an error matching
[ErrNoServer][tmux-errnoserver] instead of an empty list, so "nothing there"
and "nobody to ask" stay different answers.
[tmuxq.Matching][tmuxq-matching]: several answers from one read
Read the server once with [Server.Snapshot][tmux-server-snapshot] and filter
that read in Go. [Matching][tmuxq-matching] takes any filter the tmux
package declares:
// Given: ctx context.Context; server tmux.Server
snapshot, err := server.Snapshot(ctx)
if err != nil {
return err
}
active, err := tmuxq.Matching(snapshot.Panes(), tmux.PaneActiveIs(true))
if err != nil {
return err
}
fmt.Println("active panes:", len(active))
That session has one window with one pane in it, which is the active one, so
the example goes on to print active panes: 1.
[Server.SearchSessions][tmux-server-searchsessions]: let tmux filter
One command goes out and only the matches come back:
// Given: ctx context.Context; server tmux.Server
live := tmux.TmuxFilter("#{==:#{session_name},libtmux-filter}")
sessions, err := server.SearchSessions(ctx, &live)
if err != nil {
return err
}
fmt.Println("live matches:", len(sessions))
One session has that name, so this prints live matches: 1.
The filter is a tmux format, written the way the FORMATS section of the tmux
manual describes, and tmux evaluates it while it lists. tmuxq/ has
the filters that run in Go and the helpers that apply them.
Type into a pane and wait
[PaneObservation.WaitFor][tmux-paneobservation-waitfor]: wait for what a command prints
A pane is an io.Writer through [Pane.Writer][tmux-pane-writer], and a
newline is the Enter key. Open an observation with
[Pane.OpenObservation][tmux-pane-openobservation] before typing, then wait
for text the command prints:
// Given: ctx context.Context; pane tmux.Pane
// Open the observation before typing, so no output is missed.
output, err := pane.OpenObservation(ctx)
if err != nil {
return fmt.Errorf("observe pane: %w", err)
}
defer func() { err = errors.Join(err, output.Close()) }()
// A pane is an io.Writer, and a newline is the Enter key.
command := `echo "6 x 7 = $((6 * 7))"`
if _, err := fmt.Fprintln(pane.Writer(ctx), command); err != nil {
return fmt.Errorf("type command: %w", err)
}
// The terminal echoes what is typed, so wait for the answer: the typed
// line says "$((6 * 7))" and never "42".
if _, err := output.WaitFor(ctx, func(text string) bool {
return strings.Contains(text, "6 x 7 = 42")
}); err != nil {
return fmt.Errorf("wait for answer: %w", err)
}
fmt.Println("the pane answered 6 x 7 = 42")
tmux pushes each write as it happens, so nothing polls. The terminal echoes what is typed, and that echo is output too, which is why the wait names the answer the command prints.
Runnable: examples/pane-io, which goes on to read the
pane as an io.Reader.
To move a payload, skip the typing: the shell in the pane reads quotes, $,
and backslashes in it as syntax.
[Server.LoadBufferFrom][tmux-server-loadbufferfrom] fills a tmux buffer from
an io.Reader, and [Pane.CaptureTo][tmux-pane-captureto] writes a scrollback
to an io.Writer. examples/byte-streams does both.
Watch tmux change
[Session.OpenNotifications][tmux-session-opennotifications]: hear changes as they happen
It returns a stream of what tmux does, such as a window opening or a session being renamed. tmux pushes each change when it happens, so nothing has to guess how often to ask:
// Given: ctx context.Context; session tmux.Session
stream, err := session.OpenNotifications(ctx, tmux.NotificationOptions{})
if err != nil {
return fmt.Errorf("open notification stream: %w", err)
}
defer func() { err = errors.Join(err, stream.Close()) }()
// Rename after subscribing; notifications do not include earlier changes.
if _, err := session.Rename(ctx, "control-example"); err != nil {
return fmt.Errorf("rename session: %w", err)
}
for {
notification, err := stream.Next(ctx)
if err != nil {
return fmt.Errorf("read notification: %w", err)
}
fmt.Printf("notification: %s\n", notification.Kind())
if notification.Kind() == tmux.ControlNotificationSessionRenamed {
fmt.Println("heard the rename")
break
}
}
The first notification a new stream reads is %session-changed, which is
tmux telling the client which session it joined. The rename follows it.
This prints notification: %session-changed, then
notification: %session-renamed, then heard the rename.
[NotificationStream.Subscribe][tmux-notificationstream-subscribe]: have tmux watch a value
A subscription goes further: tmux evaluates a format for you and reports it each time its value changes, so a program hears about a window count or a pane's current command without asking again:
// Given: ctx context.Context; session tmux.Session;
// stream *tmux.NotificationStream
// A subscription is a format tmux evaluates for you: it reports the value
// when it first looks, about a second later, and then each time it changes.
if err := stream.Subscribe(ctx, tmux.SubscriptionRequest{
Name: "windows", Format: "#{session_windows}",
}); err != nil {
return fmt.Errorf("subscribe: %w", err)
}
if _, err := session.NewWindow(ctx, tmux.NewWindowRequest{}); err != nil {
return fmt.Errorf("open window: %w", err)
}
for {
notification, err := stream.Next(ctx)
if err != nil {
return fmt.Errorf("read notification: %w", err)
}
if change, ok := notification.Subscription(); ok && change.Value == "2" {
fmt.Println("session has", change.Value, "windows")
return nil
}
}
The session started with one window, so this prints session has 2 windows.
Runnable: examples/control-mode-subscribe
is the program both blocks come from.
Choosing how to reach tmux
Start with a plain [Server][tmux-server]: one tmux process per command. The
other paths keep a tmux client attached, or send several commands at once, to
start fewer:
| Path | Construct it with | Cost | Reach for it |
|---|---|---|---|
| a process per command | [tmux.NewServer][tmux-newserver] | a tmux process per command sent | one-shot commands |
| a connection | [Session.OpenControl][tmux-session-opencontrol] | one tmux client, kept attached | repeated commands |
| several connections | [ConnectionOptions{Lanes: N}][tmux-connectionoptions-lanes] | N tmux clients, kept attached | readers running in parallel |
| a plan | [tmux.NewPlan][tmux-newplan] then [Plan.Run][tmux-plan-run] | fewer process starts | builds and layouts |
| a stream | [Session.OpenNotifications][tmux-session-opennotifications] | one tmux client, kept attached | watching what tmux does |
[tmux.NewPlan][tmux-newplan]: write a layout in one pass
A plan records commands instead of running them, sends the ones needing no answer together, and hands back a reference to what a step will create.
new("echo built") is how Go 1.26 writes a pointer to a value, for a field
that may be left unset:
// Given: window tmux.Window
plan := tmux.NewPlan()
plan.SelectLayout(window.Ref(), tmux.SelectLayoutRequest{Layout: "tiled"})
editor := plan.SplitPane(window.Ref(), tmux.SplitPaneRequest{Attach: true})
plan.SetPaneTitle(editor, "editor")
plan.SendKeys(editor, tmux.SendKeysRequest{Command: new("echo built")})
plan.DisplayMessage(editor, "#{pane_title}")
[Plan.Run][tmux-plan-run] sends it, and reports on each step by itself even
where several shared one tmux process:
// Given: ctx context.Context; server tmux.Server; plan *tmux.Plan
result, err := plan.Run(ctx, server)
if err != nil {
return fmt.Errorf("run plan: %w", err)
}
if !result.OK() {
return fmt.Errorf("plan did not complete: %w", result.Err())
}
// Results remain per operation even when operations share an invocation.
for index, op := range result.Ops {
fmt.Printf("step %d %-16s %-8s created %q stdout %q\n",
index, op.Command, op.Status, op.Created, op.Stdout)
}
examples/planned-build is the program both blocks
come from.
It prints operations: 6, tmux invocations: 4 before it runs the plan: six
tmux commands, in four process starts rather than six. The last step reads
back the title the plan set, so its line ends stdout ["editor"].
A connection appears in list-clients and counts toward session_attached,
which is why opening one is explicit.
"Execution modes" in the [package documentation][tmux-package] has the rules
each path follows. BENCHMARKS.md is what each costs,
measured on every supported tmux.
examples/fast-path runs the same reads down each path.
Testing your own code
[tmuxtest.RunInPane][tmuxtest-runinpane]: run your program in a real pane
tmux/tmuxtest runs your program inside a real tmux and
lets a test assert on what it drew, without a sleep in it. This is a whole
test file:
package greeter_test
import (
"context"
"os"
"testing"
"time"
"github.com/libtmux/libtmux-go/tmux/tmuxtest"
)
func TestMain(m *testing.M) {
os.Exit(tmuxtest.Main(m))
}
func TestGreeterAnswersALine(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// A stand-in for your program: it greets each name it reads.
greeter := `while read name; do echo "hello, $name"; done`
pane := tmuxtest.RunInPane(ctx, t, greeter)
tmuxtest.Type(ctx, t, pane, "world")
tmuxtest.WaitForLine(ctx, t, pane, "hello, world")
}
[tmuxtest.WaitForText][tmuxtest-waitfortext]: a failed wait shows the screen
A wait that runs out fails with the screen the pane last held, which is what a
print statement would have shown. This one was waiting for ready:
tmuxtest: pane %1 never showed a line containing "ready"
the pane showed 3 line(s):
| tmuxtest$ printf 'loading widgets\nconnecting\n'; cat
| loading widgets
| connecting
More in tmuxtest
[tmuxtest.NewServer][tmuxtest-newserver] gives a test whose subject is tmux
itself a server on a socket of its own, killed when the test ends.
[tmuxtest.ScriptedTmux][tmuxtest-scriptedtmux] answers the tmux commands you
name instead, so a test of your own code needs no tmux installed.
tmux/tmuxtest shows both.
Packages
| Package | Source | Reference | What it is |
|---|---|---|---|
tmux | tmux/ | pkg.go.dev | The library. Sessions, windows, panes, options, hooks, formats, filters, snapshots, plans. |
tmuxtest | tmux/tmuxtest/ | pkg.go.dev | Run your program in a real tmux and assert on what it drew. |
tmuxq | tmuxq/ | pkg.go.dev | Model-free generic helpers for slices and iter.Seq. |
Others ship as separate modules, so go get on the library pulls in none
of them:
| Module | Source | Reference | What it is |
|---|---|---|---|
mcp | mcp/ | pkg.go.dev | A tmux server for AI agents, over the Model Context Protocol |
workspace | workspace/ | pkg.go.dev | Loads tmuxp-style YAML workspaces and builds them, with a CLI |
benchmarks | benchmarks/ | — | Prints what each way of reaching tmux costs |
The MCP server:
$ go install github.com/libtmux/libtmux-go/mcp/cmd/libtmux-mcp@latest
The workspace CLI:
$ go install github.com/libtmux/libtmux-go/workspace/cmd/tmux-workspace@latest
tmux: the library
Sessions, windows, panes, options, hooks, formats, filters, snapshots, and plans, with no dependency outside the standard library. Reference: pkg.go.dev and libtmux.org.
[Session.Run][tmux-session-run] runs a command in a window of its own, with
a terminal. A nonzero exit status arrives in the result:
// Given: ctx context.Context; session tmux.Session
// The command runs with a terminal, so test -t holds where a pipe fails.
command := "test -t 1 && echo stdout is a terminal; exit 3"
result, err := session.Run(ctx, command, tmux.RunOptions{})
if err != nil {
return fmt.Errorf("run command: %w", err)
}
for _, line := range result.Lines {
fmt.Println("screen:", line)
}
fmt.Println("exited", result.Status)
This prints screen: stdout is a terminal, then exited 3.
tmuxq: filters for any slice
Helpers that find records in a slice or an iter.Seq. The package imports no
tmux type, so they work on a slice of your own as well as on the records
tmux returns. Reference:
pkg.go.dev and
libtmux.org.
[tmuxq.Where][tmuxq-where] keeps what a function accepts, and
[tmuxq.First][tmuxq-first] returns the first match. This needs no tmux:
type task struct {
name string
done bool
}
tasks := []task{{"build", true}, {"test", false}, {"deploy", false}}
open := func(t *task) bool { return !t.done }
for _, t := range tmuxq.Where(tasks, open) {
fmt.Println("open:", t.name)
}
first, _ := tmuxq.First(tasks, open)
fmt.Println("first open:", first.name)
This prints open: test and open: deploy, then first open: test.
tmuxtest: a real tmux for tests
Runs your program in a real pane and lets a test assert on what it drew. The server, its sessions, and its panes end with the test. Reference: pkg.go.dev and libtmux.org.
[tmuxtest.NewServer][tmuxtest-newserver] gives a test whose subject is tmux
itself a server on a socket of its own. This is the body of a test, in a
package whose TestMain calls [tmuxtest.Main][tmuxtest-main], as in
the test above:
// Given: ctx context.Context; t *testing.T
server := tmuxtest.NewServer(ctx, t)
session, err := server.NewSession(ctx, tmux.NewSessionRequest{Name: "under-test"})
if err != nil {
t.Fatal(err)
}
if name, _ := session.Name(); name != "under-test" {
t.Errorf("session name = %q, want under-test", name)
}
workspace: tmuxp-style workspaces
Loads tmuxp-style YAML workspace files and builds them in tmux, from Go or with
the tmux-workspace command. Reference:
pkg.go.dev,
libtmux.org, and the
command reference.
[workspace/CLI.md][workspace-cli-md] covers every command and its output
formats.
With the command installed as above and a [workspace file][workspace-file]
saved as project.yaml, [tmux-workspace load][workspace-cli-load] builds it
on a tmux socket named project without attaching to it:
$ tmux-workspace load \
-L project \
-d \
./project.yaml
mcp: a tmux server for agents
Serves one tmux server to Model Context Protocol clients, as the
[libtmux-mcp][libtmux-mcp-command] command or from a program of your own.
Reference: pkg.go.dev,
libtmux.org, and the
tool pages.
[mcp/README.md][mcp-readme] covers client configuration, and
[mcp/TOOLS.md][mcp-tools] lists each tool's arguments and results.
An MCP client starts the server installed above itself, and speaks to it over stdin and stdout. Register it with Claude Code:
$ claude mcp add tmux -- libtmux-mcp
Check which socket it reaches, and what that holds, without a client, with
[libtmux-mcp -doctor][guide-diagnose-startup]:
$ libtmux-mcp -doctor
The benchmarks
module is a command that prints what each way of reaching tmux costs.
What it leaves out
- Live objects. A [
Session][tmux-session] or a [Pane][tmux-pane] is a record of what tmux reported when it was read, and does not change when tmux does. Read again, or subscribe, to see a change. - A terminal emulator. [A capture][tmux-pane-capture] and
[
Session.Run][tmux-session-run] return the screen tmux drew. [PaneObservation][tmux-paneobservation] returns the bytes a program wrote. - Your tmux configuration file. Options and hooks are read and set on a
running server, and nothing here edits
~/.tmux.conf. tmux reads it when it starts a server, unless [ServerOptions.ConfigFile][tmux-serveroptions-configfile] names another.
Support and status
Any release may change or remove exported identifiers without a deprecation period, so pin an exact version. Not recommended for production.
- No runtime dependencies. The core module imports only the standard library.
- Go 1.26+, rising with Go's own support window, about twice a year. tmux 3.3a through 3.8 across the core, workspace, and MCP modules, tested on releases spanning that range.
- Linux, including WSL, is the primary platform. CI runs every module's tests on Linux across tmux 3.3a through 3.8. macOS is supported on a best-effort basis, and CI coverage for it is highly limited.
- Other hosts. The BSDs, illumos, and Plan 9 build. A Windows build compiles, and CI tests executable lookup there; no tmux runs there.
- Tagged per directory. Each module carries its own version: the tags are
mcp/vNandworkspace/vNbeside the core's plainvN. Pin the exact ones you want in your own go.mod; the commands here fetch the newest.
Documentation
The package documentation is the reference, written to be read start to finish:
$ go doc github.com/libtmux/libtmux-go/tmux
It opens with a task index, and carries the rule mapping a tmux command to
its Go method: kill-pane is [Pane.Kill][tmux-pane-kill], and
rename-session is [Session.Rename][tmux-session-rename]. A command usually
leads to its method without a lookup.
| Reference | Every exported identifier, method, and field, one page each |
tmux/ | How the library is shaped: records, naming, options, errors |
tmux/docs/contracts.md | What the library promises: the tmux version check |
examples/ | Runnable programs, one per topic, each with a page of notes |
DESIGN.md | The conventions this package holds itself to, and the bakeoffs behind them |
PARITY.md | How the surface is checked against the Python libtmux |
BENCHMARKS.md | What each way of reaching tmux costs |
CHANGELOG.md | What each release changed |
SECURITY.md | What this software executes, and how to report a hole in it |
CONTRIBUTING.md | The gates a change has to pass, and where the writing rules live |
WRITING.md | How this repository writes: docs, the changelog, commits |
AGENTS.md | Which of the above applies to what you are changing |
Attribution
Please use the following BibTeX template to cite libtmux-go in scientific discourse:
@misc{libtmux-go,
author = {Tony Narlock},
year = {2026},
note = {https://libtmux.org/en/go/},
title = {libtmux-go: Go wrapper for tmux}
}
License
MIT. See LICENSE.
[libtmux-mcp-command]: https://pkg.go.dev/github.com/libtmux/libtmux-go/mcp/cmd/libtmux-mcp [mcp-readme]: https://github.com/libtmux/libtmux-go/tree/master/mcp [mcp-tools]: https://github.com/libtmux/libtmux-go/blob/master/mcp/TOOLS.md [contract-version]: tmux/docs/contracts.md#the-tmux-version-check [guide-diagnose-startup]: https://libtmux.org/en/go/latest/mcp/guides/connect-client/#diagnose-startup [tmux-connectionoptions-lanes]: https://libtmux.org/en/go/latest/reference/tmux-connectionoptions-lanes/ [tmux-errnoserver]: https://libtmux.org/en/go/latest/reference/tmux-errnoserver/ [tmux-errversiontoolow]: https://libtmux.org/en/go/latest/reference/tmux-errversiontoolow/ [tmux-newplan]: https://libtmux.org/en/go/latest/reference/tmux-newplan/ [tmux-newserver]: https://libtmux.org/en/go/latest/reference/tmux-newserver/ [tmux-notificationstream-subscribe]: https://libtmux.org/en/go/latest/reference/tmux-notificationstream-subscribe/ [tmux-package]: https://pkg.go.dev/github.com/libtmux/libtmux-go/tmux [tmux-pane-capture]: https://libtmux.org/en/go/latest/reference/tmux-pane-capture/ [tmux-pane-captureto]: https://libtmux.org/en/go/latest/reference/tmux-pane-captureto/ [tmux-pane-kill]: https://libtmux.org/en/go/latest/reference/tmux-pane-kill/ [tmux-pane-openobservation]: https://libtmux.org/en/go/latest/reference/tmux-pane-openobservation/ [tmux-pane-writer]: https://libtmux.org/en/go/latest/reference/tmux-pane-writer/ [tmux-pane]: https://libtmux.org/en/go/latest/reference/tmux-pane/ [tmux-paneobservation-waitfor]: https://libtmux.org/en/go/latest/reference/tmux-paneobservation-waitfor/ [tmux-paneobservation]: https://libtmux.org/en/go/latest/reference/tmux-paneobservation/ [tmux-plan-run]: https://libtmux.org/en/go/latest/reference/tmux-plan-run/ [tmux-server-loadbufferfrom]: https://libtmux.org/en/go/latest/reference/tmux-server-loadbufferfrom/ [tmux-server-panes]: https://libtmux.org/en/go/latest/reference/tmux-server-panes/ [tmux-server-searchsessions]: https://libtmux.org/en/go/latest/reference/tmux-server-searchsessions/ [tmux-server-sessions]: https://libtmux.org/en/go/latest/reference/tmux-server-sessions/ [tmux-server-snapshot]: https://libtmux.org/en/go/latest/reference/tmux-server-snapshot/ [tmux-server-windows]: https://libtmux.org/en/go/latest/reference/tmux-server-windows/ [tmux-server]: https://libtmux.org/en/go/latest/reference/tmux-server/ [tmux-serveroptions-configfile]: https://libtmux.org/en/go/latest/reference/tmux-serveroptions-configfile/ [tmux-serveroptions]: https://libtmux.org/en/go/latest/reference/tmux-serveroptions/ [tmux-session-opencontrol]: https://libtmux.org/en/go/latest/reference/tmux-session-opencontrol/ [tmux-session-opennotifications]: https://libtmux.org/en/go/latest/reference/tmux-session-opennotifications/ [tmux-session-rename]: https://libtmux.org/en/go/latest/reference/tmux-session-rename/ [tmux-session-run]: https://libtmux.org/en/go/latest/reference/tmux-session-run/ [tmux-session]: https://libtmux.org/en/go/latest/reference/tmux-session/ [tmux-window]: https://libtmux.org/en/go/latest/reference/tmux-window/ [tmux]: https://github.com/tmux/tmux [tmuxq-first]: https://libtmux.org/en/go/latest/reference/tmuxq-first/ [tmuxq-matching]: https://libtmux.org/en/go/latest/reference/tmuxq-matching/ [tmuxq-where]: https://libtmux.org/en/go/latest/reference/tmuxq-where/ [tmuxtest-main]: https://libtmux.org/en/go/latest/reference/tmuxtest-main/ [tmuxtest-newserver]: https://libtmux.org/en/go/latest/reference/tmuxtest-newserver/ [tmuxtest-runinpane]: https://libtmux.org/en/go/latest/reference/tmuxtest-runinpane/ [tmuxtest-scriptedtmux]: https://libtmux.org/en/go/latest/reference/tmuxtest-scriptedtmux/ [tmuxtest-waitfortext]: https://libtmux.org/en/go/latest/reference/tmuxtest-waitfortext/ [topic-errors]: https://libtmux.org/en/go/latest/topics/errors-and-exceptions/ [topic-options-and-hooks]: https://libtmux.org/en/go/latest/topics/options-and-hooks/ [workspace-cli-load]: https://libtmux.org/en/go/latest/workspace/cli/load/ [workspace-cli-md]: https://github.com/libtmux/libtmux-go/blob/master/workspace/CLI.md [workspace-file]: https://github.com/libtmux/libtmux-go/tree/master/workspace#a-workspace-file
Frequently asked questions
What is tmux?
Drive one tmux server: sessions, windows, panes, and the output they produce.
How do I install tmux?
Follow the installation steps in its documentation, shown on this page and in its repository.
Is tmux open source?
Yes. Its code is on GitHub under the MIT license.
Who maintains tmux?
It is published by libtmux. AI.tn lists it but does not maintain or endorse it.
From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.