TypeScriptHome

Choose documentation 3

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.

libtmux for TypeScript

TypeScript-based tmux client

tmux client library for TypeScript. Control tmux, connect to and create servers, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents

Product

Start with TypeScript

Example details
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { Server } from "libtmux";
const directory = await mkdtemp(join(tmpdir(), "ltx-home-"));
const server = new Server({
socketPath: join(directory, "tmux.sock"),
configFile: "/dev/null",
timeoutMs: 5_000,
});
const signal = AbortSignal.timeout(5_000);
const session = await server.newSession({ name: "work", signal });
const editor = await session.newWindow({
name: "editor",
shellCommand: "sh",
signal,
});
await editor.split({ signal });
const pane = editor.activePane;
if (!pane) throw new Error("The window has no active pane");
await pane.sendKeys("printf '\\nhello\\n'", { signal });
for (;;) {
const lines = await pane.capture({ signal });
if (lines.includes("hello")) {
console.log("hello");
break;
}
await delay(20, undefined, { signal });
}
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { Server } from "libtmux";
const directory = await mkdtemp(join(tmpdir(), "ltx-home-"));
const server = new Server({
socketPath: join(directory, "tmux.sock"),
configFile: "/dev/null",
timeoutMs: 5_000,
});
try {
const signal = AbortSignal.timeout(5_000);
const session = await server.newSession({ name: "work", signal });
const editor = await session.newWindow({
name: "editor",
shellCommand: "sh",
signal,
});
await editor.split({ signal });
const pane = editor.activePane;
if (!pane) throw new Error("The window has no active pane");
await pane.sendKeys("printf '\\nhello\\n'", { signal });
for (;;) {
const lines = await pane.capture({ signal });
if (lines.includes("hello")) {
console.log("hello");
break;
}
await delay(20, undefined, { signal });
}
} catch (error) {
throw new Error("tmux quickstart failed", { cause: error });
}
import { mkdtemp, readdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { Server } from "libtmux";
const directory = await mkdtemp(join(tmpdir(), "ltx-home-"));
const server = new Server({
socketPath: join(directory, "tmux.sock"),
configFile: "/dev/null",
timeoutMs: 5_000,
});
try {
const signal = AbortSignal.timeout(5_000);
const session = await server.newSession({ name: "work", signal });
const editor = await session.newWindow({
name: "editor",
shellCommand: "sh",
signal,
});
await editor.split({ signal });
const pane = editor.activePane;
if (!pane) throw new Error("The window has no active pane");
await pane.sendKeys("printf '\\nhello\\n'", { signal });
for (;;) {
const lines = await pane.capture({ signal });
if (lines.includes("hello")) {
console.log("hello");
break;
}
await delay(20, undefined, { signal });
}
} finally {
if ((await readdir(directory)).includes("tmux.sock")) {
await server.kill();
}
await rm(directory, { recursive: true });
}
import { mkdtemp, readdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { Server } from "libtmux";
const directory = await mkdtemp(join(tmpdir(), "ltx-home-"));
const server = new Server({
socketPath: join(directory, "tmux.sock"),
configFile: "/dev/null",
timeoutMs: 5_000,
});
const failures: unknown[] = [];
try {
const signal = AbortSignal.timeout(5_000);
const session = await server.newSession({ name: "work", signal });
const editor = await session.newWindow({
name: "editor",
shellCommand: "sh",
signal,
});
await editor.split({ signal });
const pane = editor.activePane;
if (!pane) throw new Error("The window has no active pane");
await pane.sendKeys("printf '\\nhello\\n'", { signal });
for (;;) {
const lines = await pane.capture({ signal });
if (lines.includes("hello")) {
console.log("hello");
break;
}
await delay(20, undefined, { signal });
}
} catch (error) {
failures.push(error);
}
try {
if ((await readdir(directory)).includes("tmux.sock")) {
await server.kill();
}
await rm(directory, { recursive: true });
} catch (error) {
failures.push(new Error(`Cleanup failed: ${directory}`, {
cause: error,
}));
}
if (failures.length) throw new AggregateError(failures);
Run this example

POSIX shell, Git, tmux 3.3a or newer and Bun 1.4.2.

Save the program above as quickstart.ts in a new directory, along with the files below.

Cleanup is omitted; the private tmux server and its temporary files may remain. Select Cleanup to remove them when the program finishes. The program stops its private tmux server and removes any temporary files it created when it finishes. Error handling adds diagnostics; every version checks required results and deadlines.

package.json
{"type":"module","private":true,"dependencies":{"libtmux":"file:./source/packages/libtmux"}}

Setup and run

git init -q source
git -C source fetch -q --depth=1 \
https://github.com/libtmux/libtmux-ts.git \
46cccfef2a546de55ce8b308249b8c76339089a0
git -C source checkout -q --detach FETCH_HEAD
bun install
unset TMUX TMUX_PANE
export ENV=/dev/null BASH_ENV=/dev/null SHELL=/bin/sh
bun run quickstart.ts

Expected output

hello

Library revision: 46cccfef2a54.

Core Library documentation →More TypeScript examples →

Install libtmux

Dependency cooldowns

The checkbox turns a cooldown on and off for every picker on the site. It applies to what an install pulls in; the package itself, and the libtmux it pins, stay current.

Cooldown type
What are cooldowns?

Cooldowns delay picking up newly uploaded packages so the community has time to spot supply-chain attacks before you install. Most package managers don't apply one by default; you opt in through a global setting in your tool's configuration.

Learn more at cooldowns.dev · Datadog Security Labs writeup.

$ npm install libtmux
$ npm install \
    --min-release-age=7 \
    --min-release-age-exclude=libtmux \
    libtmux
$ npm install --min-release-age=0 libtmux
$ pnpm add libtmux
$ pnpm_config_minimum_release_age=10080 \
    pnpm_config_minimum_release_age_exclude='["libtmux"]' \
    pnpm add libtmux
$ pnpm_config_minimum_release_age=0 pnpm add libtmux
$ yarn add libtmux
$ YARN_NPM_MINIMAL_AGE_GATE=7d \
    YARN_NPM_PREAPPROVED_PACKAGES=libtmux \
    yarn add libtmux
$ YARN_NPM_MINIMAL_AGE_GATE=0 yarn add libtmux
$ bun add libtmux
$ bun add libtmux

Bun cannot exempt libtmux from its own cooldown, so a cooldown here would also hold back the release this installs. This command runs without one.

$ bun add --minimum-release-age=0 libtmux

MCP

The TypeScript server lets an agent work with tmux through 45 tools, grouped as Inspect, Manage, Execute and Teardown.

Worked example

Given this prompt, an agent connected to the TypeScript server makes these calls and gets these results.

No sessions

After kill_session.

you

Create a tmux session called checks, split its window into two panes, run pytest -q in one and show checks.log in the other, show me the results, then remove the session.

agentCreate the session.

create_session Execute {"name":"checks"}
Result
{
"paneId": "%0",
"session": {
"attachedClients": 0,
"humanAttachedClients": 0,
"id": "$0",
"metadataComplete": true,
"name": "checks",
"omittedMetadataBytes": 0,
"windows": 1
},
"windowId": "@0"
}

agentSplit the pane the create result returned (%0) in two.

split_window Execute {"paneId":"%0","direction":"right"}
Result
{
"pane": {
"active": false,
"command": "sh",
"cwd": "/home/user/project",
"dead": false,
"height": 24,
"id": "%1",
"index": 1,
"isAttended": false,
"isCallerPane": false,
"metadataComplete": true,
"omittedMetadataBytes": 0,
"title": "host",
"…": "6 more"
}
}

agentRead the window's layout.

list_windows Inspect {"session":"checks"}
Result
{
"complete": true,
"omittedEntries": 0,
"windows": [
{
"id": "@0",
"layout": "{\"V\":2,\"L\":{\"t\":\"h\",\"w\":80,\"h\":24,\"x\":0,\"y\":0,\"c\":[{\"t\":\"p\",\"w\":40,\"h\":24,\"x\":0,\"y\":0,\"a\":true,\"i\":0,\"I\":\"%0\"},{\"t\":\"p\",\"w\":39,\"h\":24,\"x\":41,\"y\":0,\"i\":1,\"I\":\"%1\"}]}}",
"metadataComplete": true,
"name": "sh",
"omittedMetadataBytes": 0,
"panes": 2,
"zoomed": false,
"omittedPlacements": 0,
"placements": [
{
"active": true,
"index": 0,
"sessionId": "$0",
"sessionName": "checks"
}
],
"placementsComplete": true
}
]
}

agentRun the tests in the pane the create result returned (%0).

run_shell_command Execute {"paneId":"%0","command":"pytest -q"}
Result
{
"effectiveTimeoutMs": 30000,
"exitStatus": 0,
"foreignOutputSuspected": false,
"droppedLines": 0,
"missedBytes": 0,
"omittedBytes": 0,
"outcome": "completed",
"output": ".. [100%]\n2 passed in 0.00s",
"outputComplete": true,
"paneId": "%0",
"returnedBytes": 57,
"stillRunning": false
}

agentShow the log in the new pane the split returned (%1).

run_shell_command Execute {"paneId":"%1","command":"cat checks.log"}
Result
{
"effectiveTimeoutMs": 30000,
"exitStatus": 0,
"foreignOutputSuspected": false,
"droppedLines": 0,
"missedBytes": 0,
"omittedBytes": 0,
"outcome": "completed",
"output": "2026-01-01 checks started\n2026-01-01 checks finished",
"outputComplete": true,
"paneId": "%1",
"returnedBytes": 52,
"stillRunning": false
}

agentRemove the session once the result is read.

kill_session Teardown {"session":"checks"}
Result
{
"killed": "$0"
}

agent in %0 it printed 2 passed in 0.00s; in %1 it printed 2026-01-01 checks finished.

Calls and results are recorded against libtmux/libtmux-ts at ecc3eaca0, on a private tmux 3.8a server, running pytest 9.2.0. The preview draws only what those results state. The agent's narration is illustrative. Long values in a result are shortened here; the recording keeps them. The sandbox's project directory is shown as /home/user/project.

Install @libtmux/mcp

Choose your client and configuration scope, then run the command or paste the configuration.

$ claude mcp add tmux -- npx -y @libtmux/mcp

Config file: ~/.claude.json (this project)

$ claude mcp add tmux -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.claude.json (this project)

$ claude mcp add tmux -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ~/.claude.json (this project)

$ claude mcp add tmux --scope user -- npx -y @libtmux/mcp

Config file: ~/.claude.json (all projects)

$ claude mcp add tmux --scope user -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.claude.json (all projects)

$ claude mcp add tmux --scope user -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ~/.claude.json (all projects)

$ claude mcp add tmux --scope project -- npx -y @libtmux/mcp

Config file: .mcp.json (in repo, version-controlled)

$ claude mcp add tmux --scope project -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: .mcp.json (in repo, version-controlled)

$ claude mcp add tmux --scope project -- npx -y --min-release-age=0 @libtmux/mcp

Config file: .mcp.json (in repo, version-controlled)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ claude mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (this project)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ claude mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (this project)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ claude mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (this project)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ claude mcp add tmux --scope user -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (all projects)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ claude mcp add tmux --scope user -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (all projects)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ claude mcp add tmux --scope user -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.claude.json (all projects)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ claude mcp add tmux --scope project -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .mcp.json (in repo, version-controlled)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ claude mcp add tmux --scope project -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .mcp.json (in repo, version-controlled)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ claude mcp add tmux --scope project -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .mcp.json (in repo, version-controlled)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

Config file: claude_desktop_config.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: claude_desktop_config.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "--min-release-age=0", "@libtmux/mcp"]
        }
    }
}

