docs(readme,config): english readme, tailnet addresses out of the examples
This commit is contained in:
@@ -1,39 +1,52 @@
|
||||
# t3code-mcp
|
||||
|
||||
MCP-коннектор к серверам [T3 Code](https://github.com/pingdotgg/t3code) (`архитектура.md` §5): диспетчер бобра запускает кодинг-треды на маке и на dell, ждёт их и читает результат. Ни шелла, ни терминала - только оркестрация по HTTP API T3 (`packages/contracts/src/environmentHttp.ts`).
|
||||
An MCP connector to [T3 Code](https://github.com/pingdotgg/t3code) 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](https://git.kotikot.com/beaver/beaver-agent); works with any MCP client.
|
||||
|
||||
| Тулза | Что делает |
|
||||
## Tools
|
||||
|
||||
| Tool | What it does |
|
||||
|---|---|
|
||||
| `t3_machines()` | машины из конфига, кто сейчас online (`/.well-known/t3/environment`), allowlist проектов |
|
||||
| `t3_projects(machine)` | проекты машины, прошедшие allowlist: путь, модель по умолчанию, число тредов |
|
||||
| `t3_dispatch(machine, project, prompt, title?, model?, thread_id?, watch?)` | `thread.create` + `thread.turn.start` (или только `turn.start` в существующий тред) → `thread_id` сразу; `watch=false` - не слать по треду события |
|
||||
| `t3_thread(thread_id, turns?)` | состояние без ожидания: тёрн, сессия, последние сообщения, тулзы, файлы, `pending` (вопрос/аппрув) |
|
||||
| `t3_wait(thread_id, timeout?)` | блокируется до `completed` / `interrupted` / `error`, вопроса или таймаута; та же вьюха |
|
||||
| `t3_answer(thread_id, answers)` | `thread.user-input.respond` на висящий вопрос треда (`answers`: id вопроса → label, список для multi_select) |
|
||||
| `t3_interrupt(thread_id)` | `thread.turn.interrupt` |
|
||||
| `t3_watch(thread_id?, enabled?)` | без аргументов - списки `watched` / `unwatched`; с обоими - включить или выключить события по треду |
|
||||
| `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.
|
||||
|
||||
Коннектор держит WS-подписку `orchestration.subscribeShell` на каждую машину (Effect RPC поверх JSON: `Request` → `Chunk` + `Ack`, `Ping` раз в 20 с, реконнект с `afterSequence`, снапшот при реконнекте переигрывает пропущенное). Треды, запущенные через `t3_dispatch`, отслеживаются (`T3CODE_MCP_STATE`, json на volume); переходы `running → completed/interrupted/error` и появление вопроса (`hasPendingUserInput`) превращаются в события `completed` / `interrupted` / `failed` / `question` и уходят `POST` в `T3CODE_MCP_HOOK_URL` (gateway `/hooks/t3code`, bearer `T3CODE_MCP_GATEWAY_TOKEN`, scope `api`) через persisted outbox с ретраями. Gateway делает из события инжект в мастер (`beaver-agent/config.py`, job `t3code`). Подписка включается на каждый `t3_dispatch` (в том числе на продолжение треда); снять её - `t3_watch(thread_id, enabled=false)` или сразу `t3_dispatch(..., watch=false)`: тред продолжает работать, просто молча.
|
||||
## Events
|
||||
|
||||
`t3_wait` - обычный долгий вызов, не MCP Task: Claude Code расширение Tasks не поддерживает, зато сам уводит вызов дольше двух минут в фоновую задачу (`CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS`). Треды бегут в `full-access` без аппрувов; тред ищется по всем машинам, если коннектор перезапускался.
|
||||
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, `POST`ed 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` (путь - `T3CODE_MCP_CONFIG`, в образе `/config/t3code.toml`), пример - `t3code.example.toml`. Токены - только из env (`token_env`), выпускаются на каждой машине: `t3 auth session issue --token-only --ttl 365d --label beaver-t3code-mcp`. `projects` - fnmatch по названию и по workspace root; что не совпало, для диспетчера не существует. Порт и адрес - `T3CODE_MCP_HOST` / `T3CODE_MCP_PORT` (8000, путь `/mcp`, `/healthz`).
|
||||
`t3code.toml` (path in `T3CODE_MCP_CONFIG`; in the image `/config/t3code.toml`), see `t3code.example.toml`:
|
||||
|
||||
## Разработка
|
||||
```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
|
||||
|
||||
```sh
|
||||
make sync # uv sync
|
||||
make check # ruff format --check, ruff check (ALL), ty, pytest
|
||||
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 t3-smoke # живой диспатч + ожидание
|
||||
uv run python scripts/smoke.py http://127.0.0.1:8000/mcp mac <project> # a live dispatch and wait
|
||||
```
|
||||
|
||||
## Деплой
|
||||
## Deploy
|
||||
|
||||
Сервис `t3code-mcp` в `beaver-agent/docker-compose.yml` (профиль `t3`), образ собирается из этой репы (`T3CODE_MCP_REF`), gateway подключает его как `McpServer.http(name="t3code", url="http://t3code-mcp:8000/mcp")` и отдаёт только диспетчеру.
|
||||
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.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
```toml
|
||||
[machines.mac]
|
||||
url = "http://100.65.207.48:3773"
|
||||
url = "http://<tailnet-ip>:3773"
|
||||
token_env = "T3_MAC_TOKEN"
|
||||
projects = ["t3-smoke", "/Users/h/projects/openprise/beaver/*"]
|
||||
model = "claudeAgent/claude-opus-5"
|
||||
|
||||
+2
-2
@@ -5,14 +5,14 @@
|
||||
# `model` is optional (`instance/model`); without it the project default applies.
|
||||
|
||||
[machines.mac]
|
||||
url = "http://100.65.207.48:3773"
|
||||
url = "http://<tailnet-ip>:3773"
|
||||
token_env = "T3_MAC_TOKEN"
|
||||
projects = ["*"]
|
||||
model = "claudeAgent/claude-opus-5"
|
||||
options = { effort = "high", contextWindow = "1m" }
|
||||
|
||||
[machines.dell]
|
||||
url = "http://100.76.140.93:3773"
|
||||
url = "http://<tailnet-ip>:3773"
|
||||
token_env = "T3_DELL_TOKEN"
|
||||
projects = ["*"]
|
||||
model = "claudeAgent/claude-opus-5"
|
||||
|
||||
Reference in New Issue
Block a user