Prerequisites: the GAIA TUI binary, started with the control API on:
gaia tui --control.Overview
The GAIA TUI can expose a loopback control API, and this MCP server wraps it as tools. An assistant then reads the screen, sends keys and types into chat in the session you are looking at — same process, same terminal, frames updating live. It is not a headless replica and not a replay. That makes two things possible that were not before:- Testing the TUI without a human at the keyboard. Send keys, wait for a condition, read the rendered screen, assert.
- Watching an assistant work. You keep your terminal open; the readiness gate, the questions, and the answers happen in front of you.
tea.Program with Send; the screen served
back is the exact frame the renderer just drew.
Available Tools
What view can be
gaia-tui boots straight into one agent, so there is nothing to browse and no
agent to pick. tui_status reports view as one of:
Wait on state, not on a substring, wherever you can:
tui_wait_for(state={"view": "chat"}) returns the moment the gate hands off,
and tui_wait_for(state={"blocker": "lemonade"}) tells you which row is
holding without parsing the remedy text — that wording is allowed to change,
the row key is not.tui_wait_for is the one that makes automation reliable. Without it, every
caller busy-polls tui_screen and races the render. On timeout it reports
what the screen actually contained, so a failure is debuggable.tui_send_keys, tui_send_text, and tui_resize return only once the input has
been consumed and redrawn, so reading tui_screen immediately after is
race-free. They report settled: false if the model was still busy — the input
is queued, so re-read the screen rather than assume it was dropped.
That is not the same as “the consequences finished”. Pressing enter to launch
an agent is consumed at once, while the view switch it starts arrives a moment
later — so wait on the outcome (tui_wait_for) rather than reading the screen
once and concluding nothing happened.
If the user has quit the TUI (or it is still starting), these tools fail with
“the TUI is not accepting input” instead of reporting a success. Bubble Tea
discards messages sent outside its event loop, so a success there would be a
lie. tui_status and tui_screen keep working, and tui_status reports
running: false.
Setup with Claude Code
1
Start the TUI with the control API
~/.gaia/tui/control.json
(mode 0600) holding the pid, port, and bearer token. The token is never
printed. Add --dev to log input counts, state transitions, and
wait resolution to stderr.For a fixed port: gaia tui --control-port 8770.2
Add the MCP server to Claude Code
3
Start a new Claude Code conversation
MCP tools are loaded at conversation start, so open a new one. Ask for
tui_status first — it confirms the assistant can see your session.Usage Examples
See why a launch is stuck
tui_status. A preflight view with blocker: "lemonade"
names the row without reading a single pixel; tui_screen then shows the
remedy the gate resolved for this machine.
Ask the agent something
tui_wait_for(state={"view": "chat"}) waits out the readiness gate, then
tui_send_text + tui_send_keys(["enter"]) sends the query, and
tui_wait_for(state={"streaming": False}) returns the moment the turn ends.
Check the layout at a small terminal
tui_resize(80, 24) followed by tui_screen.
Discovery and Trust
The MCP server finds the TUI by reading~/.gaia/tui/control.json — there is
no fixed port. Before trusting it, it checks both:
- the recorded pid is alive, and
GET /control/v1/statusanswers with our service id and a matching pid.
Configuration
Environment:
Troubleshooting
'No GAIA TUI is running'
'No GAIA TUI is running'
The control file does not exist. Start the TUI with
gaia tui --control
(plain gaia tui does not expose the API).'stale control file' / pid is not running
'stale control file' / pid is not running
A previous TUI crashed without cleaning up. Start a fresh one with
gaia tui --control — it overwrites the registration.Keys seem to do nothing
Keys seem to do nothing
Check
tui_status first. A help overlay swallows keys until it is
dismissed (esc), and the splash view takes no input at all — wait for
preflight or chat. If the screen looks unlaid-out, it has not received
a terminal size: call tui_resize.tui_wait_for keeps timing out
tui_wait_for keeps timing out
The timeout reports the screen it actually saw. Compare it with what you
were matching on — styling is stripped in plain mode, so match on the text,
not on box-drawing characters.
gaia tui --control --dev records input counts, state changes,
and waits in the TUI log. Input contents are never logged.Related
TUI control API reference
The HTTP endpoints, flags, and discovery file this server wraps.
Agent UI MCP Server
The same idea for the browser-based Agent UI.