UNPKG

obsidian-mcp-server

Version:

MCP server for Obsidian vaults — read, write, search, and surgically edit notes, tags, and frontmatter via the Local REST API plugin. STDIO or Streamable HTTP.

27 lines (18 loc) 3.33 kB
--- summary: obsidian_write_note refuses to clobber existing notes by default — opt in with overwrite:true; obsidian_list_commands moves behind OBSIDIAN_ENABLE_COMMANDS alongside obsidian_execute_command. breaking: false --- # 3.1.02026-04-29 A safety pass on whole-file writes plus a tighter command-palette gate. Service-layer error classification picks up the framework's `httpErrorFromResponse` for the 4xx/5xx fallback so the canonical status → code mapping (and `Retry-After` capture) replaces the hand-rolled ladder. ## Added - **`obsidian_write_note` `overwrite` flag (default `false`).** Whole-file writes against an existing note now fail with `file_exists` (`Conflict`) and an actionable message pointing at `obsidian_patch_note` / `obsidian_append_to_note` / `obsidian_replace_in_note` for in-place edits, or `overwrite: true` for a deliberate full replacement. Section-targeted writes are unaffected — the flag is ignored when `section` is set. - **`obsidian_write_note` `created` field in the output.** `true` when the call brought a new file into existence; `false` when it replaced an existing one or targeted a section. Surfaces in both `structuredContent` and the rendered `content[]` twin. - **Typed error contract on `obsidian_write_note`.** Declares `file_exists` so clients can switch on `error.data.reason` instead of parsing message text. - **`ObsidianService.noteExists()`** — HEAD probe that returns `true` on 2xx, `false` on 404, and surfaces other statuses through the normal error classifier so a 401 doesn't masquerade as a missing file. Bypasses `withRetry` — a HEAD probe shouldn't retry on 404. ## Changed - **`obsidian_list_commands` is now opt-in alongside `obsidian_execute_command`.** Both register only when `OBSIDIAN_ENABLE_COMMANDS=true`; both are hidden otherwise. Previously `obsidian_list_commands` was always-on and only execution was gated, which advertised a discovery surface for capabilities the operator had explicitly disabled. The pair now travels together as `commandToolDefinitions`. - **Service-layer 4xx/5xx fallback routes through `httpErrorFromResponse`.** The default branch in `ObsidianService.#throwForStatus` delegates to the framework helper for unhandled statuses so the canonical mapping (`500/501 → InternalError`, `502/503 → ServiceUnavailable`, `504 → Timeout`) and `Retry-After` capture replace the local `serviceUnavailable` ladder. The body is consumed before the helper runs, so the call passes `captureBody: false` and forwards a truncated copy via `data.body`. Wire-visible: 500s now land as `InternalError` (`-32603`) instead of `ServiceUnavailable` (`-32004`). - **Dependency bump:** `@cyanheads/mcp-ts-core` 0.8.10.8.2. - **`skills/maintenance/SKILL.md` v1.9 → v2.0.** Step 6's framework-adoption rule moved from "default adopt" to "auto-adopt every applicable site, in this pass." Documents an explicit asymmetry against third-party adoptions, a hard rule against scope/effort/marginal-benefit deferrals, and a valid-vs-invalid deferral table. Step 8's "Open decisions" section is reframed accordingly — empty is now the expected outcome of a clean framework upgrade. ## Fixed - **`obsidian_list_commands` description** no longer claims it is always available — calls out the `OBSIDIAN_ENABLE_COMMANDS` gate.