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.

24 lines (17 loc) 3.94 kB
--- summary: Folder-scoped read/write permissions and a global read-only kill switchthree opt-in env vars (OBSIDIAN_READ_PATHS, OBSIDIAN_WRITE_PATHS, OBSIDIAN_READ_ONLY) gate every path-taking tool and resource, with a new path_forbidden error reason. breaking: false --- # 3.1.22026-05-03 Three opt-in env vars now scope which vault paths read and write tools may target. Closes [#40](https://github.com/cyanheads/obsidian-mcp-server/issues/40); resolves the original ask in [discussion #15](https://github.com/cyanheads/obsidian-mcp-server/discussions/15#discussioncomment-13918526). Backwards compatible — all three default unset / false, preserving current full-vault behavior. ## Added - **`OBSIDIAN_READ_PATHS` / `OBSIDIAN_WRITE_PATHS`** — comma-separated vault-relative folder allowlists. Prefix-based with implicit recursion (`projects/` matches `projects/a.md`, `projects/sub/b.md`, …); case-insensitive; trailing slashes normalized; deduplicated. Empty / whitespace-only input falls back to unset; separator-only input (`,`, `,,,`) and absolute paths or `..` traversal throw a `ConfigurationError` at startup naming the offending env var. - **`OBSIDIAN_READ_ONLY`** — global kill switch. When `true`, denies every write regardless of `OBSIDIAN_WRITE_PATHS`, and suppresses the `OBSIDIAN_ENABLE_COMMANDS` pair (commands can mutate). Useful for shared or public-facing deployments. - **`path_forbidden` error reason** declared on every path-taking tool's `errors[]` contract — mapped to `JsonRpcErrorCode.Forbidden`. The wire payload carries `{ path, op, subreason, activeScope, recovery }` where `subreason` is one of `outside_read_paths`, `outside_write_paths`, or `read_only_mode`, and the recovery hint echoes the active scope so the LLM can self-correct without inspecting server logs. - **Startup banner** — `core.logger.info('Path policy', …)` echoes the active scope and command-palette enable state at boot, so operators can verify their config. Emits a warning when `OBSIDIAN_READ_ONLY=true` AND `OBSIDIAN_WRITE_PATHS` is non-empty (the boolean wins; the paths are ignored). - **`PathPolicy`** module (`src/services/obsidian/path-policy.ts`) — single chokepoint that decides per path: `isReadable`, `isWritable`, `assertReadable`, `assertWritable`, plus a silent `filterReadable` for search post-filtering. ## Changed - **`ObsidianService` gates every path-taking method** before the upstream HTTP call: `getNoteContent`, `getNoteJson`, `getDocumentMap`, `listFiles`, `openInUi` (read), and `writeNote`, `appendToNote`, `patchNote`, `deleteNote` (write). For non-`path` targets (`active`, `periodic`) under restrictions, the service routes through a JSON resolution fetch first to learn the path, then gates on the resolved path — costs one extra fetch only for users who configured a path scope. - **`obsidian_search_notes` post-filters hits against `OBSIDIAN_READ_PATHS`** silently — surfacing a "we hid N hits" indicator would defeat the gate. The `excluded` overflow indicator continues to count hits trimmed by the 100-hit cap, not policy-dropped hits. - **Tool registration in `src/index.ts`** splits read-only tools from write tools at the barrel and wraps the write set with `disabledTool()` when `OBSIDIAN_READ_ONLY=true`. The command-palette pair stays wrapped with `disabledTool()` whenever it's not callable (either `OBSIDIAN_ENABLE_COMMANDS=false` or `OBSIDIAN_READ_ONLY=true`), with a `hint` explaining how to enable it. The LLM never sees disabled tools in `tools/list`; they remain visible in the operator-facing manifest. - **`obsidian_list_notes` listing semantics** — the vault root always lists, even with `OBSIDIAN_READ_PATHS` set. Specific subdirectories outside the read scope throw `path_forbidden`. Children aren't filtered at the service level; the per-file read gate on subsequent reads (`obsidian_get_note`, etc.) still catches access to out-of-scope notes.