Prompts

MCP prompts are reusable, parameterised text templates the server ships to its clients. A client renders a prompt by calling prompts/get; the rendered text is what the model sees. libtmux-mcp’s prompts are short workflow recipes — the MCP-shaped counterpart to the longer narrative recipes in Recipes.

Available prompts

run_and_wait

Execute a shell command through run_command and inspect its typed result.

run_and_wait
diagnose_failing_pane

Gather pane context and produce a root-cause hypothesis without taking action.

diagnose_failing_pane
build_dev_workspace

Set up a 3-pane editor / shell / logs layout shell-agnostically.

build_dev_workspace
interrupt_gracefully

Send SIGINT and verify the shell prompt returns, refusing to auto-escalate.

interrupt_gracefully

Tip

Most MCP clients render prompts via a slash-command UI (/<server>:<prompt>). For tools-only clients that don’t expose prompts, set LIBTMUX_MCP_PROMPTS_AS_TOOLS=1 in the server environment to surface them as list_prompts / get_prompt tools instead.


run_and_wait

run_and_wait
prompt
prompt
run_and_wait

Run a shell command in a tmux pane and wait for completion.

Use when the agent needs to execute a single shell command, wait for completion, and inspect exit status plus output.

Why use this instead of send_keys + capture_pane polling? run_command sends the command, waits through a private tmux signal, captures tail-preserved output, and returns a RunCommandResult. That removes the manual channel plumbing from the common authored-command workflow.

Arguments

Argument

Type

Required

Description

command

str

yes

The shell command to run.

pane_id

str

yes

Target pane (e.g. %1).

timeout

float

no

Maximum seconds to wait for command completion. Default 60.

Sample render (command="pytest", pane_id="%1"):

Run this shell command in tmux pane %1, wait until it
finishes, and inspect the typed result:

```python
result = run_command(
    pane_id='%1',
    command='pytest',
    timeout=60.0,
    max_lines=100,
)
```

Use `result.exit_status`, `result.timed_out`, and `result.output`
to decide what happened. Do NOT use a `send_keys` + `capture_pane`
retry loop for authored commands — `run_command` already performs
deterministic completion and returns tail-preserved output.

If the task needs persistent shell state or TUI keystrokes instead of
a one-shot shell command, use `send_keys` or `send_keys_batch`, then
observe later output with `capture_since`.

Single-line renders omit suppress_history, so MCP calls use the server’s enabled-by-default setting. If command contains a carriage return or line feed, the prompt instead renders suppress_history=False and warns that the shell may record the multiline command. See History suppression for the Bash, Zsh, and Fish behavior behind both cases.

For custom shell composition that falls outside run_command, compose tmux wait-for -S <channel> yourself and call wait_for_channel. Keep that as the low-level escape hatch, not the default command-running recipe.

diagnose_failing_pane

diagnose_failing_pane
prompt
prompt
diagnose_failing_pane

Gather pane context and propose a root-cause hypothesis.

Use when something visibly went wrong in a pane and the agent needs to investigate before deciding what to fix. Produces a plan, not an action.

Why use this instead of just calling capture_pane? The recipe prefers snapshot_pane readonly, which returns content + cursor position + pane mode + scroll state in one call — saving a follow-up get_pane_info round-trip. It also explicitly forbids the agent from acting before it has a hypothesis, which prevents “fix the symptom” anti-patterns. For repeated observation, it routes follow-up reads through capture_since readonly cursors instead of full pane captures.

Arguments

Argument

Type

Required

Description

pane_id

str

yes

The pane to diagnose.

Sample render (pane_id="%1"):

Something went wrong in tmux pane %1. Diagnose it:

1. Call `snapshot_pane(pane_id="%1")` to get content,
   cursor position, pane mode, and scroll state in one call.
2. If the content looks truncated, re-call with `max_lines=None`.
3. If you need to watch the pane across more than one turn, call
   `capture_since(pane_id="%1")`, keep the returned cursor,
   and pass it to later `capture_since(cursor=...)` calls.
4. Identify the last command that ran (look at the prompt line and
   the line above it) and the last non-empty output line.
5. Propose a root cause hypothesis and a minimal command to verify
   it (do NOT execute anything yet — produce the plan first).

build_dev_workspace

build_dev_workspace
prompt
prompt
build_dev_workspace

Construct a simple 3-pane development session.

Use when the operator wants a fresh 3-pane workspace with editor on top, terminal bottom-left, and a logs pane bottom-right — the most common shape for active development.

Why use this instead of describing the layout in free form? The recipe uses real parameter names that match the tools’ actual signatures (session_name=, pane_id=, direction="below") so an agent following it verbatim never hits a validation error. It also explicitly avoids waiting for shell prompts after launching vim / watch / tail -f — the kind of guidance that would deadlock an agent following naïve “wait for the prompt between each step” advice.

