t3code-mcp

An MCP connector to T3 Code servers. It fills the one gap T3 Code leaves for agents: they cannot create their own threads. With this connector an agent dispatches a coding thread on any machine that runs T3 Code, waits for it, answers its questions and reads the result - all through T3's HTTP API, with no shell or terminal involved.

Built for the dispatcher in beaver-agent; works with any MCP client.

Tools

Tool What it does
t3_machines() machines from the config, which are online now, their project allowlists
t3_projects(machine) allowlisted projects on a machine: path, default model, thread count
t3_dispatch(machine, project, prompt, title?, model?, thread_id?, watch?) creates a thread and starts a turn (or starts a turn in an existing thread); returns thread_id at once; watch=false starts it without reporting events
t3_thread(thread_id, turns?) the state without waiting: turn, session, last messages, tools, files, a pending question
t3_wait(thread_id, timeout?) blocks until completed, interrupted, failed, a question, or the timeout
t3_answer(thread_id, answers) answers a question the thread is waiting on (question id → label; a list for multi-select)
t3_interrupt(thread_id) interrupts the running turn
t3_watch(thread_id?, enabled?) without arguments lists the watched and unwatched threads; with both turns a thread's events on or off

t3_wait is an ordinary long call, not an MCP Task: Claude Code moves calls over two minutes into the background by itself (CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS). Threads run in full-access without approvals. A thread is looked up across all machines, so the connector may restart in between.

Events

The connector keeps a websocket subscription to every machine (orchestration.subscribeShell, Effect RPC over JSON, reconnect with replay). Threads started through t3_dispatch are tracked in a state file; the transitions running → completed / interrupted / failed and a new pending question become events, POSTed to T3CODE_MCP_HOOK_URL with a bearer through a persisted outbox with retries. In the beaver setup that URL is the gateway's /hooks/t3code, and the event lands in the master conversation as an urgent inject. Every t3_dispatch subscribes its thread, a continuation included; t3_watch(thread_id, enabled=false) or t3_dispatch(..., watch=false) lets a thread run silently.

Configuration

t3code.toml (path in T3CODE_MCP_CONFIG; in the image /config/t3code.toml), see t3code.example.toml:

[machines.mac]
url = "http://<tailnet-ip>:3773"
token_env = "T3_MAC_TOKEN"
projects = ["*"]
model = "claudeAgent/claude-opus-5"
options = { effort = "high", contextWindow = "1m" }

Tokens never live in the file: token_env names the variable, and the token is issued on each machine with t3 auth session issue --token-only --ttl 365d --label beaver. projects are fnmatch patterns against a project's title and workspace root; a project that matches nothing does not exist for the client. The server listens on T3CODE_MCP_HOST / T3CODE_MCP_PORT (8000), MCP at /mcp, health at /healthz.

Develop

make sync      # uv sync
make check     # ruff format --check, ruff check, ty, pytest
make run       # T3CODE_MCP_CONFIG=... uv run python -m t3code_mcp
uv run python scripts/smoke.py http://127.0.0.1:8000/mcp mac <project>   # a live dispatch and wait

Deploy

A service in beaver-agent/docker-compose.yml (profile t3), image built from this repository at T3CODE_MCP_REF. The gateway attaches it as McpServer.http(name="t3code", url="http://t3code-mcp:8000/mcp") and hands it to the dispatcher only.

S
Description
MCP connector to T3 Code servers on the mac and dell - dispatch coding threads for the beaver dispatcher
Readme
251 KiB
Languages
Python 98.9%
Dockerfile 0.8%
Makefile 0.3%