Config file: claude_desktop_config.json

Build or install the server first:

$ npm install --global @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: claude_desktop_config.json

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: claude_desktop_config.json

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: claude_desktop_config.json

$ codex mcp add tmux -- npx -y @libtmux/mcp

Config file: ~/.codex/config.toml

$ codex mcp add tmux -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.codex/config.toml

$ codex mcp add tmux -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ~/.codex/config.toml

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "npx"
args = ["-y", "@libtmux/mcp"]

Config file: .codex/config.toml (in repo)

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "npx"
args = ["-y", "@libtmux/mcp"]

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: .codex/config.toml (in repo)

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "npx"
args = ["-y", "--min-release-age=0", "@libtmux/mcp"]

Config file: .codex/config.toml (in repo)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ codex mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.codex/config.toml

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ codex mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.codex/config.toml

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ codex mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.codex/config.toml

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "libtmux-mcp"

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .codex/config.toml (in repo)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "libtmux-mcp"

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .codex/config.toml (in repo)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

Codex's CLI doesn't support project scope yet — paste this into .codex/config.toml at the repo root.

[mcp_servers.tmux]
command = "libtmux-mcp"

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .codex/config.toml (in repo)

$ gemini mcp add --scope user tmux npx -- -y @libtmux/mcp

Config file: ~/.gemini/settings.json

