> [!IMPORTANT] Актуальная конфигурация для Balda (2026-10-09) > Текущая Balda использует Codex ACP и Streamable HTTP: `type: http`, URL `http://host.docker.internal:3101/mcp`. > Указания ниже про Balda + SSE или `/sse` относятся к прежним клиентам/развёртываниям. Канонический runbook: [[personal/projects/balda/setup]]. # Obsidian MCP — текущая архитектура ## Факт: что стоит сейчас **Hermes native MCP client** (встроен в Hermes Agent, не wrapper). Конфиг в `~/.hermes/config.yaml` (и в `~/.hermes/hermes-whale/config.yaml`): ```yaml mcp_servers: obsidian: command: mcpvault args: - /Users/admin/obsidian ``` Пакет: **`@bitbonsai/mcpvault`** v0.12.1 (npm). Команда `mcpvault`. Hermes на старте: 1. Читает `mcp_servers` из config.yaml 2. Спавнит `mcpvault /Users/admin/obsidian` как subprocess 3. Init + list_tools → регистрирует инструменты как `mcp_obsidian_*` 4. Агент видит инструменты `mcp_obsidian_read_note`, `mcp_obsidian_patch_note`, и т.д. **Схема:** ``` Hermes (native MCP client) → spawn: mcpvault → reads/writes /Users/admin/obsidian/ ``` ### Инструменты, доступные через эту связку `read_note`, `write_note`, `patch_note`, `list_directory`, `delete_note`, `search_notes`, `move_note`, `move_file`, `read_multiple_notes`, `update_frontmatter`, `get_notes_info`, `get_frontmatter`, `manage_tags`, `get_vault_stats`, `list_all_tags`. ## Факт: что такое ~/scripts/obsidian-mcp-wrapper.js и почему он НЕ используется **Создан** 2026-05-09, когда вместо `mcpvault` стоял `npx obsidian-mcp` (старый пакет, автор Steven, ещё до переименования в `@bitbonsai/mcpvault`). Пакет постоянно падал: - ZodError на `"id": null` в notifications — `.strict()` валидация - Race condition при рестарте gateway - UTF-8 chunk split портил большие JSON - Зависания без watchdog Wrapper решал всё это. **Сейчас НЕ используется.** Конфиг в `~/.hermes/config.yaml`: ```yaml mcp_servers: obsidian: command: mcpvault # ← не node wrapper.js args: - /Users/admin/obsidian ``` **Почему:** после перехода на `@bitbonsai/mcpvault` (пришёл на смену старому obsidian-mcp), пакет стабильно работает с Hermes native MCP client. Wrapper стал не нужен. Конфиг поменяли, а doc не обновили. **Файл `~/scripts/obsidian-mcp-wrapper.js`** лежит на диске, не используется. Надо удалить. ## Факт: корень проблем с patch_note `mcp_obsidian_patch_note` падает с `"String not found"` — это **НЕ проблема транспорта**. Это проблема **самого mcpvault**: - Файл: `dist/src/filesystem.js`, строка 217 - Механизм: `fullContent.split(oldString).length - 1` - **Exact string match** — без trim, без fuzzy, без нормализации whitespace Workaround: использовать Hermes `patch()` (fuzzy matching, 9 стратегий) для сложных строк. `mcp_obsidian_patch_note` — только для простого текста без спецсимволов. ## Fact: Obsidian MCP на Kraken — есть, и это stdio-процесс Hermes Проверено 2026-09-26. На Kraken MCP-сервер **не является отдельным контейнером** — его спавнит агент Hermes как дочерний процесс. Первый поиск «нет контейнера → нет MCP» был **ошибочным**: искать надо было процессы, а не `docker ps`. ``` # на хосте Kraken ps aux | grep mcpvault → UID 10000 1168741 ... node /opt/data/.local/bin/mcpvault /vault PPID 5039 → /opt/hermes/.venv/bin/hermes gateway (контейнер hermes-kraken) ``` Конфиг (`/opt/data/config.yaml` **внутри** контейнера `hermes-kraken`): ```yaml mcp_servers: obsidian: command: /opt/data/.local/bin/mcpvault args: - /vault ``` `mcpvault` там — симлинк на npm-пакет: `/opt/data/.local/lib/node_modules/@bitbonsai/mcpvault/dist/server.js`. ### Волт на Kraken — реальный, не пустой ``` /home/kraken/obsidian -> /srv/dev-disk-by-uuid-49e8f586-…/obsidian (тот же HDD, что и docker-data) ``` Внутри: `personal/`, `family/`, `.obsidian/`, `.git/`, `_index.md` — полноценная git-реплика волта Mac. То есть **волт существует в обоих местах**, а не только на Mac. ### Итог: два независимых Obsidian MCP | Где | Как | Транспорт | Порт | |---|---|---|---| | Mac | контейнер `obsidian-mcp` (`mcp-proxy` → `mcpvault`) | SSE | `:3101`, публикуется на `0.0.0.0` | | Kraken | `mcpvault` как дочерний процесс `hermes-kraken` | **stdio** | нет | | Kraken (для Балды) | контейнер `obsidian-mcp` — **создан 2026-09-26** | SSE | `:3101`, только внутри `mattermost_mm-net` | 📌 `mcpvault` **не умеет HTTP** — только stdio (`--help` подтверждает: только `[vault-path]`). Чтобы отдать его по сети, нужен `mcp-proxy` (обёртка stdio → SSE). Именно так сделано на Mac и повторено на Kraken. --- ## Fact: obsidian-mcp на Kraken для Балды (создан 2026-09-26) Собрано для шага 5 [[personal/plans/balda-mm-phase4-agents]]: Балде нужен доступ к волту, чтобы читать документацию при формировании ТЗ. **Точная копия рабочей схемы с Mac** — пара `mcp-proxy` + `mcpvault`: | Что | Путь / значение | |---|---| | Проект (Mac) | `~/Developer/obsidian-mcp-kraken/` | | Dockerfile | `node:24-bookworm` + `@bitbonsai/mcpvault@0.11.0` + `mcp-proxy@6.4.4` | | Образ | `obsidian-mcp-image:0.1.0` (загружен на Kraken) | | Compose (Kraken) | `/home/kraken/obsidian-mcp/docker-compose.yml` | | Контейнер | `obsidian-mcp`, сеть `mattermost_mm-net` | | Волт | `/srv/dev-disk-by-uuid-49e8f586-…/obsidian` → `/vault` **`:ro`** | | Команда | `mcp-proxy --port 3101 -- mcpvault /vault` | 🔴 **Волт смонтирован только для чтения (`:ro`).** Балда читает документацию, но не правит её. Запись в волт через Балду — отдельное решение, требует апрува Alex. ```yaml services: obsidian-mcp: image: obsidian-mcp-image:0.1.0 container_name: obsidian-mcp restart: unless-stopped command: ["--port", "3101", "--", "mcpvault", "/vault"] volumes: - /srv/dev-disk-by-uuid-49e8f586-…/obsidian:/vault:ro networks: - mm-net # mattermost_mm-net, external ``` ### Питфоллы, пройденные при сборке | # | Питфолл | Симптом | Фикс | |---|---|---|---| | 1 | `--host` у `mcp-proxy` **нет** | контейнер падает на разборе аргументов | только `--port N -- `; слушает `0.0.0.0` по умолчанию | | 2 | Точка монтирования должна существовать в образе | `mkdir -p /vault` в Dockerfile + `WORKDIR /vault` | иначе mcpvault стартует не в том каталоге | ### Проверка ```bash # /sse отвечает 200 (и локально, и из docker-сети Kraken) curl -s -o /dev/null -w "%{http_code}\n" http://obsidian-mcp:3101/sse # ⚠️ Порт именно 3101, НЕ 8080. Проба на 8080 даёт "Connection refused" при живом # контейнере — mcp-proxy слушает порт из --port. ``` ✅ **ПРИМЕНЕНО 2026-09-26.** Конфиг Балды на Kraken теперь содержит оба сервера: ```yaml runtime: mcp_servers: executor-mcp: type: http # ⚠️ НЕ streamable-http — валидатор: stdio|http|sse url: "http://balda-executor-mcp:8890/mcp" obsidian: type: sse # ⚠️ НЕ http — у obsidian-mcp SSE-транспорт url: "http://obsidian-mcp:3101/sse" balda: mcp_servers: - executor-mcp - obsidian ``` 🔴 **Тип транспорта — источник двух подряд ошибок.** Obsidian отдаёт **SSE**, поэтому `type: sse`. Если поставить `type: http`, Балда упадёт с `failed to init MCP session: … session not found`. Полная таблица типов — [[personal/tech/balda-executor-mcp]] §10e. Бэкапы: `config.yaml.bak-obsidian-20260926-214849` (на Kraken, рядом с `config.yaml`). ⚠️ Туннель к Obsidian MCP **не нужен** — Балда ходит по docker-сети по имени `obsidian-mcp`. Наружу порт не публикуется. --- ## Эволюция Obsidian MCP у нас | Период | Что было | Проблемы | |--------|----------|----------| | До 2026-05-09 | `npx obsidian-mcp` (пакет Steven, прямой) | ZodError, race condition, UTF-8 chunk split, зависания | | 2026-05-09 → ? | `npx obsidian-mcp` через wrapper | Wrapper решил проблемы | | Сейчас | `mcpvault` (Hermes native MCP client) | Стабильно. patch_note exact match — единственная боль | ## Рекомендация по замене (2026-06-24) При проблемах с mcpvault (зависания, память, exact match) — **cyanheads/obsidian-mcp-server**: - `obsidian_replace_in_note` с regex + flexible whitespace (решает exact match) - 9.7K dl/week, dual transport (stdio + HTTP), active - Требует Obsidian Local REST API plugin (Obsidian должен быть открыт) - Есть Docker support (`ghcr.io/cyanheads/obsidian-mcp-server`) Подробнее: `personal/tech/obsidian-mcp-ecosystem.md` ## См. также - `personal/tech/obsidian-mcp-ecosystem.md` — обзор всех 6 реализаций - ⚠️ `personal/projects/personal-os/obsidian-mcp-wrapper.md` — **УСТАРЕЛ**, не отражает реальность