obsidian-mcp
README.md
# 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 <transaction-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/<tool>/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
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessResponsive