Files
obsidian-vault/personal/tech/balda-executor-mcp.md
T

191 lines
8.6 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# balda-executor-mcp — MCP-сервер делегирования задач (шаг 5 Фазы 4)
> Создан 2026-09-26. Шаг 5 плана [[personal/plans/balda-mm-phase4-agents]].
> Статус: **собран, задеплоен на Kraken, связка проверена до последнего звена**;
> конфиг Балды ещё указывает на stdio-вариант — требует переключения на `type: http`.
> Связка: Балда (Kraken) → MCP → мост (Mac :8790) → контейнер-исполнитель.
---
## 1. Зачем отдельный MCP-сервер
Балда **не может** сама запустить CLI на Mac: Mac за NAT, а `runtime.mcp_servers` —
это либо stdio-процесс внутри контейнера Балды, либо HTTP по сети.
Схема выбрана такая:
```
Балда (Kraken, контейнер balda-mm)
│ runtime.mcp_servers.executor-mcp
▼
MCP-сервер balda-executor-mcp (Kraken, контейнер в mattermost_mm-net) ← HTTP
│ X-Bridge-Secret
▼
мост agents-bridge (Mac, 192.168.1.57:8790) ← обычный HTTP по LAN
│ docker exec
▼
контейнер balda-executor (Mac) → CLI claude/codex
```
---
## 2. Расположение и файлы
| Что | Путь |
|---|---|
| Проект | `~/Developer/balda-executor-mcp/` |
| Сервер | `server.py` (stdio + HTTP в одном файле) |
| Образ | `balda-executor-mcp:0.1.0` (python:3.12-slim-bookworm) |
| Dockerfile | `~/Developer/balda-executor-mcp/Dockerfile` |
| Compose (Kraken) | `/home/kraken/mcp/docker-compose.yml` |
| Контейнер на Kraken | `balda-executor-mcp` (сеть `mattermost_mm-net`) |
| Порядковый порт | `:8890` внутри docker-сети, **наружу не публикуется** |
---
## 3. Инструменты MCP
| Инструмент | Аргументы | Что делает |
|---|---|---|
| `executor_run` | `prompt` (обяз.), `provider?` (`claude`/`codex`), `idempotency_key?` | ставит задачу, возвращает `task_id` немедленно |
| `executor_status` | `task_id` | статус + stdout/stderr/exit_code |
| `executor_health` | — | живость моста и контейнера-исполнителя |
Ошибки возвращаются как `isError: true` с человеческим текстом:
- 429 → «Исполнитель занят (лимит одновременных задач)»
- 404 → «задача … не найдена»
- прочие → «мост вернул N: …»
---
## 4. Переменные окружения
| Ключ | Смысл |
|---|---|
| `BRIDGE_URL` | базовый URL моста (например `http://192.168.1.57:8790`) |
| `BRIDGE_SECRET` | общий секрет, уходит в `X-Bridge-Secret` |
| `BRIDGE_TIMEOUT` | таймаут обычных вызовов (по умолчанию 60 с) |
| `MCP_HOST` / `MCP_PORT` | адрес HTTP-режима (по умолчанию `127.0.0.1:8890`) |
---
## 5. Два режима запуска
```bash
# stdio (Балда сама запускает процесс)
python3 server.py
# HTTP (отдельный контейнер, Балда ходит по URL)
python3 server.py --http --host 0.0.0.0 --port 8890
```
⚠️ **Использовать надо HTTP-режим.** Причины — §7.
---
## 6. Приёмка (2026-09-26) — что именно проверено
| Проверка | Результат |
|---|---|
| stdio: `initialize` → `tools/list` | 3 инструмента возвращены ✅ |
| stdio: `tools/call` health | живые данные моста ✅ |
| stdio: 429 / 500 / 404 | корректные `isError` с текстом ✅ |
| образ: тот же handshake внутри контейнера | ✅ |
| образ на Kraken: загрузка | `Loaded image: balda-executor-mcp:0.1.0` ✅ |
| Kraken → мост по LAN (curl из docker-сети) | `status: ok` ✅ |
| MCP в docker-сети → `executor_health` через мост | живые данные Mac ✅ |
| контейнер `balda-executor-mcp` | Up ✅ |
Тестовые скрипты: `/tmp/test-mcp-server.sh` (мок моста + stdio-проба),
`/tmp/kraken-test-mcp.sh` (ручной handshake из docker-сети Kraken).
---
## 7. 🔴 Питфолл: stdio-вариант нерабочий в этом окружении
Изначальный план был `type: stdio` + `cmd: ["docker", "run", ...]` — то есть Балда
сама запускает MCP-контейнер. **Это не работает по двум причинам:**
1. **`docker.sock` в контейнер Балды не смонтирован.** Проверено:
`docker inspect balda-mm` даёт только три тома (config/state/workspace) — сокета нет.
Значит `docker run` изнутри Балды невозможен.
2. **В образе Балды нет shell.** `docker exec balda-mm sh -c ...` →
`OCI runtime exec failed: exec: "sh": executable file not found in $PATH`
(образ из `scratch`). Отладка и запуск подпроцессов через shell недоступны.
**Решение:** MCP-сервер вынести в **отдельный контейнер** с HTTP-транспортом.
Конфиг Балды тогда:
```yaml
runtime:
mcp_servers:
executor-mcp:
type: http
url: "http://balda-executor-mcp:8890/mcp"
balda:
mcp_servers:
- executor-mcp
```
---
## 8. 🔴 Питфолл: `--chmod=644` для файла, запускаемого не-root
Dockerfile изначально делал `COPY server.py /app/server.py` + `USER mcpsvc` → при
запуске: `python3: can't open file '/app/server.py': [Errno 13] Permission denied`.
Фикс: `COPY --chmod=644 server.py /app/server.py`. Не-root пользователь должен уметь
**читать** код, иначе контейнер падает ещё до handshake.
Симптом обманчив — выглядит как поломка образа, а не как права файла.
---
## 9. Питфолл: `docker-compose` на Mac, `docker compose` на Kraken
| Хост | Как вызывать |
|---|---|
| Mac (Colima) | **`docker-compose`** (v5.1.3, отдельный бинарь) |
| Kraken | **`docker compose`** (плагин v5.1.3); `docker-compose` → `command not found` |
Одна и та же версия, но на разных хостах **разный способ вызова**. Скрипты деплоя
для двух хостов нельзя копировать без правки этой строки.
---
## 10. HTTP-transport: ручная проба (streamable)
```bash
docker run --rm --network mattermost_mm-net curlimages/curl:latest \
-s --max-time 10 -X POST http://balda-executor-mcp:8890/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}' -i
```
⚠️ Заголовок `Accept: text/event-stream` **обязателен** — сервер отвечает SSE.
Без него — отказ. Ответ отдаётся как `event: message\ndata: {...}`.
`GET /healthz` у MCP-сервера отдаёт `{"status":"ok","bridge_url":...,"secret_set":true}`.
---
## 11. Что осталось
1. **Переключить `runtime.mcp_servers.executor-mcp` на `type: http`** (сейчас там stdio-вариант с `docker run`).
2. Перезапустить Балду, убедиться, что инструменты видны в сессии.
3. Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
4. Логин CLI внутри `balda-executor` — делает **Alex вручную**.
---
## Связанное
- [[personal/plans/balda-mm-phase4-agents]] — план Фазы 4 (шаг 5)
- [[personal/tech/agents-bridge]] — мост :8790, к которому ходит этот MCP
- [[personal/projects/balda/balda-kraken-mm-deploy]] — деплой Балды на Kraken (конфиг, `.env`, сети)
- [[personal/tech/macos-docker-virtiofs-mount-limits]] — ограничения macOS Docker
- `~/Developer/balda-executor-mcp` — код
- скилл `native-mcp` — handshake streamable-http, питфоллы