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

  1. A current Toowl install. Desktop install puts both toowl and toowld on your PATH (and inside Toowl.app on macOS).
  2. 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:

  1. Call roost.spawn with argv = ["bash", "-lc", "exec bash"] and cwd set to the project directory. That returns a session_id.
  2. Call roost.inject_input with bytes = "git status\\n". The trailing newline is what presses Enter.
  3. Call roost.wait_for_idle so it does not snapshot mid-print.
  4. Call roost.snapshot_text and 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.

ToolWhat it doesArguments
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_idle after you inject a command and just want the prompt back. Good default.
  • wait_for_pattern when you know a unique string will appear — a test result, a URL, Ready.
  • wait_for_command_exit when 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).

VariableMeaning
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 0600 and only accepts connections from the same user id. Another account on the machine cannot open it.
  • roost.spawn and roost.inject_input can 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