$ gemini mcp add --scope user tmux npx -- -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.gemini/settings.json

$ gemini mcp add --scope user tmux npx -- -y --min-release-age=0 @libtmux/mcp

Config file: ~/.gemini/settings.json

$ gemini mcp add --scope project tmux npx -- -y @libtmux/mcp

Config file: .gemini/settings.json (in repo)

$ gemini mcp add --scope project tmux npx -- -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: .gemini/settings.json (in repo)

$ gemini mcp add --scope project tmux npx -- -y --min-release-age=0 @libtmux/mcp

Config file: .gemini/settings.json (in repo)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ gemini mcp add --scope user tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/settings.json

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ gemini mcp add --scope user tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/settings.json

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ gemini mcp add --scope user tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/settings.json

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ gemini mcp add --scope project tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .gemini/settings.json (in repo)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ gemini mcp add --scope project tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .gemini/settings.json (in repo)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ gemini mcp add --scope project tmux libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .gemini/settings.json (in repo)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

Config file: .cursor/mcp.json (in repo)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: .cursor/mcp.json (in repo)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "--min-release-age=0", "@libtmux/mcp"]
        }
    }
}

Config file: .cursor/mcp.json (in repo)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

Config file: ~/.cursor/mcp.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.cursor/mcp.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "--min-release-age=0", "@libtmux/mcp"]
        }
    }
}

