Skip to main content
Glama

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

smoke_test_tmux

Reports whether the integration can see tmux at all — the "is this alive" check when something breaks after an OS or app update.

list_claude_sessions

Lists the running Claude Code sessions with the folder each is working in.

check_claude_session_status

Reports whether a session is working, idle, or waiting, and what it is waiting on.

read_claude_session

Reads back a session's recent turns from its transcript, for summarizing aloud.

send_to_claude_session

Types an instruction into a session and submits it, then confirms the session actually moved.

read_claude_prompt

Reads the approval prompt a waiting session is showing, with its numbered options and a fingerprint.

answer_claude_prompt

Answers that prompt by option number, refusing if the prompt changed since it was read out.

launch_claude_session

Starts a new session in a given folder, optionally with a first instruction.

interrupt_claude_session

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 $TMUX and 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 %0 and 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_session refuses to type when the session already has text in its input box, rather than merging into a half-written line.

  • Prompt fingerprint. read_claude_prompt hashes the entire prompt block. answer_claude_prompt re-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 it

It 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 *:7392

start.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/mcp

The 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.mjs

Prints 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 test

The 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.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Augments MCP Server - A comprehensive framework documentation provider for Claude Code

  • Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.

  • User-owned memory for AI agents, Copilot, Claude, IDEs, CLIs, and chat apps over remote MCP.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/seanchiuai/voiceos-tmux-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server