# Obsidian MCP A local [Model Context Protocol](https://modelcontextprotocol.io/) server that lets MCP-compatible assistants safely read and modify explicitly configured Obsidian vaults. Version 2 supports both MCP `2026-07-28` and 2025-era clients by default, and requires Node.js 22 or newer. It works directly with Markdown files, so Obsidian does not need to be open. Legacy protocol and v1 positional-path compatibility are deprecated and print exact migration instructions to stderr. > [!IMPORTANT] > MCP clients can invoke destructive tools. Back up important vaults, review client permission prompts, and use revision preconditions for concurrently edited notes. ## Quick start Node.js 22 or newer is required: ```bash node --version # v22 or newer ``` Run with `npx` without installing the package globally. Pinning the major version receives compatible 2.x updates without automatically crossing a future major version: ```bash npx -y obsidian-mcp@2 serve --vault notes=/absolute/path/to/vault ``` Configure an MCP client to launch the same command over stdio: ```json { "mcpServers": { "obsidian": { "command": "npx", "args": ["-y", "obsidian-mcp@2", "serve", "--vault", "notes=/absolute/path/to/vault"] } } } ``` Alternatively, install the package globally and use `"command": "obsidian-mcp"` with the same arguments beginning at `"serve"`: ```bash npm install -g obsidian-mcp@2 ``` Each vault must already contain an `.obsidian` directory and must be configured using an absolute path. Both protocol eras are served from the same tool definitions. After confirming that your MCP client negotiates `2026-07-28`, you may opt into modern-only mode by adding `"--legacy", "reject"` to `args`. Vault ids use lowercase letters, digits, `_`, and `-`, must begin with a letter, and are the values assistants pass to tools. Up to ten vaults may be configured. Repeat `--vault` to expose more than one vault: ```bash obsidian-mcp serve \ --vault work=/Users/me/Documents/WorkVault \ --vault personal=/Users/me/Documents/PersonalVault ``` Network, removable, hidden, and synced locations are allowed because an explicit `--vault` is treated as authorization; the same containment protections apply to every location. ## Design principles - Vault access is explicitly allowlisted at process startup. - Every tool path is vault-relative, segment-checked, and blocked from symlinks and reserved state. - File mutations are journaled, conflict-checked, atomically replaced, and rolled back as one transaction. - The server never listens on a network interface or sends telemetry. - stdout is reserved exclusively for MCP messages; structured diagnostics go to stderr. - Results are bounded, paginated where appropriate, and available as both text and structured content. ## Tools | Tool | Purpose | | --------------------------- | ----------------------------------------------------------------------------- | | `obsidian_list_vaults` | List configured vault ids without exposing host paths. | | `obsidian_read_note` | Read a bounded page of a note and return its SHA-256 `etag`. | | `obsidian_create_note` | Atomically create a note without overwriting. | | `obsidian_edit_note` | Append, prepend, or replace exact note content. | | `obsidian_delete_note` | Move a note to MCP trash or permanently delete it with explicit confirmation. | | `obsidian_move_note` | Move or rename a note and update unambiguous backlinks transactionally. | | `obsidian_create_directory` | Transactionally create a directory inside a vault. | | `obsidian_search_vault` | Search content, filenames, or tags with bounded cursor pagination. | | `obsidian_add_tags` | Add tags to one or more notes atomically. | | `obsidian_remove_tags` | Remove exact, nested, or wildcard-selected tags atomically. | | `obsidian_rename_tag` | Rename a tag across the vault atomically. | | `obsidian_manage_tags` | Unified add/remove tag workflow using the same implementation. | All schemas are strict JSON Schema 2020-12 contracts generated from Zod. Mutating results include a transaction id; tool failures return `isError: true` with an actionable error code. ### Reading and concurrency `obsidian_read_note` returns an `etag`. Pass it as `if_match` to edit, move, or delete when avoiding lost updates matters. Batch tag operations accept an `expected_etags` map. A changed note returns `REVISION_CONFLICT` rather than being overwritten. Large notes are paginated using an opaque cursor bound to the path and `etag`. Search uses an opaque cursor bound to the query and options. Tool text responses are capped at 25,000 characters. ### Deletion and recovery Trash is the default. Deleted note bytes and metadata are stored separately under `.obsidian-mcp/trash`; metadata is never injected into the note. Permanent deletion requires `confirm_path` to exactly match the canonical relative path. Transactions and recovery snapshots live in `.obsidian-mcp/transactions`. Completed data is retained for 30 days and pruned oldest-first above 1 GiB by default: ```bash obsidian-mcp serve --vault work=/path \ --recovery-days 14 \ --recovery-max-bytes 536870912 ``` Inspect or restore a completed transaction while the MCP server is stopped: ```bash obsidian-mcp recovery list --vault work=/path obsidian-mcp recovery restore --vault work=/path --id ``` Recovery refuses to overwrite content changed since the selected transaction. Permanent deletion snapshots are purged after commit and cannot be restored. ## Path and filesystem safety The server: - canonicalizes configured vault roots and rejects duplicate or nested roots; - rejects absolute, UNC, Windows-drive, NUL, backslash, empty, and dot-segment tool paths; - reserves `.obsidian`, `.obsidian-mcp`, `.git`, `.backup`, and `.trash` from tool access; - checks existing targets and the nearest existing ancestor for new targets; - rejects symlinks, junctions, and reparse-point paths, and skips them during scans; - performs no shell execution for filesystem validation; - strictly decodes UTF-8 and does not silently replace invalid bytes. The process needs read and write access to each configured vault. `obsidian-mcp doctor --vault id=/path` validates startup readiness and recovery state. ## Link and tag behavior Moves recognize Obsidian Wikilinks, embeds, Markdown links, aliases, URL-encoded destinations, headings, and block anchors. A link is rewritten only when it resolves unambiguously to the source note; ambiguous links are reported and left unchanged. Deletion preserves backlinks unless `backlink_action: "mark_broken"` is requested. Tags follow Obsidian's case-insensitive rules and support Unicode, emoji, `_`, `-`, `/`, and nested tags. Frontmatter tags are written as YAML lists. Inline tag processing ignores fenced/inline code and HTML comments. Wildcards use a bounded matcher rather than regular expressions. ## Development ```bash npm ci npm run typecheck npm test npm run build npm run ci ``` Each tool owns a typed definition under `src/tools//index.ts`; the small registry in `src/tools/index.ts` applies shared MCP registration and response behavior. Filesystem, transaction, Markdown, link, and search behavior live in reusable utilities. A new tool must use `VaultFs` for every path, `TransactionManager` for mutations, strict input/output schemas, structured results, annotations, and security/integration tests. See [MIGRATING.md](MIGRATING.md) for the 1.x migration and [SECURITY.md](SECURITY.md) for vulnerability reporting. Startup errors and compatibility warnings are written only to stderr with a stable code, the detected problem, an exact fix, a verification step, and a link to the matching migration section. If the server does not appear, find the code in the MCP client logs and use the [diagnostic reference](MIGRATING.md#diagnostic-reference). ## License MIT