[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/tech/balda-executor-mcp.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 22:23:17 +06:00
1 parent c3ced58ee1
commit 218165c8c7
2 files changed
+118 -12

No files matched your search

+13 -5
View File
@@ -14,8 +14,9 @@
| 2 | Мост FastAPI :8790 | ✅ работает под launchd, туннель `agents-bridge.qentra.top` заведён Alex'ом |
| 3 | Сеть Kraken → Mac | ✅ **Cloudflare Tunnel** (постоянно); LAN остаётся для отладки |
| 4 | Секция «Agents» в Dashboard | ✅ активирована и проверена |
| 5 | Связка Балда → мост | ✅ конфиг переведён на `type: http`, образ 0.2.0, `tools/list` из docker-сети |
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге Балды |
| 5 | Связка Балда → мост | ✅ конфиг `type: http`, образ 0.2.0, `tools/list` из docker-сети |
| 6 | Obsidian MCP для Балды | ✅ контейнер `obsidian-mcp` на Kraken + `type: sse` в конфиге (транспорт проверен) |
| 6a | 🔴 Инструменты MCP в сессии Балды | 🔴 **блокер:** снапшот каталога в `state.db` содержит только `builtin` → Балда «MCP не вижу». См. [[personal/tech/balda-executor-mcp]] §10g |
| 7 | Приёмка от аккаунта Alex | 🔴 блокер: авторизация CLI |
---
@@ -292,9 +293,16 @@ lsof -nP -iTCP:8790 -sTCP:LISTEN
- obsidian
```
См. §11 ниже.
3. 🟡 Проверить, что инструменты видны в сессии Балды — **постом от админа** в
Mattermost. ⚠️ Пост **от имени самого бота** Балда игнорирует (правильно);
для теста нужен пост от админского токена (`mattermost mmctl --local token generate admin …`).
3. 🔴 **MCP-инструменты не видны в сессии Балды.** Транспорты и сети проверены живьём
(оба сервера отвечают 200 из docker-сети), Балда стартует без ошибок и отвечает в чате —
но говорит «MCP не вижу». **Корень найден:** снапшот каталога в `state.db`
(`balda.appruntime_catalog_snapshot:*`) содержит **только `builtin`** — источники
`configured` не скомпилированы; плюс сессия закреплена за старым `RuntimeSnapshotID`.
Разбор и механика — [[personal/tech/balda-executor-mcp]] §10g.
⏳ Ждёт решения Alex: **A)** вычистить ключи снапшотов из `state.db` (рекомендовано),
**B)** полный сброс `state.db`.
⚠️ Тест-пост должен идти **от админского токена** — посты от имени самого бота Балда
игнорирует (правильно): `docker exec mattermost mmctl --local token generate admin …`.
4. 🔴 Логин CLI (Alex вручную) — без него сквозная приёмка невозможна.
5. 🔴 **Завести Access Application** на `agents-bridge.qentra.top` — сейчас туннель
отвечает 200 без авторизации (проверено). Защита — только `X-Bridge-Secret`.
+105 -7
View File
@@ -1,8 +1,10 @@
# balda-executor-mcp — MCP-сервер делегирования задач (шаг 5 Фазы 4)
> Создан 2026-09-26. Шаг 5 плана [[personal/plans/balda-mm-phase4-agents]].
> Статус: **собран, задеплоен на Kraken, образ обновлён до 0.2.0; конфиг Балды
> переведён на `type: http`, Obsidian MCP прописан рядом** — ждёт сквозной проверки в чате.
> Статус: **собран, задеплоен на Kraken, образ `0.2.0`; конфиг Балды — `type: http` +
> `obsidian: sse`. Балда стартует без ошибок и отвечает в чате.**
> 🔴 **Блокер:** MCP-инструменты не попадают в сессию — корень в снапшоте каталога
> (`state.db`), см. **§10g**. Ждёт решения Alex (вариант A/B).
> Связка: Балда (Kraken) → MCP → мост (Mac :8790) → контейнер-исполнитель.
---
@@ -276,16 +278,112 @@ docker exec balda-executor-mcp python3 -c "import uvicorn, fastapi, httpx; print
---
## 10g. 🔴🔴 ГЛАВНЫЙ КОРЕНЬ: MCP-серверы не попадают в сессию из-за снапшота каталога
Симптом: **все транспорты верны, оба сервера отвечают 200 из docker-сети, а Балда в чате
говорит «MCP не вижу, ни одного сервера».** Ошибок в логах нет. `/reset` при этом
**перечисляет** серверы — то есть ID прочитаны из YAML, но инструменты не подключены.
### Диагностика (порядок)
```bash
# 1. Есть ли вообще слова obsidian / executor-mcp в логе Балды?
docker logs balda-mm 2>&1 | grep -ci "obsidian" # → 0
docker logs balda-mm 2>&1 | grep -ci "executor-mcp" # → 0
```
Если **0** — проблема не в транспорте и не в сети: каталог не публикует эти источники.
```bash
# 2. Что лежит в снапшотах каталога (state.db)
STATE=/srv/dev-disk-by-uuid-49e8f586-…/docker/balda/state
strings "$STATE/state.db" | grep -o 'balda.appruntime_catalog_snapshot:[a-f0-9]*' | head
strings "$STATE/state.db" | grep -A1 'catalog_snapshot' | grep -o '"kind":"[a-z]*","name":"[a-z-]*"' | sort -u
```
**Показательный результат (баг):**
```json
"sources":[{"id":{"kind":"builtin","name":"balda"}, …}]
```
В снапшоте **только `builtin`**. Источников `configured` (наши `executor-mcp`/`obsidian`)
нет — значит `PreparePluginCandidate` их не скомпилировала.
### Механика (по коду, `internal/apps/balda/`)
| Звено | Файл | Что делает |
|---|---|---|
| параметры модуля | `catalogapp/module.go` | `params.Norma.MCPServers` ← `runtime.mcp_servers` |
| сборка | `catalogapp/runtime.go:81` | `configuredMCP: configuredMCPSources(configured)` |
| компиляция | `runtime.go:128` `PreparePluginCandidate` | `sources := [builtin] + r.configuredMCP + skills + plugins` |
| публикация | `runtime.go:168` `PublishCandidate` | `persistSnapshot` → `store.PublishApplication` → **`r.mcp.Reconcile(ctx, retained)`** |
| привязка к сессии | `runtime.go:188` `ResolveEffectiveSnapshot` | если у сессии есть `record.RuntimeSnapshotID` → берётся **именно он** |
| выдача ID | `module.go:57` `AcquireMCPServerIDs(ctx, snapshot.ID)` | ID MCP берутся из **снапшота**, не из конфига |
🔴 **Два независимых условия, оба обязаны выполниться:**
1. `PublishCandidate` должен быть **вызван** — он единственный, кто дёргает
`mcp.Reconcile`. Вызывается при публикации кандидата (плагины/скиллы/старт).
2. Сессия **не должна** быть закреплена за старым `RuntimeSnapshotID`.
Привязка вечная: строка `record.RuntimeSnapshotID != ""` → старый снапшот навсегда.
📌 **Из этого следует:** правка `runtime.mcp_servers` в YAML **не влияет** на уже
существующие сессии. Нужно новое состояние каталога + новые сессии.
### Правило для будущих правок MCP в Балде
1. Залить конфиг.
2. Убедиться, что `PublishCandidate` отработает (новый каталог) — иначе снапшот остаётся старым.
3. Создать **новую** сессию (не переиспользовать старую — она прибита к снапшоту).
4. Проверить **постом от админского токена** (не от бота — свои посты Балда игнорирует):
```bash
docker exec mattermost mmctl --local token generate admin whale-check --json | jq -r '.[0].token'
```
### Открытый вопрос на конец сессии
Требует решения Alex (удаление данных → нужно подтверждение):
- **A)** точечно вычистить ключи `balda.appruntime_catalog_snapshot:*` из `state.db`
(owner/настройки целы, снапшоты пересоберутся) — рекомендовано;
- **B)** полный сброс `state.db` (заново `bootstrap-admin` + owner-авторизация).
⚠️ `catalog-revisions/` на хосте — `drwx------ root`, обычным пользователем не читается
(`Permission denied`). Смотреть только через `strings state.db`.
---
## 11. Что осталось
1. ✅ **`runtime.mcp_servers.executor-mcp` → `type: http`** — сделано, залито на Kraken.
2. ✅ **`obsidian` прописан рядом** (`type: sse` → `http://obsidian-mcp:3101/sse`).
3. ✅ Образ обновлён до `0.2.0` на Kraken, `tools/list` отдаёт 3 инструмента из docker-сети.
4. 🟡 Перезапустить Балду и убедиться, что инструменты обоих серверов видны в сессии
(последняя правка `type: http` в конфиге **залита в `/tmp/balda-cfg/config.yaml`**, но
на момент окончания сессии перезапуск с валидным типом ещё не подтверждён сквозным тестом).
5. 🔴 Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
6. 🔴 Логин CLI внутри `balda-executor` — делает **Alex вручную**.
4. ✅ Балда перезапущена с валидным типом — **стартует без ошибок**, `Balda bot started`,
`type: http` принят валидатором. Сквозной пост отработан, Балда **ответила**.
5. 🔴 **Блокер:** Балда отвечает «MCP не вижу». Корень — снапшот каталога (**§10g**):
в `state.db` снапшот содержит только `builtin`. Нужно решение Alex (вариант A или B, §10g).
6. 🔴 Сквозная приёмка: `executor_run` из чата → задача на Mac → результат.
7. 🔴 Логин CLI внутри `balda-executor` — делает **Alex вручную**.
### Что проверено живьём (2026-09-26, конец сессии)
| Проверка | Результат |
|---|---|
| `executor-mcp:8890/mcp` из docker-сети | HTTP 200 + `mcp-session-id` ✅ |
| `obsidian-mcp:3101/sse` из docker-сети | 200 + `text/event-stream` + `event: endpoint` ✅ |
| `executor-mcp:8890/sse` (не тот эндпоинт) | 404 — правильно ✅ |
| `obsidian-mcp:3101/mcp` (не тот эндпоинт) | 400 — правильно ✅ |
| туннель `agents-bridge.qentra.top/healthz` из `mattermost` | 200 через Cloudflare ✅ |
| Балда после правки `type: http` | старт без ошибок, ответ в чате ✅ |
| слова `obsidian`/`executor-mcp` в логе Балды | **0** ❌ → корень в снапшоте (§10g) |
📌 **Метод проверки транспорта:** у каждого сервера ровно один рабочий эндпоинт.
Быстрый тест из `mattermost` (там есть `curl`, в образе Балды — нет):
```bash
docker exec mattermost sh -c 'curl -sS -m 8 -o /dev/null -w "HTTP %{http_code}\n" -H "Accept: text/event-stream" http://obsidian-mcp:3101/sse'
```
## 12. Версии образа