obsidian-mcp-server: surgical vault edits over the Obsidian Local REST API
Read, write, search, and surgically edit Obsidian vault notes, tags, and frontmatter via MCP. STDIO or Streamable HTTP.
At a glance
- What is it?
- cyanheads/obsidian-mcp-server is a TypeScript MCP server that exposes 14 tools and 3 resources for reading, searching and editing Obsidian vault notes through the Local REST API. Its design bet is narrow, location-addressed edits rather than whole-file rewrites, and that bet drives both its safety properties and its sharpest limits.
- Who is it for?
- Adopt it if your agent needs to change a heading, a block or a frontmatter key inside notes that already exist, and you can live with a vault that must be open in the Obsidian desktop app behind an API key. Do not adopt it if you want to edit vaults offline, in bulk, or without an LLM in the loop, because every write path here is a tool call and the server has no batch mode.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem is not reading notes, it is editing one line of them
Giving a language model raw filesystem access to a vault produces whole-file rewrites. A model that wants to add one bullet under a heading reads a 400-line note, regenerates the note, and silently drops whatever it failed to reproduce. The damage is invisible until you diff. obsidian-mcp-server addresses that specific failure by making the edit target explicit. The README describes obsidian_patch_note as surgical append, prepend or replace against a heading, block reference or frontmatter field, and obsidian_manage_frontmatter as atomic get, set or delete on a single frontmatter key. The unit of change is a named location, not a file. The audience is narrow: people running an MCP-capable client (the README ships install links for Claude Desktop, Cursor and VS Code) against a vault they care enough about not to hand to a generic file tool. If you only need an agent to read notes and answer questions, the write tools are dead weight and a read-only MCP filesystem server would do the same job with less surface area.
The server talks to the Obsidian Local REST API, not to the vault directory
The architecture is a thin client. The server holds no index of your vault and does not parse files off disk; it calls the Obsidian Local REST API, which means Obsidian itself must be running and reachable. The install snippets in the README pass the credential as an environment variable, OBSIDIAN_API_KEY, alongside the npx invocation. Two transports are listed: STDIO for local clients that spawn the process, and Streamable HTTP for clients that connect to a URL. The README states that JSONLogic search is evaluated against path, content, frontmatter.<key>, tags and stat.{ctime,mtime,size}, with custom glob and regexp operators, and that text-mode search supports a pathPrefix filter. Because the index lives in Obsidian, search quality varies with what Obsidian and its plugins can answer. The README notes that BM25-ranked search depends on Omnisearch being reachable, which is a plugin dependency the server cannot satisfy on its own. Tools are grouped by shape: readers fetch notes and metadata, writers create or edit content, managers reconcile tags and frontmatter, and a guarded escape hatch dispatches command-palette commands.
Four read projections and the document map that feeds the writer
obsidian_get_note resolves a target three ways: a vault path, the active file, or a periodic note (daily, weekly, monthly, quarterly, yearly). It then returns one of four projections. content gives the raw markdown body. full gives content, frontmatter, tags and file metadata, and with includeLinks: true it also parses outgoing wiki and markdown links, restricted to vault-internal references. document-map returns a catalog of headings, block references and frontmatter fields. section returns a single heading, block or frontmatter value, and the README specifies that heading sections include the full subtree beneath that heading. The intended workflow is stated plainly: pair the document-map projection with obsidian_patch_note to discover edit targets before patching. That is the mechanism that makes surgical editing work in practice, since the model must learn the exact locator before it can address it. The section projection also echoes the resolved locator back in sectionTarget as a full Parent::Child path, and when a bare leaf name collides across several headings it lists every colliding path in candidates while still returning the first match. That echo is the useful part: it turns an ambiguous edit into a visible one, but only if the caller reads the field.
Search modes and the glob/regexp argument order that bites
obsidian_search_notes offers three modes selected by mode. text does substring matching with context windows sized by contextLength (default 100 characters per side of each match) and accepts pathPrefix, which the README says is rejected in any other mode with path_prefix_invalid_mode. jsonlogic evaluates a JSONLogic tree over note fields. The third is BM25-ranked Omnisearch when that plugin is reachable. Results paginate through opaque cursors. The custom glob and regexp operators take a two-element array in a fixed order: pattern first, then the field reference, as in {"glob": ["Projects/*.md", {"var": "path"}]}. The README is explicit about what happens when you reverse it: the note's own field is compiled as the pattern, glob then matches nothing, and regexp fails outright on whatever the field parses as. A silent empty result set is the worst kind of wrong answer for an agent, because the model will report that no such notes exist. The README also notes that backlinks are expressed through this same regexp form, since there is no dedicated backlink tool and no upstream endpoint for one. That is a real gap: a common Obsidian question requires hand-written JSONLogic rather than a named tool.
Write tools are guarded, and the guards are the point
obsidian_write_note creates a note, replaces a single section in place, or clobbers an existing file when overwrite: true is set. The README states that it refuses whole-file writes against an existing path by default. obsidian_append_to_note behaves differently depending on whether section is supplied: without it, the file is created if missing; with it, the append targets a specific heading, block or frontmatter field and the file must already exist. obsidian_replace_in_note does search-replace inside one note, scoped to the body by default, with literal or regex matching, whole-word and whitespace-flexible and case-sensitivity options, and capture-group replacement. obsidian_manage_tags defaults to the frontmatter tags: array, and only touches the note body when location is set to 'inline' or 'both'. obsidian_delete_note permanently deletes and always asks the user to confirm first, with the call answered by a confirmation request and retried with the answer. Two tools, obsidian_list_commands and obsidian_execute_command, are opt-in behind OBSIDIAN_ENABLE_COMMANDS=true. The v3.5.0 release notes mention frontmatter-safe tag removal and note replacement, which suggests earlier versions had failure modes in exactly that area. The confirmation round-trip on delete is the strongest safety design here, and it is also the one a headless client will find most awkward.
Where it is the wrong tool
The dependency on a running Obsidian desktop app with the Local REST API enabled is the hard boundary. There is no headless path described in the README, so a CI job, a cron-driven sync, or a server without a GUI cannot use this. Bulk operations are the second gap. There is no batch tool. Every edit is one tool call, and obsidian_delete_note adds a confirmation round-trip on top, so a task like retagging 200 notes becomes 200 conversations. Third, obsidian_list_notes walks recursively with a default depth of 2 and a maximum of 20, capped at 1000 entries, and obsidian_list_tags is capped at limit (default 200, max 10000) with the withheld remainder disclosed. Those caps are sensible against runaway responses, but a vault that exceeds them needs several calls with narrower filters. Fourth, the section read returns the first match when a leaf name is ambiguous, listing alternatives in candidates rather than refusing. That is a defensible default for a read, and a hazardous one to build an automated patch loop on without checking candidates. Finally, the tool surface assumes an MCP client that can handle multi-turn confirmation; a client that cannot will stall on delete.
How it compares to the Obsidian Local REST API MCP server
The obvious alternative is the community MCP server that wraps the same Obsidian Local REST API and exposes it largely as-is, with the upstream endpoints surfacing close to one-to-one. The difference is where the intelligence sits. A thin wrapper hands the model raw endpoint shapes and leaves it to compose the right request; obsidian-mcp-server composes for it. document-map exists so the model can discover a heading path before patching it. obsidian_patch_note and obsidian_manage_frontmatter exist so the model never has to reconstruct a file to change one field. The trade is coverage against ergonomics: a thin wrapper can reach whatever the API grows next, while this server only exposes what its 14 tools describe, and its JSONLogic backlink workaround is a symptom of that ceiling. A second alternative is skipping MCP entirely and pointing an agent at the vault directory with a filesystem tool. That removes the running-app requirement and the API key, and it removes every guard described above, including the whole-file write refusal and the delete confirmation. Which one is right depends on whether you trust the model more than you trust the file format.
Licence, releases and what maintenance costs you
The project is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant, with the usual requirements to retain notices and state changes. This is not legal advice; read the LICENSE file if you plan to redistribute a modified build. Maintenance cost is mostly dependency churn. The package declares the MCP SDK at ^2.0.0, TypeScript at ^7.0.2 and Bun v1.3.0, and the v3.5.1 release notes list a dependency refresh alongside 5xx classification widening and a session-mode pin. Those three items in one patch suggest the server tracks a moving protocol and a moving runtime rather than a frozen interface. There is also a Docker image published to ghcr.io, which gives you a pinned alternative to npx pulling whatever the registry serves. The v3.5.2 notes mention periodic-route detection and tag/section resolution fixes, so recent releases have been correcting how locators resolve. If you pin a version, pin it deliberately and read the changelog before moving, because the fixes cluster around exactly the addressing behaviour your edits depend on.
Editorial conclusion
Adopt it if your agent needs to change a heading, a block or a frontmatter key inside notes that already exist, and you can live with a vault that must be open in the Obsidian desktop app behind an API key. Do not adopt it if you want to edit vaults offline, in bulk, or without an LLM in the loop, because every write path here is a tool call and the server has no batch mode. Before trusting it with a real vault, run obsidian_get_note with format: "document-map" on one note, patch a test note through obsidian_patch_note, and confirm on disk that only the targeted section changed.
Community notes