Obsidian Vault

Recommended

by cyanheads

225 20.8k downloads 5.0 (1) Apache-2.0

About

Enables interaction with Obsidian vaults for file operations, content search, and metadata management, facilitating note-taking and knowledge base organization.

Details

Repository
cyanheads/obsidian-mcp-server
License
Apache-2.0

Explore

Built on @cyanheads/mcp-ts-core:

- Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
- Unified error handling — handlers throw, framework catches, classifies, and formats. Tools advertise their failure surface via typed errors[] contracts.
- Server-level instructions on initialize — surfaces deployment-specific orientation (active path policy, read-only mode, command-palette toggle) to spec-compliant clients alongside the static tool/resource catalog
- Pluggable auth on the HTTP transport: none, jwt, oauth
- Structured logging with optional OpenTelemetry tracing
- STDIO and Streamable HTTP transports

The server itself is stateless — every tool call hits the Local REST API directly. The framework's storage backends, request-state KV, and progress streams aren't used here; Obsidian is single-vault and there's nothing to persist between calls.

Obsidian-specific:

- Wraps the Obsidian Local REST API plugin — typed client, deterministic error mapping
- Section-aware editing across headings, block references, and frontmatter fields via PATCH-with-target operations
- Tag reconciliation across both representations: frontmatter tags: array and inline #tag syntax (skipping fenced code blocks)
- Search across up to three modes: text, JSONLogic, and (when the plugin is reachable) BM25-ranked Omnisearch — cursor-paginated per the MCP 2025-11-25 spec, with per-file match clipping in text mode
- Optional human-in-the-loop confirmation for destructive deletes via ctx.elicit
- Folder-scoped read/write permissions via OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS and a global OBSIDIAN_READ_ONLY kill switch — denies are typed path_forbidden with the active scope echoed back in the error data
- Opt-in command-palette pair (obsidian_list_commands + obsidian_execute_command) — registered only when OBSIDIAN_ENABLE_COMMANDS=true
- Forgiving path resolution on obsidian_get_note and obsidian_open_in_ui — silently retries case-mismatched paths against the canonical filename, throws Conflict on ambiguous case matches, and enriches NotFound with Did you mean: …? suggestions when only near-matches exist. obsidian_delete_note is deliberately excluded — a destructive op shouldn't silently rewrite the target path.

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name Obsidian Vault
    Command (node, npx, python, etc.) npx
    Arguments
    • Argument 1 -y
    • Argument 2 obsidian-mcp-server@latest
    Environment
    • MCP_LOG_LEVEL info
    • OBSIDIAN_API_KEY your-local-rest-api-key
    • MCP_TRANSPORT_TYPE stdio

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

Add the following to your MCP client configuration file. The Obsidian Local REST API plugin must be installed and enabled in your vault — see Prerequisites.

{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["obsidian-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "obsidian-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key"
      }
    }
  }
}

For Streamable HTTP, set the transport and start the server. Inline env vars work for one-off runs; for repeated use, copy values into .env (see .env.example) and run bun run start:http.

