Skip to main content
Glama

obsidian-mcp

MCP server exposing an Obsidian vault to LLM clients (Claude, Cursor, Continue, ...).

Python 3.10+ MCP License: MIT Tests: 448

Why?

A Second Brain should be queryable by AI agents — without shipping your data to a third-party API. This server gives any MCP-compatible client read and write access to a local Obsidian vault via the Model Context Protocol.

Key differentiators (vs. existing Obsidian MCP servers):

  • Headless & filesystem-native — no Obsidian desktop app required, container-ready

  • Structured output schemas on every tool — LLMs know exactly what to expect

  • Vault graph analytics — wikilink graph, orphan detection, outlink tracing

  • Vault health check — automated quality scoring (broken links, untagged, TODOs, duplicates)

  • Policy enforcement — write log, raw/ immutability, agent locking, and wiki-index upkeep enforced at the filesystem choke point, not left to client discipline

  • Ranked full-text search — Whoosh-backed index with Lucene-style query syntax (fields, booleans, phrases, wildcards, fuzzy, ranges, did-you-mean)

  • AI-optimized tool descriptions — 5-element docstring pattern with actionable error messages

Related MCP server: Obsidian Vault API

Features

  • Read and Search notes (full-text, case-insensitive)

  • Create, Update, Delete notes with proper YAML frontmatter

  • Append to notes (upsert), patch sections under headings

  • Frontmatter and tag management (get/set/delete, dedup, case-insensitive)

  • Daily notes (YYYY-MM-DD format with date validation)

  • Search & replace (literal + regex with capture groups)

  • Backlink graph traversal (wikilinks)

  • Vault structure as a JSON resource

  • Path traversal protection on all entry points (vault sandbox)

  • Tag extraction (frontmatter + inline tags)

  • Structured output schemas for every MCP tool (22/22)

  • Vault write policies: write log (per-actor shards in .vault-write-log.d/, legacy .vault-write-log.jsonl as fallback/opt-out), raw/ immutability, agent lock (.vault-lock), wiki/index.json upkeep, link checking. The server owns this bookkeeping: clients never append log lines themselves; every write tool takes an optional related_to (work-item uid) that lands in the log line

  • archive_note: archive-instead-of-delete with backlink reporting

  • Incremental Whoosh full-text index (BM25, stemmed, field queries, fuzzy, did-you-mean) with configurable index directory

  • Vault graph analytics (nodes, edges, stats, broken links)

  • Vault health scoring (0–100 with actionable issue report)

Documentation

Document

Description

Build Guide

Architecture walkthrough, code tour, extension guide, Docker/K8s deployment

Semantic Search

Optional hybrid search (Whoosh + Qdrant vectors), setup and design

AI Optimization Guide

Best practices for AI-optimized MCP tool descriptions

Changelog

Notable changes with added, fixed, and changed items

Quick Start

pip install -e .

# Point at your real vault
export OBSIDIAN_VAULT_ROOT=/path/to/your/obsidian/vault

# Run (stdio transport)
obsidian-mcp

Or use the bundled mock vault (no config needed):

obsidian-mcp   # uses examples/mock-vault/

Client Integration

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidian-mcp",
      "env": {
        "OBSIDIAN_VAULT_ROOT": "/path/to/your/vault"
      }
    }
  }
}

Cursor

Cursor supports MCP from version 0.42+. Configure in ~/.cursor/mcp.json (same format as Claude Desktop).

Any other MCP client

The server runs on stdio. Register it with your client's standard MCP configuration (command: obsidian-mcp or python3 -m obsidian_mcp.server, env: OBSIDIAN_VAULT_ROOT).

Tools (22)

Tool

Description

read_note(path)

Read a note (title, tags, metadata, content)

search_notes(query, include_restricted?)

Full-text search across title and body

list_notes(folder?, include_restricted?)

List notes, optionally scoped to a folder

list_note_paths(folder?, include_restricted?)

List note paths only (lightweight)

create_note(path, content, tags?)

Create a note with frontmatter

update_note(path, content)

Update body (frontmatter preserved)

delete_note(path)

Permanently delete a note

append_to_note(path, content)

Append content (create if missing)

manage_frontmatter(path, action, key?, value?, values?, keys?)

Get/set/delete one frontmatter key, or several in one write (values / keys)

manage_tags(path, action, tags?)

Add/remove/list tags

patch_note(path, heading, action, content)

Insert/prepend/replace under a heading

daily_note(action, content?, date?)

Read/append daily notes (YYYY-MM-DD.md)

