/obsidian
Activate when the user mentions their Obsidian vault, notes, tags, frontmatter, daily notes, backup, or sync. Route operations across MCP, Obsidian CLI/app actions, and git sync with safe defaults.
$ npx -y skills add bitbonsai/mcp-obsidian --skill obsidian --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/obsidian
Context preview
The summary Claude sees to decide when to auto-load this skill.
Activate when the user mentions their Obsidian vault, notes, tags, frontmatter, daily notes, backup, or sync. Route operations across MCP, Obsidian CLI/app actions, and git sync with safe defaults.
SKILL.md
obsidian.SKILL.mdname: obsidian
description: >
Activate when the user mentions their Obsidian vault, notes, tags,
frontmatter, daily notes, backup, or sync. Route operations across MCP,
Obsidian CLI/app actions, and git sync with safe defaults.
metadata:
version: "2.2"
author: bitbonsai
Obsidian Skill
Routing Policy
Use the backend that best matches user intent:
1. **MCP (default for vault data operations)**
- Read/write/patch/search notes
- Move/rename notes with `move_note`, then explicitly repair backlinks
- Frontmatter and tag updates
- Metadata and batch note operations
2. **Obsidian CLI/App context (only when app context is needed)**
- Open a note in Obsidian from URI
- Trigger app/plugin workflows that MCP cannot perform
3. **CLI git (sync/backup workflows)**
- Initialize repo, configure remote, commit, pull, push
- Periodic or manual vault backup/sync requests
When a request is ambiguous, pick MCP first unless the user explicitly asks for sync/backup/git/app behavior.
Safe Note Rename Workflow
Use MCP `move_note` for every note move or rename, even when Obsidian is running. Do not invoke the Obsidian CLI `move` command automatically: delayed link rewrites can apply stale byte offsets to notes edited after the move began, silently corrupting unrelated content ([#176](https://github.com/bitbonsai/mcpvault/issues/176)). Reconsider CLI moves only after an upstream fix has been independently retested.
Backlink preservation is an explicit, verifiable second step:
1. Before moving, search for the old wikilink target using both its vault-relative path and filename without the extension. If Obsidian is running, its read-only `backlinks` command may supplement discovery, but it does not replace the MCP search. 2. Move the note with MCP `move_note`. 3. Read each referring note and patch only exact wikilink targets, including embeds and links with aliases or fragments. Preserve display text (`|alias`) and `#heading` / `#^block-id` suffixes while changing the target. 4. Search again for the old path and basename. Report any remaining references instead of claiming success. 5. `search_notes` returns at most 20 results. If a search reaches that cap, or the old basename is ambiguous, tell the user exhaustive backlink repair cannot be proven and ask before continuing with a broader scan.
Report the move and backlink repair separately: which note moved, how many referring notes changed, and any stale references that remain.
Gotchas
1. **patch_note rejects multi-match by default.** With `replaceAll: false`, if `oldString` appears more than once the call fails and returns `matchCount`. Set `replaceAll: true` only when you mean it, or add surrounding context to make the match unique.
2. **patch_note matches inside frontmatter.** The replacement runs against the full file including the YAML block. A generic string like `title:` will match frontmatter fields. Include enough context to target the right occurrence.
3. **patch_note forbids empty strings.** Both `oldString` and `newString` must be non-empty and non-whitespace. To delete text, use `newString` with a single space or restructure the note with `write_note`.
4. **search_notes returns minified JSON.** Fields are abbreviated: `p` (path), `t` (title), `ex` (excerpt), `mc` (matchCount), `ln` (lineNumber), `uri` (obsidianUri). Hard cap of 20 results regardless of `limit`.
5. **search_notes multi-word queries score terms individually AND as a phrase.** Each term is OR-matched, so a document matching any term appears in results. The full phrase gets an additional scoring boost.
6. **write_note auto-creates directories.** Parent folders are created recursively. In `append`/`prepend` mode, if the note doesn't exist it's created. Frontmatter is merged (new keys override) in append/prepend; replaced entirely in overwrite.
7. **delete_note requires exact path confirmation.** `confirmPath` must be character-identical to `path`. No normalization, no trailing-slash tolerance. Mismatch silently fails with `success: false`.
8. **move_file needs double confirmation.** Both `confirmOldPath` and `confirmNewPath` must exactly match their counterparts. Use `move_note` for markdown renames (text-aware, no confirmation needed); use `move_file` only for binary files or when you need binary-safe moves.
9. **manage_tags reads from two sources but writes to one.** `list` merges frontmatter tags + inline `#hashtags`. `add`/`remove` only modify the frontmatter `tags` array. Inline tags are never touched.
10. **read_multiple_notes never rejects.** Uses `allSettled` internally. Failed files appear in the `err` array; successful ones in `ok`. Always check both. Hard limit of 10 paths per call.
Error Recovery
| Error | Next step | |-------|-----------| | patch_note "Found N occurrences" | Add surrounding lines to `oldString` to make it unique, or set `replaceAll: true` | | delete_note / move_file confirmation mismatch | Re-read the note path with `read_note` or `list_directory`, then retry with the exact string | | search_notes returns 0 results | Try single keywords instead of phrases, toggle `searchFrontmatter`, or broaden with partial terms | | read_multiple_notes partial `err` | Verify failed paths with `list_directory`, fix typos or missing extensions, retry only failed ones |
Git Sync Mode
When the user asks to "sync", "backup", or "store my vault with git", use CLI git with this behavior:
1. Run a **preflight** before changing anything:
- `git` available
- current directory is a git repo (or prompt to initialize)
- `git config user.name` and `git config user.email` are set
- at least one remote exists for push/pull sync
2. If preflight is incomplete, ask exactly one targeted question with a recommended default.
- Use askuserquestion for decisions that materially change behavior.
- Good examples:
- "No git repo found. Initialize one in this vault now? (Recommended: Yes)"
- "
Read more
name: obsidian description: > Activate when the user mentions their Obsidian vault, notes, tags, frontmatter, daily notes, backup, or sync. Route operations across MCP, Obsidian CLI/app actions, and git sync with safe defaults. metadata: version: "2.2" author: bitbonsai
Obsidian Skill
Routing Policy
Use the backend that best matches user intent:
1. **MCP (default for vault data operations)**
- Read/write/patch/search notes
- Move/rename notes with `move_note`, then explicitly repair backlinks
- Frontmatter and tag updates
- Metadata and batch note operations
2. **Obsidian CLI/App context (only when app context is needed)**
- Open a note in Obsidian from URI
- Trigger app/plugin workflows that MCP cannot perform
3. **CLI git (sync/backup workflows)**
- Initialize repo, configure remote, commit, pull, push
- Periodic or manual vault backup/sync requests
When a request is ambiguous, pick MCP first unless the user explicitly asks for sync/backup/git/app behavior.
Safe Note Rename Workflow
Use MCP `move_note` for every note move or rename, even when Obsidian is running. Do not invoke the Obsidian CLI `move` command automatically: delayed link rewrites can apply stale byte offsets to notes edited after the move began, silently corrupting unrelated content ([#176](https://github.com/bitbonsai/mcpvault/issues/176)). Reconsider CLI moves only after an upstream fix has been independently retested.
Backlink preservation is an explicit, verifiable second step:
1. Before moving, search for the old wikilink target using both its vault-relative path and filename without the extension. If Obsidian is running, its read-only `backlinks` command may supplement discovery, but it does not replace the MCP search. 2. Move the note with MCP `move_note`. 3. Read each referring note and patch only exact wikilink targets, including embeds and links with aliases or fragments. Preserve display text (`|alias`) and `#heading` / `#^block-id` suffixes while changing the target. 4. Search again for the old path and basename. Report any remaining references instead of claiming success. 5. `search_notes` returns at most 20 results. If a search reaches that cap, or the old basename is ambiguous, tell the user exhaustive backlink repair cannot be proven and ask before continuing with a broader scan.
Report the move and backlink repair separately: which note moved, how many referring notes changed, and any stale references that remain.
Gotchas
1. **patch_note rejects multi-match by default.** With `replaceAll: false`, if `oldString` appears more than once the call fails and returns `matchCount`. Set `replaceAll: true` only when you mean it, or add surrounding context to make the match unique.
2. **patch_note matches inside frontmatter.** The replacement runs against the full file including the YAML block. A generic string like `title:` will match frontmatter fields. Include enough context to target the right occurrence.
3. **patch_note forbids empty strings.** Both `oldString` and `newString` must be non-empty and non-whitespace. To delete text, use `newString` with a single space or restructure the note with `write_note`.
4. **search_notes returns minified JSON.** Fields are abbreviated: `p` (path), `t` (title), `ex` (excerpt), `mc` (matchCount), `ln` (lineNumber), `uri` (obsidianUri). Hard cap of 20 results regardless of `limit`.
5. **search_notes multi-word queries score terms individually AND as a phrase.** Each term is OR-matched, so a document matching any term appears in results. The full phrase gets an additional scoring boost.
6. **write_note auto-creates directories.** Parent folders are created recursively. In `append`/`prepend` mode, if the note doesn't exist it's created. Frontmatter is merged (new keys override) in append/prepend; replaced entirely in overwrite.
7. **delete_note requires exact path confirmation.** `confirmPath` must be character-identical to `path`. No normalization, no trailing-slash tolerance. Mismatch silently fails with `success: false`.
8. **move_file needs double confirmation.** Both `confirmOldPath` and `confirmNewPath` must exactly match their counterparts. Use `move_note` for markdown renames (text-aware, no confirmation needed); use `move_file` only for binary files or when you need binary-safe moves.
9. **manage_tags reads from two sources but writes to one.** `list` merges frontmatter tags + inline `#hashtags`. `add`/`remove` only modify the frontmatter `tags` array. Inline tags are never touched.
10. **read_multiple_notes never rejects.** Uses `allSettled` internally. Failed files appear in the `err` array; successful ones in `ok`. Always check both. Hard limit of 10 paths per call.
Error Recovery
| Error | Next step | |-------|-----------| | patch_note "Found N occurrences" | Add surrounding lines to `oldString` to make it unique, or set `replaceAll: true` | | delete_note / move_file confirmation mismatch | Re-read the note path with `read_note` or `list_directory`, then retry with the exact string | | search_notes returns 0 results | Try single keywords instead of phrases, toggle `searchFrontmatter`, or broaden with partial terms | | read_multiple_notes partial `err` | Verify failed paths with `list_directory`, fix typos or missing extensions, retry only failed ones |
Git Sync Mode
When the user asks to "sync", "backup", or "store my vault with git", use CLI git with this behavior:
1. Run a **preflight** before changing anything:
- `git` available
- current directory is a git repo (or prompt to initialize)
- `git config user.name` and `git config user.email` are set
- at least one remote exists for push/pull sync
2. If preflight is incomplete, ask exactly one targeted question with a recommended default.
- Use askuserquestion for decisions that materially change behavior.
- Good examples:
- "No git repo found. Initialize one in this vault now? (Recommended: Yes)"
- "
A universal AI bridge for Obsidian vaults using the Model Context Protocol (MCP) standard. Connect any MCP-compatible AI assistant to your knowledge base - works with Claude, ChatGPT, and future AI tools.
Repo: bitbonsai/mcp-obsidian

