Documentation
Toowl MCP
Let Grok, Claude Code, and Cursor spawn Toowl sessions, type into them, and read the screen — all through the Model Context Protocol.
Toowl MCP is how an AI agent drives a real Toowl
terminal. The agent talks to toowl mcp over stdio; that
command starts the toowld daemon if needed and exposes a
small set of roost.* tools. The agent can open a shell,
type into it, wait for output, and read the viewport — the same
sessions you can attach to in the GUI or TUI.
This is Toowl as an MCP server. The opposite direction — plugging filesystem, GitHub, or other MCP servers into the Perch — is the MCP Hub Feather.
What you can do
- Start a long-lived shell (or any command) that keeps running after the agent turn ends.
- Type into that session as if you were at the keyboard, including newlines.
- Read a plain-text snapshot of what is on screen.
- Wait until output matches a pattern, the session goes idle, or a command exits.
- List every live session the daemon currently owns.
Sessions live in toowld, not in the agent. Close Grok or
Claude and the shells stay up. Open a workspace window later and
reattach. See Workspaces and
persistent sessions.
Prerequisites
-
A current Toowl install. Desktop install puts both
toowlandtoowldon yourPATH(and insideToowl.appon macOS). -
Confirm the daemon binary exists:
toowld --version which toowl
toowl mcp auto-starts the daemon. You do not need to run
toowld yourself unless you prefer a systemd or launchd
unit.
Connect an agent
Every host below runs the same command: toowl mcp. That
is the stdio MCP server. After you save the config, restart
the agent so it re-spawns the subprocess.
Grok
Add this to ~/.grok/config.toml:
[mcp_servers.toowl]
command = "toowl"
args = ["mcp"]
enabled = true
If toowl is not on the PATH Grok inherits
(common for Dock-launched apps), use the absolute path:
~/.cargo/bin/toowl after a cargo install, or
/Applications/Toowl.app/Contents/MacOS/toowl for the
bundled app.
Claude Code
Project file .mcp.json in the repo root, or Claude's user
MCP config:
{
"mcpServers": {
"toowl": {
"command": "toowl",
"args": ["mcp"]
}
}
} Cursor
Settings → MCP, or .cursor/mcp.json in the project:
{
"mcpServers": {
"toowl": {
"command": "toowl",
"args": ["mcp"]
}
}
} Claude Desktop
Edit the desktop config
(~/Library/Application Support/Claude/claude_desktop_config.json
on macOS) and add the same mcpServers.toowl block as Claude
Code. Restart Claude Desktop.
A first session
Once the host shows Toowl as connected, ask the agent something
concrete: “Start a bash session in this repo, run git status,
and tell me what you see.” Under the hood it will roughly:
-
Call
roost.spawnwithargv = ["bash", "-lc", "exec bash"]andcwdset to the project directory. That returns asession_id. -
Call
roost.inject_inputwithbytes = "git status\\n". The trailing newline is what presses Enter. -
Call
roost.wait_for_idleso it does not snapshot mid-print. -
Call
roost.snapshot_textand read the viewport as plain text.
You can attach to the same session from Toowl itself — palette Open Workspace, or the Workspaces Feather — and see the same scrollback.
Tools
All tools are namespaced roost.*. Arguments are JSON.
Hosts usually wrap them in the MCP tools/call envelope;
you do not type these by hand unless you are debugging.
| Tool | What it does | Arguments |
|---|---|---|
roost.spawn | Start a new session. Returns session_id. | argv (required, string array), cwd (optional path) |
roost.list | Every session the daemon knows, with name, status, cwd, and exit code. | none |
roost.snapshot_text | Plain-text viewport, plus cwd and whether a command is running. | session_id |
roost.inject_input | Write UTF-8 to the session's stdin. Include \\n to submit a line. | session_id, bytes |
roost.wait_for_pattern | Block until recent output matches a regex, or time out. | session_id, pattern, timeout_ms (default 10000) |
roost.wait_for_idle | Block until the session has been quiet for idle_ms. | session_id, idle_ms (default 500), timeout_ms (default 10000) |
roost.wait_for_command_exit | Block until the last command finishes (OSC 133) or the process exits. | session_id, timeout_ms (default 60000) |
When to wait
-
wait_for_idleafter you inject a command and just want the prompt back. Good default. -
wait_for_patternwhen you know a unique string will appear — a test result, a URL,Ready. -
wait_for_command_exitwhen the shell has OSC 133 integration (toowl's bundled bash snippet) and you need the exit code, not just “it printed something”.
Environment
Optional. Set these on the MCP subprocess if the defaults do not fit (tests, a second daemon, a non-standard install).
| Variable | Meaning |
|---|---|
TOOWLD_BIN | Absolute path to the toowld binary used for auto-spawn. |
TOOWLD_SOCKET | Control socket. Default is under the toowl state dir. |
TOOWLD_MCP_SOCKET | MCP listener socket (toowld-mcp.sock next to the control socket). |
On macOS the state dir is
~/Library/Application Support/toowl/.
On Linux it follows XDG
(~/.local/share/toowl/ or $XDG_RUNTIME_DIR/toowl/).
Security
-
The MCP socket is mode
0600and only accepts connections from the same user id. Another account on the machine cannot open it. -
roost.spawnandroost.inject_inputcan run anything your user can run. Treat a connected agent like a logged-in shell. - Traffic stays on the local machine. Nothing is proxied through toowl.dev.
-
Unix only. Windows does not yet host
toowl mcp.
Troubleshooting
The host says the server is disconnected, or “Broken pipe” on initialize
The MCP child exited before answering. Almost always toowld
is missing. Check:
toowld --version
toowl mcp --help
If toowld is not found, reinstall with
bash scripts/install.sh (that ships the daemon next to
the GUI) or
cargo install --path crates/toowl-roost-daemon --force
from a checkout. Then restart the agent.
A stale toowl-cli from an older cargo install is another
cause. Point the host at toowl mcp, not
toowl-cli mcp, unless that CLI is the same version as
the daemon.
Grok reports the terminal as “Unknown”
That is the terminal emulator brand, not MCP. Official Grok only
treats a terminal as Kitty-keyboard-capable when it recognises
TERM_PROGRAM. Toowl sets TERM_PROGRAM=toowl.
Until a Grok build lists Toowl as a known terminal, Shift+Enter in
Grok may need Alt+Enter. MCP still works independently of that
detection.
The agent connects but tools fail with “no such session”
The session_id is from a previous daemon. If you killed
toowld or pointed TOOWLD_SOCKET at a new
path, spawn again and use the new id. roost.list shows
what is actually live.
Injected commands never run
roost.inject_input writes the string as-is. A command
without a trailing newline sits on the prompt and is never submitted.
Send ls\\n, not ls.
Wait tools always time out
wait_for_command_exit needs OSC 133 marks from the
shell. Toowl's bundled bash snippet provides them; a bare
/bin/sh often does not. Use wait_for_idle
or wait_for_pattern in that case.
See also
- MCP Hub Feather — run other MCP servers inside Toowl
- Workspaces — attach a GUI window to the same sessions
- Persistent sessions — survive a daemon restart
- FAQ & troubleshooting