From 4ec2a77b3cc153d974eaa4be65af3fd46c220f87 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sat, 9 May 2026 19:36:30 +0600 Subject: [PATCH] =?UTF-8?q?[2026-05-09]=20obsidian-mcp-wrapper:=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20wrapper-=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D0=B0=20(?= =?UTF-8?q?ZodError=20fix=20+=20race=20condition)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wiki/obsidian-mcp-wrapper.md | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 wiki/obsidian-mcp-wrapper.md diff --git a/wiki/obsidian-mcp-wrapper.md b/wiki/obsidian-mcp-wrapper.md new file mode 100644 index 00000000..c99ff997 --- /dev/null +++ b/wiki/obsidian-mcp-wrapper.md @@ -0,0 +1,82 @@ +# 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`), потом флашит очередь. + +--- + +## Как работает + +``` +Hermes (stdin) → wrapper → obsidian-mcp (child) + ↑ auto-restart при краше (до 5 раз) +``` + +**Состояния**: +- `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` → флаш очереди + +**Логи** (все в 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 +``` + +--- + +## Конфиг 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-зависимостей) + +--- + +## История + +**2026-05-09** — создан после диагностики 58 ошибок `obsidian/... call failed` в логах Hermes. +Корневая причина — ZodError + race condition при старте. Wrapper написан вместо патча +исходников obsidian-mcp (патч не нужен, wrapper чище и не ломается при обновлении пакета).