217 lines
11 KiB
Markdown
Executable File
217 lines
11 KiB
Markdown
Executable File
> [!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 -- <cmd> <args>`; слушает `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` — **УСТАРЕЛ**, не отражает реальность
|