--- namespace: work tags: [system, obsidian, mcp, eagle] last_updated: 2026-05-09 confidence: 1.0 --- # obsidian-mcp-wrapper > **Файл**: `~/scripts/obsidian-mcp-wrapper.js` > **Назначение**: прокси-обёртка над `obsidian-mcp`, решает четыре системных бага --- ## Проблемы, которые решает ### 1. ZodError при инициализации (obsidian-mcp v1.0.6) `obsidian-mcp` падал с ZodError сразу после запуска. Причина: Hermes отправляет `notifications/initialized` с полем `"id": null`, а obsidian-mcp v1.0.6 использует `.strict()` валидацию и не принимает лишние поля. **Fix**: wrapper перехватывает все notification-сообщения (без `result`/`error`) с `id === null` и удаляет поле `id` перед передачей в child. ### 2. Race condition при gateway restart При рестарте Hermes gateway поднимает новый процесс `obsidian-mcp-wrapper`. Первые параллельные tool-вызовы приходят пока child ещё инициализируется (~500ms) → они тайм-аутились, circuit breaker открывался (3 фейла → 60s cooldown). **Fix**: wrapper буферизует все tool-вызовы до завершения handshake (`initialize` → ответ → `notifications/initialized`), потом флашит очередь. ### 3. Corrupted large payloads (UTF-8 chunk split) При больших tool-вызовах (~200KB+) Node.js доставляет stdin в нескольких chunk-ах. Старый код делал string split — JSON разрезался по байтам → UTF-8 multibyte символы портились, `JSON.parse` падал, сообщение дропалось молча. **Симптом**: `edit_note` с большим контентом тихо зависал (30s timeout), в логах: ``` [obsidian-wrapper] Non-JSON from Hermes (forwarding verbatim): {"jsonrpc": "2.0", "method": "tools/call", "id": 3, "params": {"name": "edit-not ``` **Fix**: stdin и stdout читаются через `Buffer.concat` + `Buffer.slice` на `0x0a`. Строка собирается полностью до передачи в `JSON.parse`. ### 4. Per-call watchdog (зависший child) Если child не ответил на `tools/call` / `tools/list` / `resources/*` за **5s** — watchdog убивает процесс. После авторестарта call автоматически уходит в голову очереди и ретраится. **Fix**: `armWatchdog(callLine)` → `setTimeout 5000ms` → `child.kill()` → `startChild()`. --- ## Как работает ``` Hermes (stdin) → wrapper → obsidian-mcp (child) ↑ auto-restart при краше (до 10 раз) ``` **Состояния**: - `ready = false` — child стартует, все tool-вызовы в очередь - `ready = true` — handshake завершён, очередь флашится, всё проходит напрямую **Restart логика**: 1. Child упал → `ready = false`, `restarts++` 2. Новый child спавнится 3. Wrapper реплеит сохранённый `initialize` → ждёт ответа с `serverInfo` 4. Отправляет `notifications/initialized` (не форвардит Hermes — он не просил) 5. `ready = true` → флаш очереди **Shutdown**: На stdin EOF (`Hermes` закрыл процесс) — child убивается без авторестарта, wrapper выходит чисто. **Логи** (все в stderr с timestamp): ``` [obsidian-wrapper] 2026-05-09T12:00:00.000Z Spawning obsidian-mcp (vault=/Users/admin/obsidian) [obsidian-wrapper] 2026-05-09T12:00:00.500Z Hermes → child (handshake complete): notifications/initialized [obsidian-wrapper] 2026-05-09T12:00:00.501Z Child ready — flushing queue (3 items) [obsidian-wrapper] 2026-05-09T12:00:01.200Z Stripped id:null from notification: notifications/initialized [obsidian-wrapper] 2026-05-09T12:00:06.000Z WATCHDOG: child did not respond in 5000ms for tools/call #7 — killing and restarting ``` --- ## Конфиг Hermes `~/.hermes/config.yaml`: ```yaml obsidian: command: node args: [/Users/admin/scripts/obsidian-mcp-wrapper.js] ``` Wrapper сам вызывает `/opt/homebrew/bin/obsidian-mcp /Users/admin/obsidian`. --- ## Производительность - Инициализация: ~587ms (без ZodError) - Overhead wrapper: negligible (pure Node.js child_process, нет npm-зависимостей) - MAX_RETRY: 10 - CALL_TIMEOUT_MS: 5000ms (watchdog) --- ## История **2026-05-09 (1)** — создан после диагностики 58 ошибок `obsidian/... call failed` в логах Hermes. Корневая причина — ZodError + race condition при старте. Wrapper написан вместо патча исходников obsidian-mcp (патч не нужен, wrapper чище и не ломается при обновлении пакета). **2026-05-09 (2)** — фикс large payload: Buffer-based line splitting вместо string split (`edit_note` с большим контентом молча дропался). Добавлен per-call watchdog (5s timeout → kill & retry). MAX_RETRY повышен с 5 до 10. ## Related [[personal-os-architecture]] [[personal-os-agent-rules]]