From d7e9b722475bdffb476650f08532fc92fc5ca9e4 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sun, 10 May 2026 20:39:35 +0600 Subject: [PATCH] [2026-05-10] obsidian-mcp-wrapper: large payload fix + watchdog + MAX_RETRY=10 --- wiki/obsidian-mcp-wrapper.md | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/wiki/obsidian-mcp-wrapper.md b/wiki/obsidian-mcp-wrapper.md index c99ff997..05f6f805 100644 --- a/wiki/obsidian-mcp-wrapper.md +++ b/wiki/obsidian-mcp-wrapper.md @@ -1,7 +1,7 @@ # obsidian-mcp-wrapper > **Файл**: `~/scripts/obsidian-mcp-wrapper.js` -> **Назначение**: прокси-обёртка над `obsidian-mcp`, решает два системных бага +> **Назначение**: прокси-обёртка над `obsidian-mcp`, решает четыре системных бага --- @@ -25,13 +25,35 @@ **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 при краше (до 5 раз) + ↑ auto-restart при краше (до 10 раз) ``` **Состояния**: @@ -45,12 +67,16 @@ Hermes (stdin) → wrapper → obsidian-mcp (child) 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 ``` --- @@ -72,11 +98,17 @@ Wrapper сам вызывает `/opt/homebrew/bin/obsidian-mcp /Users/admin/obs - Инициализация: ~587ms (без ZodError) - Overhead wrapper: negligible (pure Node.js child_process, нет npm-зависимостей) +- MAX_RETRY: 10 +- CALL_TIMEOUT_MS: 5000ms (watchdog) --- ## История -**2026-05-09** — создан после диагностики 58 ошибок `obsidian/... call failed` в логах Hermes. +**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.