Arguments

Argument

Type

Required

Description

session_name

str

yes

Name for the new session.

log_command

str

no

Command to run in the logs pane. Defaults to an OS-neutral watch -n 1 date so the recipe does not assume Linux log paths. Pass e.g. "tail -f /var/log/syslog" on Linux or "log stream --level info" on macOS.

Pass e.g. "tail -f /var/log/syslog" on Linux or "log stream --level info" on macOS as the log_command to override the OS-neutral default.

Sample render (session_name="dev"):

Set up a 3-pane development workspace named
'dev' with editor on top, a shell on the bottom-left, and
a logs tail on the bottom-right:

1. `create_session(session_name="dev")` — creates the
   session with a single pane (pane A, the editor). Capture the
   returned `active_pane_id` as `%A`.
2. `split_window(pane_id="%A", direction="below")` — splits off
   the bottom half (pane B, the terminal). Capture the returned
   `pane_id` as `%B`.
3. `split_window(pane_id="%B", direction="right")` — splits pane B
   horizontally (pane C, the logs pane). Capture the returned
   `pane_id` as `%C`.
4. Launch the editor and the log command via {tooliconl}`send-keys`:
   `send_keys(pane_id="%A", keys="vim")` and
   `send_keys(pane_id="%C", keys='watch -n 1 date')`. Leave pane B
   at its fresh shell prompt — nothing needs to be sent there. No
   pre-launch wait is required: tmux buffers keystrokes into the
   pane's PTY whether or not the shell has finished drawing, so
   `send_keys` immediately after `split_window` is safe and
   shell-agnostic.
5. Optionally confirm each program drew its UI via
   `wait_for_text(pane_id="%A", patterns=null, timeout=3.0)`
   (and similarly for `%C`). Omitting `patterns` makes this a
   "did anything new get printed?" check — it works whether the
   pane shows a prompt glyph, a vim splash screen, or a log tail,
   so no shell-specific regex is needed.

Use pane IDs (`%N`) for all subsequent targeting — they are stable
across layout changes; window renames are not.

interrupt_gracefully

interrupt_gracefully
prompt
prompt
interrupt_gracefully

Interrupt a running command and verify the prompt returned.

Use when the agent needs to stop a running command and confirm control returned to the shell — without escalating beyond SIGINT.

Why use this instead of just sending C-c? The recipe pairs the interrupt with a wait_for_text readonly against a common shell prompt glyph and an explicit instruction to stop and ask if the wait times out. That prevents the most dangerous failure mode — an agent auto-escalating to C-\\ (SIGQUIT, may core-dump) or kill without operator consent — by drawing a clear escalation boundary.

Arguments

Argument

Type

Required

Description

pane_id

str

yes

Target pane.

Sample render (pane_id="%1"):

Interrupt whatever is running in pane %1 and
verify that control returns to the shell:

1. `get_pane_info(pane_id="%1")` — note `pane_current_command`.
   That is the thing you are trying to change, and comparing it
   before and after is the only reliable answer.
2. `send_keys(pane_id="%1", keys="C-c", literal=False,
   enter=False)` — tmux interprets `C-c` as SIGINT.
3. `wait_for_text(pane_id="%1", patterns=["\$", "\#", "\%", ">"],
   regex=True, timeout=5.0)` — waits for a common shell prompt glyph.
   Adjust the patterns to match the user's shell theme.
   Do NOT put `\^C` in `stop`: the terminal echoes `^C` whenever SIGINT
   is DELIVERED, whether or not the process dies, so it marks the
   success path as a failure. Measured on sh and bash — the process
   exited and the wait still returned `outcome="stopped"`.
   The `wait_for_channel` pattern doesn't apply here — `C-c` is a
   signal, not a shell command, so there's no statement to compose
   `tmux wait-for -S` into.
4. Re-read `get_pane_info(pane_id="%1")`. If
   `pane_current_command` is back to a shell, the interrupt worked —
   trust this over the wait result, which can time out on a prompt
   whose glyph you did not predict.
5. Only if the command is UNCHANGED is the process ignoring SIGINT.
   Stop and ask the caller how to proceed — do NOT escalate
   automatically to `C-\` (SIGQUIT) or `kill`.

The shell-prompt regexes cover default bash / zsh — adjust for fish (> ), zsh + oh-my-zsh ( ), or starship ( ). When the patterns don’t match the user’s prompt theme the recipe times out and surfaces the situation to the caller, which is the right default for “I tried, can’t tell, what should I do?” workflows.