```sh
MCP_TRANSPORT_TYPE=http OBSIDIAN_API_KEY=... bun run start:http

obsidian_get_note

Read a note as raw content, full structured form (content + frontmatter + tags + stat, with optional outgoing links), structural document map, or a single section.

obsidian_list_notes

List notes and subdirectories under a vault path. Recursive walk (default depth 2, max depth 20; 1000-entry cap) with optional `extension` and `nameRegex` filters.

obsidian_list_tags

List every tag found across the vault with usage counts, including hierarchical parents. Optional `nameRegex` post-filters the result set.

obsidian_list_commands

List Obsidian command-palette commands, optionally filtered by `nameRegex` on display name. Opt-in via `OBSIDIAN_ENABLE_COMMANDS=true`.

obsidian_search_notes

Search the vault by text, JSONLogic, or BM25-ranked Omnisearch (when the plugin is reachable). Results paginate via opaque cursors.

obsidian_write_note

Create a note, replace a single section in place, or — with `overwrite: true` — clobber an existing file. Refuses whole-file writes against an existing path by default.

obsidian_append_to_note

Append content to a note. Without `section`, creates the file if missing. With `section`, appends to a specific heading, block, or frontmatter field (file must exist).

obsidian_patch_note

Surgical `append` / `prepend` / `replace` against a heading, block reference, or frontmatter field.

obsidian_replace_in_note

Body-wide search-replace inside a single note. Literal or regex matching with whole-word, whitespace-flexible, and case-sensitivity options; supports capture-group replacement.

obsidian_manage_frontmatter

Atomic `get` / `set` / `delete` on a single frontmatter key.

obsidian_manage_tags

Add, remove, or list tags. Defaults to the frontmatter `tags:` array; `location: 'inline'` or `'both'` opts into mutating the note body.

obsidian_delete_note

Permanently delete a note. Elicits human confirmation when the client supports it.

obsidian_open_in_ui

Open a file in the Obsidian app UI, with `failIfMissing` and `newLeaf` toggles.

obsidian_execute_command

Execute an Obsidian command-palette command by ID. Opt-in via `OBSIDIAN_ENABLE_COMMANDS=true`.

Fourteen tools grouped by shape — readers fetch notes and metadata, writers create or surgically edit content, managers reconcile tags and frontmatter, and a guarded escape hatch dispatches Obsidian command-palette commands.

| Tool Name | Description |
|:----------|:------------|
| obsidian_get_note | Read a note as raw content, full structured form (content + frontmatter + tags + stat, with optional outgoing links), structural document map, or a single section. |
| obsidian_list_notes | List notes and subdirectories under a vault path. Recursive walk (default depth 2, max depth 20; 1000-entry cap) with optional extension and nameRegex filters. |
| obsidian_list_tags | List every tag found across the vault with usage counts, including hierarchical parents. Optional nameRegex post-filters the result set. |
| obsidian_list_commands | List Obsidian command-palette commands, optionally filtered by nameRegex on display name. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true (paired with obsidian_execute_command). |
| obsidian_search_notes | Search the vault by text, JSONLogic, or BM25-ranked Omnisearch (when the plugin is reachable). Results paginate via opaque cursors. |
| obsidian_write_note | Create a note, replace a single section in place, or — with overwrite: true — clobber an existing file. Refuses whole-file writes against an existing path by default. |
| obsidian_append_to_note | Append content to a note. Without section, creates the file if missing. With section, appends to a specific heading, block, or frontmatter field (file must exist). |
| obsidian_patch_note | Surgical append / prepend / replace against a heading, block reference, or frontmatter field. |
| obsidian_replace_in_note | Body-wide search-replace inside a single note. Literal or regex matching with whole-word, whitespace-flexible, and case-sensitivity options; supports capture-group replacement. |
| obsidian_manage_frontmatter | Atomic get / set / delete on a single frontmatter key. |
| obsidian_manage_tags | Add, remove, or list tags. Defaults to the frontmatter tags: array; location: 'inline' or 'both' opts into mutating the note body. |
| obsidian_delete_note | Permanently delete a note. Elicits human confirmation when the client supports it. |
| obsidian_open_in_ui | Open a file in the Obsidian app UI, with failIfMissing and newLeaf toggles. |
| obsidian_execute_command | Execute an Obsidian command-palette command by ID. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true. |

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "obsidian vault": {
            "env": {
                "MCP_LOG_LEVEL": "info",
                "OBSIDIAN_API_KEY": "your-local-rest-api-key",
                "MCP_TRANSPORT_TYPE": "stdio"
            },
            "args": [
                "-y",
                "obsidian-mcp-server@latest"
            ],
            "command": "npx"
        }
    }
}

Linux

{
    "env": {
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key",
        "MCP_TRANSPORT_TYPE": "stdio"
    },
    "args": [
        "-y",
        "obsidian-mcp-server@latest"
    ],
    "command": "npx"
}

Macos

{
    "env": {
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key",
        "MCP_TRANSPORT_TYPE": "stdio"
    },
    "args": [
        "-y",
        "obsidian-mcp-server@latest"
    ],
    "command": "npx"
}

Windows

{
    "env": {
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key",
        "MCP_TRANSPORT_TYPE": "stdio"
    },
    "args": [
        "/c",
        "npx",
        "-y",
        "obsidian-mcp-server@latest"
    ],
    "command": "cmd"
}

<div align="center">
<h1>obsidian-mcp-server</h1>
<p><b>Read, write, search, and surgically edit Obsidian vault notes, tags, and frontmatter via MCP. STDIO or Streamable HTTP.</b>
<div>14 Tools • 3 Resources</div>
</p>
</div>

<div align="center">

Version License Docker MCP SDK npm TypeScript Bun

</div>

<div align="center">

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

</div>

---

Tools

Fourteen tools grouped by shape — readers fetch notes and metadata, writers create or surgically edit content, managers reconcile tags and frontmatter, and a guarded escape hatch dispatches Obsidian command-palette commands.

| Tool Name | Description |
|:----------|:------------|
| obsidian_get_note | Read a note as raw content, full structured form (content + frontmatter + tags + stat, with optional outgoing links), structural document map, or a single section. |
| obsidian_list_notes | List notes and subdirectories under a vault path. Recursive walk (default depth 2, max depth 20; 1000-entry cap) with optional extension and nameRegex filters. |
| obsidian_list_tags | List every tag found across the vault with usage counts, including hierarchical parents. Optional nameRegex post-filters the result set. |
| obsidian_list_commands | List Obsidian command-palette commands, optionally filtered by nameRegex on display name. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true (paired with obsidian_execute_command). |
| obsidian_search_notes | Search the vault by text, JSONLogic, or BM25-ranked Omnisearch (when the plugin is reachable). Results paginate via opaque cursors. |
| obsidian_write_note | Create a note, replace a single section in place, or — with overwrite: true — clobber an existing file. Refuses whole-file writes against an existing path by default. |
| obsidian_append_to_note | Append content to a note. Without section, creates the file if missing. With section, appends to a specific heading, block, or frontmatter field (file must exist). |
| obsidian_patch_note | Surgical append / prepend / replace against a heading, block reference, or frontmatter field. |
| obsidian_replace_in_note | Body-wide search-replace inside a single note. Literal or regex matching with whole-word, whitespace-flexible, and case-sensitivity options; supports capture-group replacement. |
| obsidian_manage_frontmatter | Atomic get / set / delete on a single frontmatter key. |
| obsidian_manage_tags | Add, remove, or list tags. Defaults to the frontmatter tags: array; location: 'inline' or 'both' opts into mutating the note body. |
| obsidian_delete_note | Permanently delete a note. Elicits human confirmation when the client supports it. |
| obsidian_open_in_ui | Open a file in the Obsidian app UI, with failIfMissing and newLeaf toggles. |
| obsidian_execute_command | Execute an Obsidian command-palette command by ID. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true. |

obsidian_get_note

Read a note in one of four projections, addressed by vault path, the active file, or a periodic note (daily, weekly, monthly, quarterly, yearly).

- format: "content" — raw markdown body
- format: "full" — content, frontmatter, tags, and file metadata; pass includeLinks: true to also parse outgoing wiki and markdown link references from the body (vault-internal only — external URLs are filtered)
- format: "document-map" — catalog of headings, block references, and frontmatter fields
- format: "section" — single heading/block/frontmatter section value (requires section); heading sections include the full subtree under that heading

Pair the document-map projection with obsidian_patch_note to discover edit targets before patching.

---

obsidian_search_notes

Up to three search modes selected by mode:

- text — substring match with surrounding context windows. contextLength controls characters of context per side of each match (default 100; bump it for more context per hit). Optional pathPrefix filter (text mode only — passing pathPrefix in any other mode is rejected with path_prefix_invalid_mode).
- jsonlogic — JSONLogic tree evaluated against path, content, frontmatter.<key>, tags, and stat.{ctime,mtime,size}; custom glob and regexp operators
- omnisearch — BM25-ranked search via the community Omnisearch plugin. Supports quoted phrases, -exclusion, path: / ext: filters, typo tolerance, and PDF + OCR coverage (via Text Extractor). Only present in the mode enum when the plugin's HTTP server is reachable at startup; the upstream hard-caps results at 50 — narrow the query to surface more (the response carries truncated: true when the cap was likely hit).

Results paginate via opaque cursors per the MCP 2025-11-25 spec: omit cursor for the first page, then pass nextCursor from the prior response. Every result carries totalCount (post-path-policy, pre-pagination); nextCursor is omitted on the last page. Text-mode hits are additionally clipped per file at maxMatchesPerHit (default 10) so a single match-heavy note can't blow the response budget — clipped hits carry truncated: true and totalMatches.

---

obsidian_write_note

Create or surgically replace, with a protective default against accidental whole-file overwrites.

- Without section — full-file PUT. Refuses to clobber an existing file unless overwrite: true is set. The file_exists (Conflict) error suggests obsidian_patch_note / obsidian_append_to_note / obsidian_replace_in_note for in-place edits.
- With section — PATCH-with-replace against the named heading/block/frontmatter field, leaving the rest of the file untouched. The overwrite flag is ignored in section mode.

…

5.0 · 1 review

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.

Videos about Obsidian Vault

Relevant YouTube tutorials, setups, and demos