Config file: ~/.cursor/mcp.json

Build or install the server first:

$ npm install --global @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .cursor/mcp.json (in repo)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .cursor/mcp.json (in repo)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: .cursor/mcp.json (in repo)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.cursor/mcp.json

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.cursor/mcp.json

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.cursor/mcp.json

$ grok mcp add --scope user tmux -- npx -y @libtmux/mcp

Config file: ~/.grok/config.toml

$ grok mcp add --scope user tmux -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.grok/config.toml

$ grok mcp add --scope user tmux -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ~/.grok/config.toml

$ grok mcp add --scope project tmux -- npx -y @libtmux/mcp

Config file: ./.grok/config.toml (in repo)

$ grok mcp add --scope project tmux -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ./.grok/config.toml (in repo)

$ grok mcp add --scope project tmux -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ./.grok/config.toml (in repo)

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ grok mcp add --scope user tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.grok/config.toml

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ grok mcp add --scope user tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.grok/config.toml

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ grok mcp add --scope user tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.grok/config.toml

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ grok mcp add --scope project tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ./.grok/config.toml (in repo)

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ grok mcp add --scope project tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ./.grok/config.toml (in repo)

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ grok mcp add --scope project tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ./.grok/config.toml (in repo)

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

Config file: ~/.gemini/config/mcp_config.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "@libtmux/mcp"]
        }
    }
}

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.gemini/config/mcp_config.json

