Vault Cortex Obsidian MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| LOG_LEVEL | No | Logging verbosity: debug, info, warn, or error. | info |
| PUBLIC_URL | No | Public URL clients use to reach the server. Used as the OAuth issuer URL in discovery metadata. | http://localhost:8000 |
| VAULT_PATH | Yes | Absolute path to your Obsidian vault, mounted into the container. | |
| INDEX_DB_PATH | No | SQLite FTS5 index DB path inside the container. | /data/index.db |
| MCP_AUTH_TOKEN | Yes | Bearer token for MCP client authentication. Generate with: openssl rand -hex 32 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Read, write, and search an Obsidian vault. Use vault_search and vault_read_note to find and read notes; vault_read_file for images, canvases, and other non-markdown files. Use vault_get_memory to retrieve user preferences and context from About Me/ files. Use vault_write_note and vault_update_memory for writes.
Vault content is Obsidian Flavored Markdown. Write tools pass content through without escaping — be intentional about Obsidian syntax (#, [[, %%, etc.) in inputs.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| vault_read_noteA | Read a markdown note by its vault-relative path. By default returns the full raw content including properties; optional modes return just the properties, just the heading outline, or just one section. Example: vault_read_note({ path: "Projects/vault-cortex.md" }) Example: vault_read_note({ path: "TASKS.md", outline: true }) Example: vault_read_note({ path: "TASKS.md", heading: "Active" }) Example: vault_read_note({ path: "TASKS.md", heading: "Done", heading_level: 2 }) Example: vault_read_note({ path: "TASKS.md", heading: "Done", start_line: 1, limit: 20 }) When to use: You know the exact path and need a specific note's content. For a large note (a long Kanban board or doc), use outline: true to see its headings and any text sitting above them, then heading: "..." to read just the one section you need. For a note or section too long for one response, page it with start_line and limit to read a window at a time. To check a note's or section's line count, request start_line: 1 with limit: 1 — one line plus the total. Prefer vault_search when you don't know the path. For task status or order on a board, prefer vault_list_tasks; heading mode returns a lane's Markdown. Prefer vault_get_memory for About Me/ files (returns content without properties). To edit a section you've read, use vault_patch_note. To explore what links to this note or what it links to, use vault_get_backlinks and vault_get_outgoing_links. Behavior: a section spans from its heading to the next heading with the same or a smaller level number (or EOF), so it includes its child headings. Empty headings ("##" with no text) act as boundaries but cannot be targeted; read the parent section or the full note, and edit them via vault_replace_in_note. Errors:
Returns: Raw markdown string (default); JSON object of properties, {} when the note has none (properties_only); JSON outline object, shaped as the outline parameter describes (outline); raw markdown of the section, heading line included (heading). When start_line or limit is given, the result is preceded by a window-metadata text block ("path — lines 1–20 of 250 (continue with start_line: 21)"). Unpaged full reads return the file's exact bytes; heading sections and paged reads use LF line endings. In the outline, bytes at the root is the whole file's size, modified its modification time (ISO 8601), and each heading's bytes the size of the text heading mode returns for that section; an empty heading appears with text: "". |
| vault_list_notesA | List the vault's note (.md) paths, optionally filtered by folder and/or glob pattern. Returns paths only — not content or metadata. Example: vault_list_notes({ folder: "Projects" }) Example: vault_list_notes({ glob: "**/session-log.md" }) Example: vault_list_notes({ folder: "Projects", glob: "*.md" }) — the folder's top-level notes only When to use: Browsing what exists in a folder by filename, or finding notes matching a path pattern. Prefer vault_search_by_folder when you need metadata (tags, type, related) along with paths. Prefer vault_search for content-based discovery. Use vault_read_note to read a note from the results. Parameters:
Behavior: Paths come back sorted by vault-relative path in code-unit order (uppercase before lowercase). Hidden (dot-prefixed) notes and folders are never listed. Errors:
Returns: JSON array of every matching vault-relative path (e.g. ["Notes/idea.md", "Projects/plan.md"]). |
| vault_write_noteA | Create a markdown note, or with overwrite: true replace an existing note's body. Properties merge with any existing properties when overwriting (new keys added, matching keys overwritten, keys set to null removed, unmentioned keys preserved); overwriting without properties keeps the existing property values. A new note leaves out keys set to null. Example: vault_write_note({ path: "Projects/notes.md", body: "# Notes\n\nProject notes here.", properties: { tags: ["project"], type: "project" } }) Example: vault_write_note({ path: "Projects/notes.md", body: "Updated content.", overwrite: true }) When to use: Creating a new note, or replacing a whole note's body. Prefer vault_patch_note or vault_replace_in_note to change part of a note. Prefer vault_update_properties for property-only edits (no body round-trip). Prefer vault_update_memory for appending dated entries to About Me/ memory files. Errors:
Obsidian syntax: Body is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag (escape with #), [[ = wikilink, %% = comment block. In properties: quote wikilink values ("[[Note]]"), use YAML lists for tags, keep property types consistent (string/number/list mismatches cause silent query failures). Returns: "Wrote ". |
| vault_patch_noteA | Surgical edits to a markdown note — append, prepend, replace, or insert content by heading. Properties are kept as written, and body line breaks are saved as LF. A repeated call can write the content again, so after a timeout or lost response, re-read the note before retrying. Example: vault_patch_note({ path: "Projects/plan.md", operation: "append", heading: "Open questions", content: "- Which region hosts the backup?" }) Example: vault_patch_note({ path: "Notes/Plan.md", operation: "replace", heading: "Status", content: "On track for launch.\n" }) Example: vault_patch_note({ path: "Notes/Plan.md", operation: "insert_before", heading: "Phase 2", content: "## Phase 1\nDone.\n" }) Example: vault_patch_note({ path: "Notes/Plan.md", operation: "prepend", content: "> [!info] Draft\n> Not reviewed yet.\n" }) — adds a leading callout When to use: Modifying part of an existing note without overwriting the entire body. Prefer vault_write_note for a new note or a full rewrite (overwrite: true); vault_replace_in_note for an in-place text change (a typo, a rename); vault_insert_at_anchor for inserting next to a specific line; vault_replace_span for replacing a run of lines; vault_delete_span for removing lines; vault_update_properties for properties; vault_create_task for adding a task; vault_update_task for a task's status or lane. Operations:
Behavior:
Errors:
Obsidian syntax: Content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block. Table rows: send only the data row ("| cell1 | cell2 |"), not the header or separator — duplicating them splits the table. Returns: Confirmation message — "Applied to → ", where target is the matched heading (e.g. "## Active") or "file body" for a no-heading append/prepend. A no-heading prepend that nested existing content under an inserted heading adds a sentence naming the content's size and any call that would have avoided it. |
| vault_replace_in_noteA | Find and replace exact text in a markdown note's body. Properties are kept as written, and body line breaks are saved as LF. Edit properties via vault_update_properties or vault_write_note's properties parameter. A repeated call replaces old_text again if the note still holds it, including a copy inside the first call's new_text, so after a timeout or lost response, re-read the note before retrying. Example: vault_replace_in_note({ path: "Projects/plan.md", old_text: "TODO: write summary", new_text: "Summary complete." }) Example: vault_replace_in_note({ path: "Projects/plan.md", old_text: "- [ ] draft outline\n", new_text: "" }) Example: vault_replace_in_note({ path: "Projects/plan.md", old_text: "Q3", new_text: "Q4", replace_all_occurrences: true }) When to use: Targeted text changes within a single location — fixing typos, updating values, renaming terms, or removing a short line (new_text=""). To delete a large multi-line block, prefer vault_delete_span (short anchors instead of full old_text). To replace a large block by anchors instead of reproducing the full old_text, use vault_replace_span. To relocate content between headings, use vault_patch_note to add at the target first, then remove from source (new_text="") — add-before-delete, so a failure duplicates the block instead of losing it. Parameters:
Behavior:
Errors:
Obsidian syntax: new_text is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block in replacement text. Returns: "Replaced occurrence(s) in ". |
| vault_delete_spanA | Delete a contiguous block of whole lines from a note's body by referencing short anchor substrings instead of reproducing the full block text. Properties are kept as written, and body line breaks are saved as LF. With first_match: true, a repeated call deletes the next match, so after a timeout or lost response, re-read the note before retrying. Example: vault_delete_span({ path: "Tracker.md", start_anchor: "| 2024-03-02 | Acme" }) — deletes the one table row whose line contains that fragment. Example: vault_delete_span({ path: "Notes/Plan.md", start_anchor: "> [!warning] Stale", end_anchor: "remove after launch" }) — deletes from the start anchor line through the end anchor line. When to use: Removing a block you have already read — a table row, callout, or run of list items — where reproducing it exactly as old_text would be error-prone. Prefer vault_replace_in_note for small in-place edits. To replace a block, use vault_replace_span (one atomic step). Parameters:
Behavior:
Errors:
Returns: 'Deleted line(s) from : ""' — the removed lines joined by line breaks, cut at 80 characters with "…". |
| vault_replace_spanA | Replace a contiguous block of whole lines in a note's body with new content, identified by short anchor substrings instead of the block's full text. Properties are kept as written, and body line breaks are saved as LF. A repeated call can write the content again, or with first_match: true replace the next match, so after a timeout or lost response, re-read the note before retrying. Example: vault_replace_span({ path: "Tracker.md", start_anchor: "| 2024-03-02 | Acme", content: "| 2024-03-02 | Acme Corp | Updated |" }) — replaces the one table row whose line contains that fragment. Example: vault_replace_span({ path: "Notes/Plan.md", start_anchor: "> [!warning] Stale", end_anchor: "remove after launch", content: "> [!info] Current\n> Updated for v2." }) — replaces the callout block with a new one. When to use: Replacing a block you have already read — a table row, callout, or run of list items — where reproducing it exactly as old_text would be error-prone. Prefer vault_replace_in_note for small in-place text changes (typos, renaming). Prefer vault_delete_span when removing without replacement. Parameters:
Behavior:
Errors:
Obsidian syntax: content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block. Returns: Confirmation message "Replaced line(s) with line(s) in " — N counts the lines the span covered, M the lines in content as sent (its line breaks plus one). |
| vault_insert_at_anchorA | Insert content as whole lines into a note's body, before or after a specific line identified by a short anchor substring. Properties are kept as written, and body line breaks are saved as LF. A failed save leaves the note unchanged. A repeated call inserts the content again, or fails as ambiguous when the content holds the anchor, so after a timeout or lost response, re-read the note before retrying. Example: vault_insert_at_anchor({ path: "Tracker.md", anchor: "| 2024-03-02 | Acme", position: "after", content: "| 2024-03-03 | Beta Corp | New entry |" }) — inserts a new table row after the matched row. Example: vault_insert_at_anchor({ path: "Notes/Plan.md", anchor: "## Phase 2", position: "before", content: "> [!note] Phase 1 must close before this starts.\n" }) — inserts a callout and a blank line above the Phase 2 heading. When to use: Adding content at a precise location identified by a nearby line's text, without needing to know the heading structure. Good for inserting rows into tables, adding items into lists at a specific position, or placing content relative to a known landmark line. Prefer vault_patch_note for heading-targeted inserts (append/prepend to a section). Prefer vault_replace_span when replacing a block rather than inserting next to it. Parameters:
Errors:
Obsidian syntax: content is Obsidian Flavored Markdown (no escaping applied). Watch for: #word = tag, [[ = wikilink, %% = comment block. Returns: Confirmation message "Inserted line(s) <before|after> anchor in " — N counts the lines in content as sent (its line breaks plus one). |
| vault_delete_noteA | Delete a markdown note, moving it to the vault's .trash/ folder or removing it for good as the vault's Obsidian "Deleted files" setting directs. Example: vault_delete_note({ path: "Scratch/temp.md" }) Example: vault_delete_note({ path: "Archive/2024/old.md", prune_empty_folders: true }) — also remove "Archive/2024" (and "Archive") if deleting the note empties them. When to use: Removing a note you no longer need. Prefer vault_delete_memory for removing individual dated entries from About Me/ memory files. To relocate a note, use vault_move_note instead. To replace a note's content, use vault_write_note with overwrite: true instead. Behavior:
Parameters:
Errors:
Returns: Confirmation message naming the outcome — "Deleted " for permanent removal, "Moved to trash (<.trash/ path>)" when the note landed in .trash/. Notes how many empty folders were pruned when any were. |
| vault_move_noteA | Move or rename a note and rewrite every link across the vault that points to it, like Obsidian's built-in rename. Incoming links in other notes — [[wikilinks]], [[wikilink|aliases]], [[wikilink#headings]], ![[embeds]], markdown, and links in properties (e.g. related:) — and Canvas cards that embed the note are updated to the new path; the moved note's own relative links are fixed so they still resolve from the new folder, including relative links to attachments (e.g. ![[../assets/photo.png]]). A link is only rewritten when leaving it unchanged would break it, so a short [[Note]] that stays unambiguous after a folder move is left alone. Example: vault_move_note({ old_path: "Inbox/Draft.md", new_path: "Inbox/Spec.md" }) — pure rename. Example: vault_move_note({ old_path: "Inbox/spec.md", new_path: "Inbox/Spec.md" }) — case-only rename; works even where the filesystem treats both spellings as one file. Example: vault_move_note({ old_path: "Inbox/Spec.md", new_path: "Projects/Spec.md" }) — move to another folder. Example: vault_move_note({ old_path: "Inbox/Spec.md", new_path: "Projects/Spec.md", prune_empty_folders: true }) — also remove "Inbox" if the move empties it. When to use: Renaming a note or relocating it to a different folder while keeping the link graph intact. Prefer this over vault_write_note + vault_delete_note, which would orphan every backlink. To only change a note's body or properties, use vault_patch_note or vault_update_properties. Protected paths can be neither moved nor moved into: About Me/ and the daily notes folder (Daily Notes/ by default; vault_get_daily_note's path shows the folder in use; not protected when Obsidian's Daily Notes folder setting is the vault root or blank). Parameters:
Errors:
Obsidian syntax: Link rewrites preserve each link's existing form — embed marker (!), heading anchor (#…), and alias (|…) are kept; a markdown link keeps its original extension and link text. Returns: JSON with moved_to (the new path), links_updated (count of link occurrences rewritten, including the moved note's own relative links), updated_notes (sorted paths of the other notes and Canvas files that were edited; the moved note is implied by moved_to), and pruned_empty_folders (count of empty parent folders removed). |
| vault_update_propertiesA | Update a note's properties. By default they merge shallowly — new keys added, matching keys overwritten, null deletes a key, unmentioned keys preserved. With replace: true they replace the whole properties block: here null writes a property with no value, and {} removes the whole block. The body is kept. Example: vault_update_properties({ path: "Projects/todo.md", properties: { status: "active", draft: null } }) Example: vault_update_properties({ path: "Projects/todo.md", properties: { title: "Todo", status: "active" }, replace: true }) When to use: Changing tags, status, type, or any property without reading/rewriting the full note body. Prefer vault_write_note when creating a new note, or replacing the body (with overwrite: true). Read current properties first with vault_read_note({ properties_only: true }), or the full note when repairing a properties block the server refused. Arrays are replaced entirely, not appended to. Errors:
Obsidian syntax: Use arrays for multi-value fields (tags: [a, b]), quote wikilinks ("[[Note]]"), keep types consistent (mismatches cause silent query failures). Returns: "Updated properties on ", or "Replaced properties on " with replace: true. |
| vault_searchA | Hybrid search across vault notes and file content (canvas, PDF, and text files), ranked by combined keyword and semantic relevance. Semantic matching finds notes even when exact keywords differ — "career aspirations" finds notes about "goals" and "targets". Unquoted terms use implicit AND with stemming ("running" also matches "run"); wrap in double quotes for exact phrases; punctuated terms (vault-cortex, deploy/local) are matched as exact adjacent-word phrases. Filters — all conditions AND-combine with each other and the text query; any filter but folder returns notes only:
Example: vault_search({ query: "how the server watches for file changes" }) Example: vault_search({ query: "kubernetes networking", filters: { tags: ["reference"] } }) Example: vault_search({ query: "meeting notes", filters: { type: "meeting", folder: "Work" } }) Example: vault_search({ query: "decision", filters: { modified: { after: "2026-06-30" } } }) — matching notes touched in July or later When to use: The primary discovery tool for content-based queries, optionally constrained by metadata. Prefer vault_search_by_tag for tag-only queries without text. Prefer vault_search_by_folder for browsing a folder. Prefer vault_search_by_property for metadata-only queries. Prefer vault_recent_notes for time-based browsing. Behavior: Results can lag a file change by a few seconds. Errors:
Returns: JSON with results array (path, title, snippet, score, tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), folder (a note's top-level folder; a file's full parent folder), type, kind, extension, created, modified, bytes), total (results returned, not all matches), search_mode ("hybrid" when vector embeddings contributed, "fts" when only keyword matching ran), and reranked (true when a second ranking pass reordered the results). A higher score is more relevant. kind is "note" or, for non-markdown content, "file", which also carries extension (e.g. ".pdf"). created is omitted, not null, when a result has none; type is null when the result has no type property; bytes is the on-disk size. With include_leading_callout, each result also carries leading_callout (the opening callout, { type, title, body }) when present. |
| vault_search_by_tagA | Find notes by tag, in the tags property or as a #tag in the body, ignoring letter case. Unless exact is true, a tag also matches every tag nested under it at any depth: "project" matches "project/vault-cortex" and "project/a/b". A nested tag continues with "/", so "project" never matches "projects" or "my-project". Example: vault_search_by_tag({ tag: "project" }) Example: vault_search_by_tag({ tag: "project", exact: true, limit: 50 }) — notes tagged "project" itself, up to 50 When to use: Tag-only lookups, for one tag or a whole tag hierarchy, with no text query. Use vault_list_tags first to discover available tags. Prefer vault_search when you need text-based relevance ranking. Parameters:
Behavior: Results can lag a file change by a few seconds. Errors:
Returns: JSON array of note metadata: path, title (the title property; file name without .md when missing or not text), tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), related ([] when missing), folder (top-level, "" at the root), type (null when missing or not text), created (null when missing or not an ISO date), modified (ISO 8601 file time), bytes (on-disk size), and when present leading_callout (the opening callout, { type, title, body }) and additional_properties (every property but title, type, created, related and tags). Tags keep the note's letter case. Sorted by most recently modified, then by path. |
| vault_list_tagsA | List all tags in the vault with note counts, ordered by count descending, then by tag name. Tags in the tags property and #tags written in note bodies both count. Spellings that differ only in letter case are one tag, shown as the spelling written most often. Each hierarchical tag appears as one full entry, not split into segments. Count is unique notes, not occurrences. Example: vault_list_tags() returns [{ tag: "session-log", count: 42 }, { tag: "project/vault-cortex", count: 8 }, ...] When to use: Discovering what tags exist before searching by tag. Good first step for vault orientation. Prefer vault_search_by_tag once you know which tag to query. To count tags property values in one folder, or only the most-used ones, use vault_list_property_values({ key: "tags" }) with folder or limit; it does not count #tags in note bodies. Errors:
Returns: JSON array of { tag, count } holding every tag in the vault; tag omits the "#" prefix. |
| vault_recent_notesA | List recently modified or created notes, sorted by timestamp — a time-ordered window into the vault, not a date-range filter. Example: vault_recent_notes({ limit: 10 }) Example: vault_recent_notes({ sort_by: "created", limit: 5 }) When to use: Catching up on vault changes, finding recent work, or orienting after a break. Prefer vault_search for content-based discovery, including a text query limited to a date range (its created and modified filters). Prefer vault_search_by_folder for browsing a specific folder. Parameters:
Behavior: Results can lag a file change by a few seconds. Errors:
Returns: JSON array of note metadata: path, title (the title property; file name without .md when missing or not text), tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), related ([] when missing), folder (top-level, "" at the root), type (null when missing or not text), created (null when missing or not an ISO date), modified (ISO 8601 file time), bytes (on-disk size), and when present leading_callout (the opening callout, { type, title, body }) and additional_properties (every property but title, type, created, related and tags). Newest first by the sort_by timestamp. |
| vault_search_by_folderA | Browse notes in a folder with full metadata. Example: vault_search_by_folder({ folder: "Projects" }) Example: vault_search_by_folder({ folder: "About Me", recursive: false, limit: 10 }) — the folder's own notes, 10 most recent When to use: Exploring a folder's contents with full context for vault orientation. Prefer vault_list_notes when you only need paths. Prefer vault_search when you have a text query. Use vault_get_backlinks or vault_get_outgoing_links to explore how notes in a folder connect to the rest of the vault. Parameters:
Behavior: Results can lag a file change by a few seconds. Notes in hidden (dot-prefixed) folders never appear. Errors:
Returns: JSON array of note metadata: path, title (the title property; file name without .md when missing or not text), tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), related ([] when missing), folder (top-level, "" at the root), type (null when missing or not text), created (null when missing or not an ISO date), modified (ISO 8601 file time), bytes (on-disk size), and when present leading_callout (the opening callout, { type, title, body }) and additional_properties (every property but title, type, created, related and tags). Sorted by most recently modified, then by path. |
| vault_list_property_keysA | Discover all property keys in the vault with note counts and sample values, without reading individual notes. Example: vault_list_property_keys() returns [{ key: "tags", count: 342, sample_values: ["session-log", "project"] }, ...] Example: vault_list_property_keys({ folder: "Projects" }) — the keys used under Projects/ only When to use: Discovering what properties exist before searching by property. Good first step for vault orientation alongside vault_list_tags. Prefer vault_list_property_values when you need the full list of values for a specific key. Prefer vault_search_by_property to find notes matching a specific key-value pair. Parameters:
Behavior: Results can lag a file change by a few seconds.
Errors:
Returns: JSON array of { key, count, sample_values } sorted by count descending, then by key. |
| vault_list_property_valuesA | List distinct values for a specific property key with how often each occurs. Example: vault_list_property_values({ key: "status" }) returns [{ value: "done", count: 211 }, { value: "active", count: 47 }, ...] Example: vault_list_property_values({ key: "tags", limit: 10 }) — the 10 most-used tags property values When to use: Enumerating possible values for a property key before calling vault_search_by_property. Call vault_list_property_keys first to discover valid key names. A value listed here can be passed straight to vault_search_by_property, which matches text exactly and a complete number numerically too, so value "1" finds the number 1, the text "1", and a checked checkbox. Parameters:
Behavior: Results can lag a file change by a few seconds.
Errors:
Returns: JSON array of { value, count } sorted by count descending, then by value in binary text order ("10" before "2"). |
| vault_search_by_propertyA | Find notes where a property matches a value. Example: vault_search_by_property({ key: "status", value: "in-progress" }) Example: vault_search_by_property({ key: "type", value: "session-log", folder: "Code Projects" }) When to use: Finding notes by metadata when you don't have a text query. Prefer vault_search when you also have a text query (it supports property filters too). Prefer vault_search_by_tag for tag-specific queries (it matches nested tags and #tags in note bodies; key "tags" here matches only the tags property). Use vault_list_property_keys to discover valid keys and vault_list_property_values to see what values a key takes. Parameters:
Behavior: Results can lag a file change by a few seconds. Errors:
Returns: JSON array of note metadata: path, title (the title property; file name without .md when missing or not text), tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), related ([] when missing), folder (top-level, "" at the root), type (null when missing or not text), created (null when missing or not an ISO date), modified (ISO 8601 file time), bytes (on-disk size), and when present leading_callout (the opening callout, { type, title, body }) and additional_properties (every property but title, type, created, related and tags). Sorted by most recently modified (any file write, so a recently-synced note can sort ahead of an older edit), then by path. |
| vault_get_backlinksA | Find all notes and canvases that link to a given note or canvas — captures [[wikilinks]], markdown, ![[embeds]], wikilinks inside properties (e.g. related:), and canvas cards that embed the file. Heading anchors ([[note#heading]]) and aliases ([[note|alias]]) resolve as backlinks to the base note. Links inside code blocks are ignored; a note linking to itself appears in its own backlinks. Example: vault_get_backlinks({ path: "Projects/vault-cortex.md" }) Example: vault_get_backlinks({ path: "Diagrams/architecture.canvas" }) When to use: Understanding what references a note or canvas, assessing its connectivity before editing or deleting, or finding related notes via the graph. For outgoing links (what a note links TO), use vault_get_outgoing_links. To find notes nothing else links to, use vault_find_orphans. Parameters:
Returns: JSON with path (the queried note or canvas), backlinks (array of { path, title, bytes } sorted by title; bytes is the source's on-disk size), and count. Errors:
|
| vault_get_outgoing_linksA | Find all notes and files a given note or canvas links to. For notes: [[wikilinks]], markdown, ![[embeds]], and wikilinks inside properties (e.g. related:). For canvas files: file nodes, not links inside text cards. Links inside code blocks are ignored; self-links are included. Each target is listed once, however often the note links to it. Example: vault_get_outgoing_links({ path: "Projects/vault-cortex.md" }) Example: vault_get_outgoing_links({ path: "Diagrams/architecture.canvas" }) When to use: Navigating the graph forward, auditing broken links in one note or canvas, or checking dependencies before editing. For incoming links (what links TO a note), use vault_get_backlinks. Behavior: Results can lag a file change by a few seconds. Returns: JSON with path, outgoing_links (array of { path, title, exists, kind, bytes, daily_note_forward_ref } sorted by target path), and count. A target's path is the vault-relative path the link resolved to; a broken link's path is its target text without any #heading or |alias, with a markdown link's %-escapes decoded. When exists is true, kind "note" is a markdown note readable via vault_read_note and kind "file" a non-markdown file (.canvas, image, PDF) readable via vault_read_file; when exists is false the link is broken and kind is "note" even for a path like "diagram.png". title is null for files and broken links. daily_note_forward_ref is true on a broken link into the daily notes folder (a daily note not yet created, not breakage) and false otherwise, including when Obsidian's Daily Notes folder setting is the vault root or blank. bytes is the on-disk size (null for broken links), not the delivery cost: vault_read_file downscales a large image to fit response limits. Errors:
|
| vault_find_orphansA | Find notes with no incoming links from other notes or canvases. A note that only links to itself still counts as an orphan. Example: vault_find_orphans({}) Example: vault_find_orphans({ exclude_folders: ["Templates", "Archive"], limit: 20 }) When to use: Vault maintenance — surfacing notes to integrate into the graph. Link an orphan by mentioning it from a relevant note with vault_patch_note. Prefer vault_get_backlinks to check the connectivity of one specific note rather than scanning the whole vault. Parameters:
Behavior: Results can lag a file change by a few seconds. Errors:
Returns: JSON array of note metadata: path, title (the title property; file name without .md when missing or not text), tags (the tags property; vault_update_properties edits it), body_tags (#tags in the note's text, one per spelling; vault_replace_in_note edits them; absent when none), related ([] when missing), folder (top-level, "" at the root), type (null when missing or not text), created (null when missing or not an ISO date), modified (ISO 8601 file time), bytes (on-disk size), and when present leading_callout (the opening callout, { type, title, body }) and additional_properties (every property but title, type, created, related and tags). Sorted by most recently modified. |
| vault_get_memoryA | Read semantic memory from About Me/ files. Each file holds dated bullet entries under H2 headings. With file: that file. With file+section: just that H2 section's entries. No args: every file, joined by --- lines — can be large. With file+on_or_after: only the entries dated on or after that date, in document order (newest first by default), from one H2 section when section is given and otherwise from every section. Example: vault_get_memory({ file: "Principles" }) — the whole file Example: vault_get_memory({ file: "Principles", section: "Decision heuristics (newest first)" }) Example: vault_get_memory({ file: "Opinions", section: "Code patterns", on_or_after: "2026-09-01" }) Example: vault_get_memory({ file: "Opinions", on_or_after: "2026-09-01" }) — every section's entries since the date When to use: Reading user preferences, principles, opinions, or other persistent context stored in About Me/ files. Call vault_list_memory_files first to discover valid file and section names. Prefer vault_read_note for reading non-memory notes. To find entries by topic across every file, use vault_memory_recall. Errors:
Returns: Without on_or_after, raw markdown with the properties block stripped, or an empty string when no memory files exist yet. With on_or_after, JSON { entries, total, on_or_after } where each entry is { file, section, date, text } — text is the raw entry markdown (bullet + continuation lines, wikilinks intact). An empty match returns { entries: [], total: 0, on_or_after }. |
| vault_list_memory_filesA | Discovery tool — lists About Me/ memory files with each file's heading outline, per-section entry counts, and leading callout. Does NOT return actual entries. Example: vault_list_memory_files() When to use: Discovering what memory files and sections exist — and what each file is for — BEFORE calling vault_get_memory, vault_update_memory, or vault_delete_memory, and each file's entry_policy to check before pruning entries. To find entries by topic instead, use vault_memory_recall. Behavior: Lists every .md file directly inside About Me/, sorted by file name. Subfolders are not read, and hidden (dot-prefixed) files are skipped. Errors:
Returns: JSON array of file outlines, each { file, title, bytes, entry_policy, leading_callout, headings } — file is the name the other memory tools take as file (no .md); title is the title property, or file when there is none; bytes is the on-disk file size; headings lists H1 and H2 headings in order, each { level, text }, with entry_count on H2s; leading_callout is the top-of-file callout ({ type, title, body }; by convention a "Scope of this file" block describing what belongs in the file), or null; entry_policy is "append-only" (the default: by convention, entries are never edited or deleted) or "living" (a current-state file whose expired entries may be pruned; declared by an |
| vault_memory_recallA | Recall memory entries about a topic — entry-granular hybrid (keyword + semantic) retrieval across ALL About Me/ files and ALL time. Returns the relevant dated entries, up to limit, sorted oldest-first — semantic matching finds early entries even when their phrasing differs from the query. Tuned for recall over precision: expect some marginal entries. Content-word queries ("testing philosophy", "sustainable pacing") rank best. A meta-framed query ("opinions on testing") that scores below the relevance threshold degrades to any-term keyword matching instead of returning nothing. Example: vault_memory_recall({ query: "working hours and pacing" }) Example: vault_memory_recall({ query: "testing philosophy", file: "Opinions" }) When to use: Answering "what does my memory say about X?" or "how has my view on Y evolved?" — topic-based recall across memory files. Prefer vault_get_memory to read a known file or section verbatim; prefer vault_search for notes outside the memory layer. Behavior: A memory file change takes a few seconds to appear here. Errors:
Returns: JSON { entries, total, truncated, search_mode, reranked }. Each entry is { file, section, date, text } — text is the raw entry markdown (wikilinks intact, continuation lines included); file and section feed directly into vault_get_memory or vault_delete_memory. The total counts every matched entry; truncated=true means limit dropped the least-relevant matches, whatever their dates, so raise limit or narrow the query for the complete set. With file, limit keeps the most relevant matches from that file. search_mode is "hybrid" when semantic matching contributed and "fts" when keyword matching alone did, the any-term fallback included; reranked is true when the reranker ran. |
| vault_update_memoryA | Append a dated entry to a section of a memory file in About Me/. An exact duplicate (same date + text in the same section) is a no-op, so retrying a timed-out call is safe. Memory files are append-only by default: when a preference changes, append the new state (newest wins) rather than deleting the old one. A file may declare Example: vault_update_memory({ file: "Opinions", section: "Code patterns (newest first)", entry: "Prefer immutable data structures" }) Example: vault_update_memory({ file: "Routines", section: "Daily/weekly rhythm (newest first)", entry: "Gym Mon/Wed at 6am", options: { date: "2026-10-01", position: "bottom" } }) — a backdated entry placed below the existing ones When to use: Recording a new preference, principle, opinion, or fact about the user. Call vault_list_memory_files first and reuse existing file and section names so entries stay grouped. Prefer vault_write_note for creating non-memory notes. A missing file or section is created automatically. Parameters:
Obsidian syntax: Entry text is Obsidian Flavored Markdown. Watch for: #word = tag, [[ = wikilink. Escape with # or backticks when unintentional. Errors:
Returns: "Added entry to About Me/.md → ## ", plus a note to fill in the placeholder scope callout when the file is new; an exact duplicate returns "Entry already exists in About Me/.md → ## — nothing was written." |
| vault_delete_memoryA | Delete a single dated entry from a memory file in About Me/. Example: vault_delete_memory({ file: "Opinions", section: "Tools and workflows (newest first)", date: "2026-05-01", entry: "Prefer X over Y" }) Example: vault_delete_memory({ file: "Routines", section: "Daily/weekly rhythm (newest first)", date: "2026-03-10", entry: "Gym Tue/Thu at 7am" }) — prune an expired entry from an entry-policy: living file When to use: Removing an entry that was wrong when it was written. Memory files are append-only by default, so do NOT delete to reflect a change: append the new state via vault_update_memory instead. The exception is a file whose properties declare Parameters:
Errors:
Returns: "Deleted entry from About Me/.md → ## ". |
| vault_get_daily_noteA | Read a daily note by date, using the vault's configured Daily Notes folder and filename date format. Example: vault_get_daily_note({ date: "2026-05-13" }) Example: vault_get_daily_note({}) — returns today's daily note When to use: When you need today's or a specific date's daily note. To append content to a daily note section, use the returned path with vault_patch_note. For recent vault activity, vault_recent_notes lists the most recently changed notes; it has no date filter. Parameters:
Errors:
Returns: JSON with path (string — resolved vault-relative path), content (string|null — the note's full text, properties block included, or null when the note doesn't exist), and exists (boolean). When exists is false, create the note with vault_write_note using the returned path. |
| vault_list_tasksA | List checkbox tasks across the whole vault with structured filters — the Tasks-plugin data model over MCP. Both task metadata formats are indexed: emoji signifiers (📅 due, ⏫ priority, 🔁 recurrence, ...) and Dataview inline fields ([due:: 2026-07-04], [priority:: high], ...). Task lines inside fenced code blocks and %% %% comment blocks are not indexed. Checkboxes typed NON_TASK in the Tasks plugin's status settings are also excluded. Example: vault_list_tasks({ due: { before: "2026-07-04" } }) — overdue triage; the default status (not_done) and sort (due ascending) make this the "what's overdue?" call Example: vault_list_tasks({ path: "Code Projects/vault-cortex/TASKS.md", heading: ["Active", "Up Next", "Waiting On"], sort_by: "position" }) — actionable Kanban lanes in board order Example: vault_list_tasks({ folder: "Code Projects/vault-cortex" }) — all open tasks across a project tree (TASKS.md + task-notes/ subdirectories) Example: vault_list_tasks({ status: "done", done: { after: "2026-06-26" } }) — what got completed this week When to use: Reading task status and order on one board (path + sort_by: "position"; add status: "all" to include done and cancelled cards). Also any vault-wide task triage question — "what's overdue?", "what's open per project?", "what did I finish this week?" — in one call instead of per-board reads. Prefer vault_read_note (heading mode) only when you need a lane's verbatim Markdown or a task's state right after a write. Prefer vault_search for full-text queries over note content. Behavior: A file change takes a few seconds to appear here. Parameters:
Errors:
Returns: JSON { total, tasks }. Every task carries path, line, status, status_char (the checkbox character, such as "x" or "/"), description, folder (the task note's parent folder), depth (0 for top-level, 1+ for sub-tasks), is_kanban_task (true when the task's note has kanban-plugin in its properties), depends_on, and tags (the arrays are [] when empty). Every other field appears only when the task has it: heading (nearest heading above the task), created/scheduled/start/due/done/cancelled dates, priority, recurrence, on_completion, task_id, block_id, parent_block_id (sub-tasks whose parent carries a ^block-id), done_lanes (Kanban boards only: the headings of the lanes whose first line is Complete), and subtask_progress ({ done, total } over the task's DIRECT checklist children; done counts status "done" only, a cancelled child counts toward total, and the counts ignore the query's filters). |
| vault_create_taskA | Create a correctly-formatted task in one call — description, target heading, dates, priority, block_id, and optional checklist sub-items. The task is always created as todo, with its created date (➕) set to today; set in_progress later with vault_update_task. Override format only at the user's request. A retried call with no block_id (a Kanban sub-task) creates it again, so after a timeout or lost response, re-read the note before retrying. Example: vault_create_task({ path: "TASKS.md", description: "Fix login bug", block_id: "fix-login", heading: "Active", priority: "high", due: "2026-09-15" }) — a card in a Kanban board's Active lane Example: vault_create_task({ path: "TASKS.md", description: "Sub-bug", parent_block_id: "fix-login", due: "2026-09-01" }) — full sub-task under fix-login Example: vault_create_task({ path: "Projects/plan.md", description: "Quick fix", block_id: "quick-fix", parent_line: 42 }) — sub-task in a regular note under a parent identified by line number Example: vault_create_task({ path: "TASKS.md", description: "Write release notes", block_id: "release-notes", heading: "Active", position: 3 }) — insert as the 3rd card in the lane When to use: Creating a new task card on a board or in a note. For lightweight checklist items under an existing card (no metadata), use vault_update_task's add_subtasks param instead. Parameters:
Errors: (messages use camelCase names, e.g. blockId for block_id)
Obsidian syntax: When description or subtasks text ends in a field the plugin's parser recognizes (an emoji field like "🔁 every week", or a Dataview [key:: value] field), with only other recognized fields after it, that text is read back as metadata. The write still succeeds; reword the text to clear the advisories the result carries. Returns: JSON { path, line, description, block_id, heading, subtasks, changes, advisories } — line is the new task's 1-based line number in the file; block_id is omitted when the task has none (line is then its handle); heading is the nearest heading above the new task (omitted when the note has none); subtasks lists each checklist item written as { line, description } (omitted when none); changes lists every field written as "field: (none) → value", and checklist items as "subtasks: 0 → N"; advisories (omitted when the line round-trips clean) lists one sentence per place the stored line parses back differently than submitted — see Obsidian syntax above. |
| vault_update_taskA | Update a task's status, priority, description, dates, recurrence, completion action, dependencies, block_id, checklist items, heading, or position within a lane, in any combination; every field passed is written in a single edit. A repeated call with add_subtasks adds the items again, and a repeated "done" or "cancelled" moves its date stamp to the retry day, so after a timeout or lost response, re-read the task before retrying. Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", status: "done" }) — complete a task; on a Kanban board, auto-moves to the done lane Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", recurrence: "every week" }) — make a task recurring Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", heading: "Done" }) — move a task to a different heading (the checkbox is unchanged) Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", description: "Updated task name", due: "2026-10-01", scheduled: null }) — change the description, set one date, and clear another Example: vault_update_task({ path: "TASKS.md", block_id: "my-task", status: "in_progress", add_subtasks: ["Design", "Implement", "Test"] }) — start working and add checklist stages Example: vault_update_task({ path: "TASKS.md", line: 42, assign_block_id: "my-task" }) — add a block_id to a task that lacks one When to use: Any change to an existing task. Use vault_list_tasks first to get identification fields (path + block_id or line). For creating a new task, use vault_create_task instead. Parameters:
Errors: (messages use camelCase names, e.g. assignBlockId for assign_block_id)
Obsidian syntax: When description or add_subtasks text ends in a field the plugin's parser recognizes (an emoji field like "🔁 every week", or a Dataview [key:: value] field), with only other recognized fields after it, that text is read back as metadata. The write still succeeds; reword the text to clear the advisories the result carries. The ✅/❌ dates a status change adds or removes produce no advisories. Returns: JSON { path, line, description, block_id, heading, subtasks, next_occurrence, changes, advisories, on_completion_applied }.
|
| vault_read_fileA | Read a non-markdown vault file in its most useful form per type — the read-side companion to vault_read_note for everything that isn't a note. Example: vault_read_file({ path: "attachments/diagram.png" }) — the image itself, shrunk when too large Example: vault_read_file({ path: "Boards/Roadmap.canvas" }) — a readable outline of the canvas Example: vault_read_file({ path: "exports/data.json" }) — the file content as text Example: vault_read_file({ path: "exports/big.csv", limit: 500 }) — the first 500 lines Example: vault_read_file({ path: "papers/research.pdf" }) — structured text with title, headings, and links When to use: whenever a note references a file you need to actually see or read — an embedded diagram, a linked canvas, data file, or PDF. Find the files a note links to (with byte sizes) via vault_get_outgoing_links. Browse a folder's files via vault_list_files. vault_search also indexes canvas, PDF, and text-format content, but not images or other files. For .md notes use vault_read_note. To check a large file's line count before reading it whole, request start_line: 1 with limit: 1 — one line plus the total. Parameters:
Behavior: A paged window comes back with \n line endings and no trailing newline, preceded by a metadata line stating the window, the total line count, and where to continue ("data.csv — lines 51–100 of 400 (continue with start_line: 101)"); an unpaged read of a text format returns the file byte-exact. Errors:
Returns: by file type:
|
| vault_list_filesA | List non-markdown files in the vault or a folder — images, canvases, PDFs, data files — with per-file byte sizes and per-extension counts. Example: vault_list_files({}) — non-markdown files across the whole vault Example: vault_list_files({ folder: "attachments" }) Example: vault_list_files({ extensions: [".png", ".jpg"], limit: 20 }) When to use: discovering what files exist before reading them with vault_read_file. vault_list_notes and vault_search_by_folder cover only markdown notes, and beyond notes vault_search indexes only canvas, PDF, and text-format files, so this is the discovery surface for everything else. For the files one specific note links to, prefer vault_get_outgoing_links. Behavior: Use the folder's exact letter case; a different case can find nothing. Hidden (dot-prefixed) files and folders, .obsidian/ among them, never appear. Errors:
Returns: JSON with files (array of { path, extension, bytes }, sorted by path; extension is "(none)" for a file without one), extension_counts (counts keyed by extension, e.g. { ".png": 3 }, over the full filtered set), total (full filtered count), and truncated (true when total exceeds limit). Readable types: vault_read_file reads only images (.png/.jpg/.jpeg/.gif/.webp), .canvas, .pdf, and the text formats .svg/.json/.txt/.csv/.xml/.log/.yaml/.yml/.base. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| vault-orientation | Survey this vault's structure and health — stats, folders, tags, properties (with adoption rates), orphans, recent notes, and the About Me/ memory layer. |
| memory-review | Reflect on the About Me/ memory layer — review its structure and scopes, read dated entries as a timeline, surface scope-fit issues and coverage gaps, and propose append-only updates. Never prunes entries for being old, except expired entries in files marked entry-policy: living. |
| daily-review | Reconcile a day — daily note, vault-wide task status (due/overdue, scheduled), modified notes, and links — surface what happened, what's open, and durable facts worth saving to About Me/ memory. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 33 tools
Most tools are documented with explicit cross-references and 'prefer X' guidance, but the set contains several closely overlapping note-edit tools (vault_patch_note, vault_replace_in_note, vault_replace_span, vault_insert_at_anchor, vault_delete_span, vault_write_note) whose boundaries an agent must reason through carefully. The search/discovery family also overlaps heavily: vault_search, vault_search_by_tag, vault_search_by_folder, vault_search_by_property, vault_list_notes, vault_recent_notes, and vault_list_files each have justified niches, but choosing among them is not effortless.
Almost all tools use a predictable vault_verb_noun snake_case pattern, with clear names like vault_list_notes, vault_read_note, vault_create_task, and vault_update_properties. Minor deviations such as vault_recent_notes, vault_memory_recall, and the search_by_* family are still readable and consistent enough not to cause confusion.
At 33 tools, the surface is large even for a full-featured Obsidian server spanning notes, search, links, files, memory, tasks, and daily notes. While most tools have domain justification, the total is heavy and includes multiple near-duplicate edit and discovery tools that make the set harder to navigate than necessary.
Core vault workflows are well covered: note CRUD, section edits, property updates, search, tags, backlinks, files, memory, tasks, and daily notes. Minor gaps remain around non-markdown file write/delete, folder management, and broader attachment operations, but agents can work around most of these.