83 lines
3.5 KiB
Markdown
83 lines
3.5 KiB
Markdown
# 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 чище и не ломается при обновлении пакета).
|