voiceos-tmux-mcp
Provides tools to inspect and interact with tmux panes, enabling the management of Claude Code sessions that run within tmux.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@voiceos-tmux-mcpWhat are my Claude Code sessions doing right now?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
voiceos-tmux-mcp
A VoiceOS integration for steering hand-started Claude Code sessions by voice.
Claude Code sessions started in a terminal register themselves under
~/.claude/sessions/ and record the tmux pane they are running in. This server reads that
registry, matches each entry against the panes tmux reports as alive, and exposes the result
as nine MCP tools. That makes it possible to ask aloud what is running, what a session said,
what it is waiting on, and to answer it — without leaving whatever you were doing.
It runs as a remote-mcp integration whose URL is on this Mac: http://127.0.0.1:7392/mcp.
"Remote" is about the transport, not the location — VoiceOS makes an HTTP call to a server
you start here, rather than spawning it. That is forced by the app: installation goes by URL,
not by folder. The listener is bound to 127.0.0.1, so only processes on this Mac can reach
it. Nothing is sent anywhere, and only local tmux sockets are ever addressed.
The server must be running or the integration is dead — see Start the server below.
Tools
Tool | What it does |
| Reports whether the integration can see tmux at all — the "is this alive" check when something breaks after an OS or app update. |
| Lists the running Claude Code sessions with the folder each is working in. |
| Reports whether a session is working, idle, or waiting, and what it is waiting on. |
| Reads back a session's recent turns from its transcript, for summarizing aloud. |
| Types an instruction into a session and submits it, then confirms the session actually moved. |
| Reads the approval prompt a waiting session is showing, with its numbered options and a fingerprint. |
| Answers that prompt by option number, refusing if the prompt changed since it was read out. |
| Starts a new session in a given folder, optionally with a first instruction. |
| Stops what a session is doing, without closing it. |
Related MCP server: claude-mux.mcp
Safety model
Every write target resolves through the session registry. No tool takes a pane, socket,
or window argument. A tool is given a session name, which is matched against
~/.claude/sessions/ entries that declare a tmux pane, have that pane alive right now,
and whose recorded pid is in the foreground process group of that pane's terminal. Panes
with no Claude Code registry entry — a bare shell, an editor, a tail -f — are not in the
set the lookup searches, so they are unreachable by construction rather than by a filter that
could be bypassed. This matters because text sent to a bare shell is executed.
The foreground-process-group condition is the kernel's own answer to "who receives a keystroke typed into this pane", and it is what closes three holes that a liveness check alone leaves open:
Two entries claiming one pane. A Claude Code started from inside another inherits
$TMUXand registers the same pane under its own pid. Under liveness alone both were steerable, and sending to the nested one typed into the other session's input box. The nested process does not own the pane's terminal, so it no longer resolves.A tmux server restart. Pane ids are monotonic within one server and never recycled, but a restart resets the counter to
%0and reallocates the whole id space — and the pty device numbers come back identical too. A registry entry written before a reboot therefore names a live, unrelated pane on the same/dev/ttysNNN. Its pid is dead, so it is refused.A session that has handed over its terminal to a pager, an editor or a shell. Text typed there lands in the pager, not the agent. Such a session is reported as running but not accepting input, and is never a write target.
It fails closed everywhere: an unreadable tty, a failed ps, or a pid that cannot be
determined all mean the session does not resolve. A session that silently disappears is an
annoyance; a wrong target is a stray keystroke into somebody's terminal. One race remains and
is not closable from here: the foreground group can change between the check and the
keystroke. The window is milliseconds, not eliminated.
Three further guards:
Empty-input check.
send_to_claude_sessionrefuses to type when the session already has text in its input box, rather than merging into a half-written line.Prompt fingerprint.
read_claude_prompthashes the entire prompt block.answer_claude_promptre-reads the screen at the moment of answering and refuses if the hash differs — so an approval spoken for one command cannot land on a different one that appeared meanwhile.Explicit sockets. Every tmux invocation passes
-S <socket>. The server never relies on the ambient default socket.
There is deliberately no general "run a terminal command" tool: ls and rm -rf ~ would be
the same call differing only by a model-written string.
Start the server
./start.sh # leave it running; Ctrl-C stops itIt listens on http://127.0.0.1:7392/mcp. Port 7392 is fixed in server-http.mjs;
7391 belongs to the environment probe on this Mac. Confirm it is up and loopback-only:
curl http://127.0.0.1:7392/health # {"ok":true,...}
lsof -nP -iTCP:7392 -sTCP:LISTEN # must say 127.0.0.1:7392, never *:7392start.sh resolves node absolutely (/opt/homebrew/bin/node, then /usr/local/bin/node,
then PATH) and installs dependencies on first run if node_modules/ is absent, so a
stripped environment fails loudly instead of silently. It execs node, so Ctrl-C reaches
the server rather than a shell wrapper.
Starting it at login instead
com.seanchiu.voiceos-tmux-mcp.plist.sample is a launchd login agent that brings the server
up at login and restarts it if it dies. It is a sample — not installed and not loaded.
Install commands are in the comments at the top of the file. Two details there are
load-bearing: the job runs through zsh -lc because launchd hands a job a minimal PATH
(/usr/bin:/bin:/usr/sbin:/sbin), not the login shell's; and it execs node so KeepAlive
watches the server instead of a zsh parent.
Install into VoiceOS
With the server running, install by URL and give VoiceOS:
http://127.0.0.1:7392/mcpThe install-by-URL option lives under Settings → Agent Mode → Integrations. (The exact menu
wording is not recorded here — the env-probe integration was installed this way at
http://127.0.0.1:7391/mcp on this Mac and answered, but the label was never written down.)
VoiceOS handshakes with the endpoint and discovers the nine tools from the server itself.
voiceos.integration.json declares the same URL under runtime, and its tool list is what
verify.mjs checks the server against.
The stdio path is still here
server.mjs + run.sh still work and still speak stdio, for a local-mcp runtime:
"runtime": { "kind": "local-mcp", "command": "/bin/zsh", "args": ["run.sh"] }Both entrypoints call buildServer() from lib/tools.mjs, so the nine tools are defined
once and the two transports cannot drift apart.
Verify
node verify.mjsPrints the live sessions the state layer can see, then checks that the tools declared in
voiceos.integration.json and those registered in lib/tools.mjs are the same set — which
covers both entrypoints, since both build from that one file. Exit code 1 means drift — the
failure that installs cleanly and then exposes nothing. Run it before installing, and after
any change to the tool list.
verify.mjs is read-only: it issues only list-sessions / list-panes and reads two files.
It never sends keys, launches, or interrupts, so it is safe against live sessions.
Tests
npm testThe glob must stay quoted — node --test test/ (a bare directory) fails on Node 26.
Tests that need a real tmux server create their own on a scratch socket under /tmp and kill
only that one; they never touch the user's socket.
This server cannot be deployed
Maintenance
Related MCP Connectors
Use AI models for chat, image, and video generation from Claude Code and other MCP hosts.
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Carbon Voice MCP serves as a bridge that connects AI assistants like ChatGPT, Claude, and Cursor to a user's Carbon Voice account, turning voice messages and conversations into a private, on-demand knowledge base. It provides 28 specialized tools for comprehensive voice messaging management, including creating and sending messages, accessing conversation history with instant transcription, running AI actions (summarization, TLDR generation, meeting notes), and managing workspace collaboration through folders, contacts, and team communications.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive tmux session management through MCP, allowing users to list sessions, send commands, set up automated messaging timers, and control tmux processes directly from Claude Desktop. Provides both real-time session monitoring and automated workflow features like exit/continue cycles.2MIT
- FlicenseNot gradedqualityDmaintenanceGives Claude Code terminal control and multi-agent coordination through tmux sessions.4-
- AlicenseAqualityDmaintenanceMCP server for orchestrating multiple Claude Code instances via tmux, enabling spawning, reading, sending, listing, and killing sessions.521 npm2MIT
- AlicenseNot gradedqualityAmaintenanceEnables Claude Code sessions to query fleet status, focus terminals, and manage sessions programmatically via MCP tools.1MIT