obsidian-mcp
It is an MCP server for Obsidian vault automation: search/read/write notes, manage daily logs and frontmatter, traverse and repair links, and run local semantic RAG over notes.
Search and browse notes: list notes in a vault/subfolder, simple text search, and semantic RAG queries.
Read and create content: read Markdown notes, create new notes, append text, and get/create today’s daily note.
Edit notes safely: replace a section under a heading, insert content at a heading, replace the first occurrence of text, and update YAML frontmatter in single or batch mode.
Organize files: move or rename notes.
Navigate links: get backlinks to a note and outgoing links from a note.
Audit links: find broken wikilinks grouped by source file.
Build a local RAG index: index the vault incrementally, reindex one file, or force a full rebuild; query indexed notes semantically.
Manage vault context: set the default vault for the session or pass a vault path override on individual tools.
Provides integration with an Obsidian vault, offering semantic search (RAG) over notes, backlink and wikilink graph traversal, broken link auditing and in-note link repair, daily note and journal logging, plus note management such as creating, moving, renaming notes, editing sections, and updating YAML frontmatter.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@obsidian-mcp search my vault for notes about project planning"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
obsidian-mcp
MCP server for Obsidian vault integration with local RAG (semantic search) — extracted from the former gemini-obsidian and obsidian-rag host plugins so any MCP client can share one server and one index.
Features
Semantic Search (RAG): natural-language questions over your notes, indexed with LanceDB + local embeddings
Graph Traversal: backlinks and outgoing wikilinks
Link Repair: audit broken wikilinks, surgical in-note replacements
Journaling: daily note fetch, timestamped append to headings
Management: create/move/rename notes, YAML frontmatter updates (single or batch), section editing
Fuzzy Search: find files by name or content
Related MCP server: vault-vector
Tools
obsidian_list_notes, obsidian_read_note, obsidian_search_notes, obsidian_rag_index, obsidian_rag_query, obsidian_set_vault, obsidian_create_note, obsidian_append_note, obsidian_get_daily_note, obsidian_get_backlinks, obsidian_get_links, obsidian_move_note, obsidian_update_frontmatter, obsidian_append_daily_log, obsidian_replace_section, obsidian_insert_at_heading, obsidian_replace_in_note, obsidian_get_broken_links, obsidian_get_orphans, obsidian_get_note_outline, obsidian_get_related
Each tool is also callable one-shot from the CLI: node dist/index.js obsidian_rag_index. CLI-only, not MCP tools: validate_frontmatter --hook, obsidian_rag_index --hook, obsidian_rag_index --force_reindex, obsidian_rag_eval --file <queries.json> (scores retrieval against a set of queries). A CLI call exits 0 with the result on stdout, or 1 with the error message on stderr.
Notable parameters:
obsidian_rag_query:folder,type,status,afterfilter results; each hit is labelled with folder, date, status and typeobsidian_list_notes:typeandstatusfilter by frontmatter; results are sorted and 100 are returned per call,offset(default 0) pages through themobsidian_read_note:headingreturns one section,frontmatter_onlyreturns just the YAMLobsidian_read_note:max_chars(default 100000,0= no cap) truncates a full-note orheadingresult with a marker naming the totalobsidian_get_backlinks:file_pathresolves that exact note (path-style and bare links);file_namematches by bare name; lists at most 200 notes, then...and <n> more.A bare-name link (
[[name]], no/) with several candidate notes resolves to the one in the linking note's folder, else the one with the fewest path segments, else the alphabetically first path.[[folder/name]]links match the exact path, case-insensitivelyEvery tool carries MCP annotations (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint) so clients can tell read-only from destructive toolsobsidian_search_notes:regex,folder,limit(filename and content hits both count toward the limit)obsidian_get_note_outline: frontmatter, headings, wikilinks and word countobsidian_get_related: outgoing links, backlinks and semantically similar notesobsidian_get_orphans: notes nothing links to;subfolderlimits the notes reported,linked_fromcounts only links from one prefix (e.g.MOCs/)obsidian_move_note: rewrites wikilinks that point at the moved note unlessupdate_linksis false; refuses an existing destinationlimitonobsidian_search_notes,obsidian_rag_queryandobsidian_get_relatedis clamped to 1..100obsidian_search_noteswithregex: truehas a 2 s time budget and errorsRegex search timed out after 2000 msobsidian_rag_index:force_reindexis CLI-only (node dist/index.js obsidian_rag_index --force_reindex); the MCP tool rejects itobsidian_rag_query:max_distance(CLI--max-distance) drops semantic hits with vector distance above n; keyword hits are kept; no defaultTool arguments are validated against each tool's schema;
obsidian_update_frontmattersingle-keyvaluemust be a string (JSON-stringify arrays and objects, or useupdates)
Install
git clone https://github.com/thoreinstein/obsidian-mcp
cd obsidian-mcp && npm installnpm install builds dist/ through the prepare script. Requires Node >= 22. On Linux x64, set ONNXRUNTIME_NODE_INSTALL=skip before npm install: onnxruntime-node 1.30 otherwise downloads a 236 MB CUDA package this server never uses.
The build bundles everything except the native packages @lancedb/lancedb and onnxruntime-node, which stay external and are the only dependencies installed by npm install. onnxruntime-node is pinned to the exact version @huggingface/transformers requires. The bundled packages, @huggingface/transformers among them, live in devDependencies because they are compiled into dist/index.js. Image pipelines are unused, so the build replaces sharp with an empty stub and never loads it. The overrides in this repo's package.json apply only when this repo is the installing root. Installed as a dependency or globally, LanceDB's optional @huggingface/transformers 3.0.2 still pulls sharp 0.33.5 (which has security advisories) and onnxruntime-node 1.19.2; this server never loads either. To dedupe in your own project, add "overrides": { "@huggingface/transformers": "4.3.1" } to its package.json.
Configure
Point an MCP client at it:
{
"mcpServers": {
"obsidian-mcp": {
"type": "stdio",
"command": "node",
"args": ["/path/to/obsidian-mcp/dist/index.js"]
}
}
}Allowed vaults: the server only operates on vaults it is configured with. Roots come from, in order:
OBSIDIAN_VAULT_PATHSenv var (several absolute paths, separated by the platform path delimiter,:on macOS/Linux)OBSIDIAN_VAULT_PATHenv varvault_paths(array) in the config file (see below)vault_pathin the config file
Relative entries are ignored. The default vault is the first root that exists. A vault_path argument on a tool, and obsidian_set_vault, must name one of these roots (matched by real path, so symlinks and trailing slashes are fine); anything else is rejected with Vault not allowed. obsidian_set_vault only switches the default for the current session and never writes the config file.
The RAG index belongs to one vault: the first vault that indexes it. Indexing another vault fails with Index belongs to vault .... To index a second vault, point OBSIDIAN_MCP_DATA_DIR at a separate directory.
Vault files
The server reads these optional files from the vault; nothing vault-specific is hard-coded.
Write tools only create or edit
.md,.baseand.canvasfiles and refuse any path with a segment starting with.(e.g..obsidian/)Dot-directories (
.obsidian/,.git/,.trash/) are never indexed, searched, listed or link-checked by vault-wide scans, unless a dot-directory is named explicitly assubfolder/folderThe daily note tools (
obsidian_get_daily_note,obsidian_append_daily_log) are exempt fromreadonlyand from frontmatter validation.obsidian/daily-notes.json:folderandformatdecide where the daily note livesAGENTS.md: appended to the server instructions (truncated if long) and served in full as theobsidian://vault-guideresource.obsidian-mcp-schema.json: frontmatter rules enforced on create and frontmatter update, and by thevalidate_frontmatter --hookCLI hook (see Claude Code hooks):
{
"exclude": ["engram/"],
"readonly": ["engram/", ".manifest.json"],
"rules": [
{ "prefix": "Engineering/", "required": ["type"], "allowed": { "type": ["plan", "pattern"] } }
]
}exclude lists path prefixes skipped by validation. readonly lists path prefixes no write tool may change, create, or move notes into or out of; the daily note tools are exempt (above). The first rule whose prefix matches the path applies ("" matches all), with required fields and allowed values per field.
Claude Code hooks
Two CLI modes read a Claude Code hook payload (JSON with tool_input.file_path, and content for a Write) on stdin. Both resolve file_path (absolute or vault-relative) against the vault and ignore anything that is not a .md note inside it.
validate_frontmatter --hook: checks the writtencontentagainst.obsidian-mcp-schema.json. Exit 2 with[obsidian-mcp] Blocked: "<path>" has invalid frontmatter: ...on stderr when it breaks a rule, which makes Claude Code block the write. Exit 0 for everything else: valid content, no schema or an unreadable one, empty or invalid stdin, nocontent(an Edit carries none, so only Writes are checked), a path outside the vault. Paths under the schema'sexcludeprefixes pass.obsidian_rag_index --hook: reindexes the one note named byfile_path. Exit 0 on success and on every no-op (empty or invalid stdin, nofile_path, no vault, a non-note path). When indexing is refused or fails it prints{"success":false,"message":...}on stdout and still exits 0, so the hook never blocks or reports an error to Claude Code; see stderr orOBSIDIAN_MCP_LOG=debugfor why.
{
"hooks": {
"PreToolUse": [
{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "node /path/to/obsidian-mcp/dist/index.js validate_frontmatter --hook" }] }
],
"PostToolUse": [
{ "matcher": "Write|Edit|MultiEdit", "hooks": [{ "type": "command", "command": "node /path/to/obsidian-mcp/dist/index.js obsidian_rag_index --hook" }] }
]
}
}The vault comes from tool_input.vault_path if present (the obsidian_rag_index hook also honors --vault_path), else the configured default vault.
Data paths
Canonical, shared by all clients:
Config:
~/.config/obsidian-mcp/config.jsonLanceDB index:
~/.config/obsidian-mcp/lancedb/File-hash cache:
~/.config/obsidian-mcp/file-hashes.jsonIndex owner:
~/.config/obsidian-mcp/index-meta.json(the vault the index belongs to and the embedder that built it)Embedding model cache:
~/.cache/obsidian-mcp/models/($XDG_CACHE_HOME/obsidian-mcp/models/whenXDG_CACHE_HOMEis set; override withOBSIDIAN_MCP_MODEL_DIR)
Override the directory with OBSIDIAN_MCP_DATA_DIR, the config file with OBSIDIAN_MCP_CONFIG.
The LanceDB directory keeps an hour of table versions (older ones are cleaned up on optimize), so it is larger on disk than the live data.
The embedding model defaults to Xenova/all-MiniLM-L6-v2, pinned to revision 751bff37182d3f1213fa05d7196b954e230abad9 and loaded as q8; set OBSIDIAN_RAG_MODEL to use another. Other models use revision main unless OBSIDIAN_RAG_MODEL_REVISION is set. Non-default models get their own index (lancedb-<model>/, file-hashes-<model>.json, with / in the model name replaced by __), so switching models never mixes embeddings. Reindex after switching.
The model downloads once (about 23 MB) into the model cache on first use. Once the cache holds the model, it loads from there without the network. Set OBSIDIAN_MCP_OFFLINE=1 to forbid the first download too: with an incomplete cache, loading then fails with an error naming the cache directory.
The index meta records the embedder that built it (transformers@4/<model>@<revision>/q8). An index built by another embedder, including every index built before the move to @huggingface/transformers 4, is rebuilt in full by the next obsidian_rag_index (the server's startup index does this). The first start after upgrading from an older embedder therefore runs a full rebuild: about 5 minutes at full CPU for a 3,000-note vault on an Apple M-series machine. Until it finishes, queries log a warning and search returns partial results, and single-note reindexing is refused. node dist/index.js obsidian_rag_index --force_reindex runs the rebuild on demand (the MCP tool rejects force_reindex).
Embedding batches hold at most OBSIDIAN_RAG_EMBED_BATCH_SIZE chunks (default 48) and OBSIDIAN_RAG_EMBED_BATCH_CHARS characters (default 6000); the character cap bounds peak memory on long chunks.
The index rebuilds incrementally from scratch on first use, so migrating from the old plugin locations needs no manual step.
The MCP server runs an incremental index in the background when it starts, so notes edited outside the server (in Obsidian, by exporters, by hand) are picked up on every client restart. Writes through the server's own tools reindex the note immediately.
Environment variables
Variable | Default | Effect |
| unset | Allowed vault roots, absolute, separated by the platform path delimiter |
| unset | One allowed vault root |
|
| Config file |
|
| Index, file hashes and index meta |
|
| Embedding model cache |
|
| Base of the default model cache |
| unset |
|
| unset |
|
|
| Embedding model |
| pinned revision for the default model, else | Model revision |
| 40 | Smallest chunk kept |
| 700 | Chunk size aimed for (must exceed the minimum) |
| 1800 | Largest chunk |
| 48 | Chunks per embedding batch (max 256) |
| 6000 | Characters per embedding batch |
| unset | Install time only, read by |
Invalid or non-positive numeric values fall back to the default.
Development
npm run type-check && npm test && npm run buildTests need the network once, to download the embedding model. Set OBSIDIAN_MCP_OFFLINE=1 to skip the two tests that need it.
See SECURITY.md for the trust model and CHANGELOG.md for changes.
Available Tools
18 toolsobsidian_append_daily_logB
Append text to a specific heading in today's daily note.
Inputs: content (required), heading (required), vault_path.
obsidian_append_noteC
Append text to the end of an existing note.
Inputs: content (required), file_path (required), vault_path.
obsidian_create_noteC
Create a new note with the given content.
Inputs: content (required), file_path (required), vault_path.
obsidian_get_backlinksC
Find all notes that link to a specific note.
Inputs: file_name (required), vault_path.
obsidian_get_broken_linksA
Find all wikilinks in the vault (or a subfolder) that point to non-existent notes. Returns broken links grouped by source file.
Inputs: subfolder, vault_path.
obsidian_get_daily_noteB
Get (or create) today's daily note.
Inputs: vault_path.
obsidian_get_linksC
Get all outgoing links from a specific note.
Inputs: file_path (required), vault_path.
obsidian_insert_at_headingA
Insert content under a specific heading. If heading not found, appends it as a new ## section.
Inputs: content (required), heading (required), file_path (required), position, +1 more.
obsidian_list_notesC
List markdown files in the vault or a subdirectory.
Inputs: subfolder, vault_path.
obsidian_move_noteC
Move or rename a note.
Inputs: dest_path (required), source_path (required), vault_path.
obsidian_rag_indexA
Index the vault for semantic search (RAG). If file_path is provided, only that file is re-indexed. Incremental by default — only re-embeds changed files. Use force_reindex to rebuild from scratch.
Inputs: file_path, vault_path, force_reindex.
obsidian_rag_queryC
Perform a semantic search on the indexed vault.
Inputs: query (required), limit.
obsidian_read_noteC
Read the content of a specific note (Markdown).
Inputs: file_path (required), vault_path.
obsidian_replace_in_noteA
Replace the first occurrence of a specific text string in a note. Use for surgical inline edits, e.g. adding a wikilink to existing text.
Inputs: old_text (required), file_path (required), new_text, vault_path.
obsidian_replace_sectionB
Replace the body under a heading (up to the next heading of equal/higher level, or EOF). The heading line itself is preserved.
Inputs: content (required), heading (required), file_path (required), vault_path.
obsidian_search_notesB
Search for notes containing specific text (simple text match).
Inputs: query (required), vault_path.
obsidian_set_vaultC
Set the default Obsidian vault path for this session.
Inputs: path (required).
obsidian_update_frontmatterB
Update YAML frontmatter of a note safely. Supports single key/value or batch updates.
Inputs: file_path (required), key, value, updates, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 18 tools
Tools target distinct Obsidian operations: file CRUD, daily notes, linking, frontmatter, search, and RAG. Overlaps like append_note vs. append_daily_log and replace_in_note vs. replace_section are clearly differentiated by descriptions. An agent can reliably select the intended tool.
All 18 tools use the obsidian_ prefix and snake_case, mostly verb_noun patterns. Minor variation for rag_index/rag_query and daily-note composites, but convention is highly predictable.
18 tools is slightly above the ideal 3-15 range, but the domain is feature-rich and each tool covers a distinct capability. It does not feel redundant or bloated.
The surface covers read/list/create/append, surgical edits, frontmatter, links/backlinks, search/RAG, daily notes, and move/rename. A delete-note operation is missing, but most lifecycle workflows are covered or workable.
Maintenance
Related MCP Connectors
Markdown-based note-taking with a hosted MCP server. Your notes serve you and your AI.
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Notes and memory assistant: one Markdown vault that Claude, ChatGPT and any MCP client can share.
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables MCP clients to access and manage a personal markdown knowledge base stored in Cloudflare R2. Provides tools for listing, reading, writing, searching (full-text and semantic), and following backlinks between notes.14 npm3MIT
- AlicenseNot gradedqualityCmaintenanceEnables semantic and full-text search over markdown notes, with tools to create, edit, move, and delete notes, all through MCP. Runs entirely locally using Ollama for embeddings and SQLite for storage, working with Obsidian or standalone.MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to read, search, create, and append Markdown notes from a local vault (e.g., Obsidian) via MCP, allowing natural-language note management through compatible clients.-
- FlicenseNot gradedqualityAmaintenanceEnables creating, linking, searching, exploring, and synthesizing atomic notes as a knowledge graph through MCP-compatible clients such as Claude. Supports tags, note types, semantic relationships, Markdown storage, and an SQLite index for efficient traversal.-