{
    "mcpServers": {
        "tmux": {
            "command": "npx",
            "args": ["-y", "--min-release-age=0", "@libtmux/mcp"]
        }
    }
}

Config file: ~/.gemini/config/mcp_config.json

Build or install the server first:

$ npm install --global @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/config/mcp_config.json

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/config/mcp_config.json

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then use this config:

{
    "mcpServers": {
        "tmux": {
            "command": "libtmux-mcp"
        }
    }
}

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.gemini/config/mcp_config.json

$ opencode mcp add tmux -- npx -y @libtmux/mcp

Config file: ~/.config/opencode/opencode.jsonc

$ opencode mcp add tmux -- npx -y @libtmux/mcp

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release it runs; this runs without one. The Global install tab applies a cooldown to the server's dependencies.

Config file: ~/.config/opencode/opencode.jsonc

$ opencode mcp add tmux -- npx -y --min-release-age=0 @libtmux/mcp

Config file: ~/.config/opencode/opencode.jsonc

Build or install the server first:

$ npm install --global @libtmux/mcp

Then register:

$ opencode mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.config/opencode/opencode.jsonc

Build or install the server first:

$ npm install --global \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/mcp \
    --min-release-age-exclude=libtmux \
    @libtmux/mcp

Then register:

$ opencode mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.config/opencode/opencode.jsonc

Build or install the server first:

$ npm install --global --min-release-age=0 @libtmux/mcp

Then register:

$ opencode mcp add tmux -- libtmux-mcp

The package installs a `libtmux-mcp` binary. Requires Node 22 or newer, or Bun 1.3.14 or newer.

Config file: ~/.config/opencode/opencode.jsonc

Embed the server in a program
examples/mcp-agent/mcp-agent.ts
export async function connectAgent(server: Server): Promise<Client> {
const client = new Client({ name: "example", version: "0.0.0" });
const [clientSide, serverSide] = InMemoryTransport.createLinkedPair();
await Promise.all([
createTmuxMcpServer(server, {
environment: { LIBTMUX_TOOLSETS: "inspect,execute" },
}).connect(serverSide),
client.connect(clientSide),
]);
return client;
}

Excerpt of examples/mcp-agent/mcp-agent.ts, which bun run typecheck && bun run test runs in the repository's CI (.github/workflows/typescript.yml).

Workspace Manager

Short-hand panes

A pane can be one command written as a string, and a command list can be one string.

session_name: shorthands
windows:
- window_name: long form
panes:
- shell_command:
- echo 'did you know'
- echo 'you can inline'
- shell_command: echo 'single commands'
- echo 'for panes'
{
"session_name": "shorthands",
"windows": [
{
"window_name": "long form",
"panes": [
{
"shell_command": [
"echo 'did you know'",
"echo 'you can inline'"
]
},
{
"shell_command": "echo 'single commands'"
},
"echo 'for panes'"
]
}
]
}

Converted from the YAML; the tests load the YAML.

0:long form
Window 0, long form: 3 panes, tiled❯ echo 'did you know'❯ echo 'you can inline'❯ echo 'single commands'❯ echo 'for panes'
no layout set, so the port tiles the panes

Save the file as workspace.yaml and load it with:

tmux-workspace load workspace.yaml

Workspace file: packages/workspace-cli/tests/fixtures/tmuxp-examples/shorthands.yaml. Tested by every tmuxp example workspace loads through the CLI (packages/workspace-cli/tests/tmuxp-examples.test.ts; a test runs the command).

Install @libtmux/workspace-cli

The workspace package is separate from the core library.

Dependency cooldowns

The checkbox turns a cooldown on and off for every picker on the site. It applies to what an install pulls in; the package itself, and the libtmux it pins, stay current.

Cooldown type
What are cooldowns?

