[2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/tech/balda-executor-mcp.md
This commit is contained in:
1 parent
c3ced58ee1
commit
218165c8c7
2 files changed
+118
-12
No files matched your search
@@ -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. Версии образа
|
||||
|
||||
|
||||
Reference in new issue
Block a user