diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index f3dffc51..c20ab9db 100755 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -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`. diff --git a/personal/tech/balda-executor-mcp.md b/personal/tech/balda-executor-mcp.md index 3caf1365..f7e461b0 100755 --- a/personal/tech/balda-executor-mcp.md +++ b/personal/tech/balda-executor-mcp.md @@ -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. Версии образа