Cooldowns delay picking up newly uploaded packages so the community has time to spot supply-chain attacks before you install. Most package managers don't apply one by default; you opt in through a global setting in your tool's configuration.

Learn more at cooldowns.dev · Datadog Security Labs writeup.

$ npx -y @libtmux/workspace-cli --help
$ npx -y @libtmux/workspace-cli --help

npx ignores --min-release-age-exclude, so a cooldown here would also hold back the release this runs. This command runs without one.

$ npx -y --min-release-age=0 @libtmux/workspace-cli --help

Runs tmux-workspace without installing it. Pass a command such as load ./workspace.yaml in place of --help.

$ bunx --bun @libtmux/workspace-cli --help
$ bunx --bun @libtmux/workspace-cli --help

Bun cannot exempt @libtmux/workspace-cli from its own cooldown, so a cooldown here would also hold back the release this installs. This command runs without one.

$ bunx --bun --minimum-release-age=0 @libtmux/workspace-cli --help

--bun runs the command on Bun. Without it, bunx runs the command on Node when Node is installed, because its shebang names node.

$ pnpm dlx @libtmux/workspace-cli --help
$ EXCLUDE='["@libtmux/workspace-cli","libtmux"]' && \
pnpm_config_minimum_release_age=10080 \
    pnpm_config_minimum_release_age_exclude="$EXCLUDE" \
    pnpm dlx @libtmux/workspace-cli --help
$ pnpm_config_minimum_release_age=0 pnpm dlx @libtmux/workspace-cli --help
$ yarn dlx @libtmux/workspace-cli --help
$ YARN_NPM_MINIMAL_AGE_GATE=7d \
    YARN_NPM_PREAPPROVED_PACKAGES=@libtmux/workspace-cli,libtmux \
    yarn dlx @libtmux/workspace-cli --help
$ YARN_NPM_MINIMAL_AGE_GATE=0 yarn dlx @libtmux/workspace-cli --help

Yarn refuses a release younger than its npmMinimalAgeGate, one day by default.

$ npm install -g @libtmux/workspace-cli
$ npm install -g \
    --min-release-age=7 \
    --min-release-age-exclude=@libtmux/workspace-cli \
    --min-release-age-exclude=libtmux \
    @libtmux/workspace-cli
$ npm install -g --min-release-age=0 @libtmux/workspace-cli

Puts tmux-workspace on PATH, so a workspace loads with tmux-workspace load ./workspace.yaml.

$ pnpm add -g @libtmux/workspace-cli
$ EXCLUDE='["@libtmux/workspace-cli","libtmux"]' && \
pnpm_config_minimum_release_age=10080 \
    pnpm_config_minimum_release_age_exclude="$EXCLUDE" \
    pnpm add -g @libtmux/workspace-cli
$ pnpm_config_minimum_release_age=0 pnpm add -g @libtmux/workspace-cli
$ bun add -g @libtmux/workspace-cli
$ bun add -g @libtmux/workspace-cli

Bun cannot exempt @libtmux/workspace-cli from its own cooldown, so a cooldown here would also hold back the release this installs. This command runs without one.

$ bun add -g --minimum-release-age=0 @libtmux/workspace-cli
Build the workspace from a program
examples/workspace/workspace.ts
export const DEVELOPMENT_WORKSPACE = {
session_name: "workspace-example",
windows: [
{ panes: ["sleep 30", "sleep 30"], window_name: "editor" },
{ panes: ["sleep 30"], window_name: "server" },
{ window_name: "logs" },
],
} satisfies WorkspaceInput;
/** Apply the package's declarative workspace to a server. */
export function buildWorkspace(server: Server): Promise<Session> {
return applyWorkspace(server, DEVELOPMENT_WORKSPACE);
}

Excerpt of examples/workspace/workspace.ts, which bun run typecheck && bun run test runs in the repository's CI (.github/workflows/typescript.yml).

Packages and integrations

API references

All TypeScript versions and install instructions

Esc

Type to search.

Reference previewOpen reference