search_and_replace(path, find, replace, use_regex?, case_sensitive?)

Find/replace in note body

get_backlinks(title, include_restricted?)

Find notes linking via wikilinks

vault_graph(include_restricted?)

Full vault link graph (nodes, edges, stats)

find_orphans(include_restricted?)

Notes with no incoming or outgoing wikilinks

get_outlinks(path)

Outgoing wikilinks from a note

vault_health(include_restricted?)

Health score + issue report (0–100)

archive_note(path, archive_dir?)

Move note to archive folder; report backlinks

search_query(query, limit?, include_restricted?)

Ranked Lucene-style full-text query (fields, fuzzy, ranges)

rebuild_index()

Rebuild the full-text index from all notes

index_status()

Index health: docs indexed vs. vault notes

Restricted folders (optional): OBSIDIAN_SEARCH_RESTRICTED="05 People,Private" hides these folders from every search, listing and graph tool (and from hybrid_search) unless the call sets include_restricted=true. Results then carry restricted_hidden: true. Reading a note by its path, or listing a restricted folder by name, stays possible: naming it is an explicit request. Links into a hidden folder still resolve (no false broken links), but the graph shows only the link text, not the hidden path. Unset (default): nothing is hidden.

Index freshness: on start the server reconciles the full-text index with the files on disk in the background (notes moved or edited by file sync or the Obsidian editor while it was not running). Processes sharing one index do this once: a process skips if the index was reconciled within OBSIDIAN_INDEX_RECONCILE_MAX_AGE seconds (default 900). OBSIDIAN_INDEX_RECONCILE=0 turns it off.

Optional, with pip install 'obsidian-mcp[semantic]' and OBSIDIAN_SEMANTIC=1 (see Semantic Search):

Tool

Description

hybrid_search(query, limit?, folder?, include_restricted?)

Find notes by meaning and words, fused ranking

semantic_sync(max_seconds?, status_only?)

Catch up the vector index; report its status

Plugins

Optional extensions are installed as Python packages with an entry point in the group obsidian_mcp.plugins and enabled by name, e.g. OBSIDIAN_PLUGINS=oversight (comma-separated). Nothing is loaded without the variable. A plugin's register(api) can add tools (api.add_tool(fn), core tool names are reserved) and subscribe to events (api.on(event, handler)):

Event

Keyword arguments

startup

actor

after_write

path, action, layer, actor

policy_refusal

tool, policy, message, actor

tool_error

tool, error_type, message, actor

Plugins are fail-open: an error while loading or in a handler goes to stderr and never fails a write or a tool call. They run in the server process with the server's rights, so only enable plugins you trust. Example: the sensor of agent-oversight reports the server's heartbeat and every refusal or tool error as oversight signals.

Prompts (3)

Prompt

Arguments

Description

session-start

project?

Review daily notes and project context before starting work

session-end

project?

Document accomplishments, decisions, and open questions

project-checkin

project (required)

Review and update a specific project's documentation

Resources (1)

URI

Description

vault://structure

Folder/file tree of the vault as JSON

Architecture

MCP Client  ←—MCP stdio—→  obsidian-mcp server  →  vault.py  →  Obsidian Vault
(Claude/Cursor/...)         (22 tools + 3 prompts)   (filesystem)   (*.md + YAML)
                            + 1 resource                 │
                                        policies.py ←——————┘ (write log, raw lock,
                                                        wiki index, link check)
                                        search_index.py → Whoosh index (BM25)

Design decisions:

  • Filesystem-native instead of REST API: headless, containerizable, no app dependency

  • Path traversal protection at all entry points (_resolve(), _resolve_folder())

  • Singleton pattern for Vault instance (testable via set_vault())

  • Pydantic BaseModel return types for outputSchema on all 22 tools

Development

pip install -e ".[dev]"
pytest tests/       # 448 tests, all passing

CI runs on Python 3.10, 3.11, and 3.12 with pytest + ruff.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI clients to read, write, search, and manage files in an Obsidian vault via MCP over SSE, running standalone without the Obsidian desktop app.
    1
    Creative Commons Attribution Non Commercial Share Alike 4.0 International
  • F
    license
    Not graded
    quality
    B
    maintenance
    Serves as a unified MCP gateway to an Obsidian vault, enabling AI agents to perform hybrid lexical/vectorial semantic search, explore note graphs, and write through consent-based intent batches with conflict detection.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP clients to read, write, search, and manage notes, canvases, kanban boards, and other Obsidian vault content over a network, without requiring the Obsidian desktop app.
    33
    MIT