Obsidian Skill
Routing Policy
Use the backend that best matches user intent:
MCP (default for vault data operations)
- Read/write/patch/move/search notes
- Frontmatter and tag updates
- Metadata and batch note operations
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
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.
Gotchas
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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
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)"
- "No remote configured. Set up GitHub remote now via gh if available, or provide remote URL? (Recommended: Set up via gh)"
- "Local and remote diverged. Try
git pull --rebase now? (Recommended: Yes)"
Safe sync sequence (never force push by default):
git add -A
git commit -m "vault sync: YYYY-MM-DD HH:mm" (skip commit if no changes)
git pull --rebase
git push
gh is optional:
- Use
gh only for remote bootstrapping (create repo / set origin) when requested.
- Do not require
gh for normal sync once remote is configured.
Stop on conflicts and report clear next steps.
- Do not auto-resolve merge conflicts silently.
- Explain what failed and what user should run next.
Obsidian CLI Mode
When the user asks for app-context operations (active file, open in editor, daily notes with templates, backlinks), use the Obsidian CLI directly via shell commands.
Run a preflight before first CLI use:
Resolve the CLI binary using the first match from these candidates:
| Priority |
macOS |
Linux |
Windows |
| 1 |
obsidian (PATH) |
obsidian (PATH) |
obsidian.exe or Obsidian.com (PATH) |
| 2 |
/Applications/Obsidian.app/Contents/MacOS/obsidian-cli |
— |
— |
| 3 |
/Applications/Obsidian.app/Contents/MacOS/Obsidian |
— |
— |
Obsidian 1.12.7+ installer bundles a dedicated obsidian-cli binary (10x
faster than the legacy Electron-based CLI: ~25ms vs ~250ms per call). On macOS,
after installing the 1.12.7+ installer, disable then re-enable the CLI in
Settings > General > Advanced to update PATH registration. This replaces the old
`/.zprofilePATH entry with a/usr/local/bin/obsidiansymlink pointing toobsidian-cli`.
On Linux, PATH registration creates a symlink at /usr/local/bin/obsidian
(or ~/.local/bin/obsidian as fallback). On Windows, the installer places an
Obsidian.com terminal redirector alongside Obsidian.exe.
Note: The priority table and stale PATH check are verified on macOS only.
Linux and Windows may also bundle obsidian-cli with the 1.12.7+ installer,
but this has not been confirmed. Contributions welcome via issue or PR.
Stale PATH check (macOS): If priority 1 resolved obsidian on PATH, check
whether it points to the fast binary or the slow Electron launcher:
| Resolved path |
Meaning |
Action |
/usr/local/bin/obsidian → obsidian-cli |
1.12.7 symlink registration |
None — fast binary |
/Applications/.../MacOS/obsidian |
Old ~/.zprofile entry (pre-1.12.7 registration or 1.12.7 installer without re-registering) |
Check if obsidian-cli exists in the bundle |
If obsidian resolves to the MacOS directory (not /usr/local/bin) AND
/Applications/Obsidian.app/Contents/MacOS/obsidian-cli exists, tell the user:
"Obsidian 1.12.7+ is installed but PATH still points to the slower Electron
binary. In Obsidian, go to Settings > General > Advanced and disable then
re-enable the CLI to update PATH registration."
Continue with whichever priority matched — this is advisory, not blocking.
Check Obsidian is running: pgrep -xiq obsidian (macOS/Linux) or tasklist /FI "IMAGENAME eq Obsidian.exe" /NH (Windows)
If either fails, tell the user and fall back to MCP tools + obsidian:// URIs
Vault targeting: obsidian vault="VaultName" <command>. The vault name is the folder basename unless OBSIDIAN_VAULT_NAME is set.
Key commands:
# Read the currently active file
obsidian read
# Read a specific file
obsidian read file="My Note"
# Open a file in Obsidian
obsidian open path="Notes/example.md"
# Open today's daily note
obsidian daily
# Append to daily note
obsidian daily:append content="- [ ] New task"
# Search (Obsidian's own search, different from MCP's BM25)
obsidian search query="meeting notes" limit=10
# List all tags with frequency
obsidian tags sort=count counts
# Get backlinks for a note
obsidian backlinks file="My Note"
# Find unresolved links
obsidian unresolved
Run obsidian help for the full command reference. The CLI evolves with Obsidian releases.
When to use CLI vs MCP:
- MCP for reads/writes/search/tags/frontmatter (sandboxed, validated, works headless)
- CLI for active file, daily notes with template expansion, backlinks, open in editor, plugin commands
- If unsure, prefer MCP
Resources
Load these only when needed, not on every invocation.
- Tool Patterns - read when you need a tool's response shape, mode details, or the move_note vs move_file decision
- Obsidian Conventions - read when creating/writing note content (link syntax, frontmatter fields, daily note format, template variables)
- Git Sync - read when user asks for backup/sync/store-vault workflows with git/gh
1---2name: obsidian3description: 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.4---5
6# Obsidian Skill
7
8## Routing Policy
9
10Use the backend that best matches user intent:
11
121. **MCP (default for vault data operations)**
13 - Read/write/patch/move/search notes
14 - Frontmatter and tag updates
15 - Metadata and batch note operations
16
172. **Obsidian CLI/App context (only when app context is needed)**
18 - Open a note in Obsidian from URI
19 - Trigger app/plugin workflows that MCP cannot perform
20
213. **CLI git (sync/backup workflows)**
22 - Initialize repo, configure remote, commit, pull, push
23 - Periodic or manual vault backup/sync requests
24
25When a request is ambiguous, pick MCP first unless the user explicitly asks for sync/backup/git/app behavior.
26
27## Gotchas
28
291. **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.
30
312. **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.
32
333. **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`.
34
354. **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`.
36
375. **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.
38
396. **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.
40
417. **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`.
42
438. **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.
44
459. **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.
46
4710. **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.
48
49## Error Recovery
50
51| Error | Next step |
52|-------|-----------|
53| patch_note "Found N occurrences" | Add surrounding lines to `oldString` to make it unique, or set `replaceAll: true` |
54| delete_note / move_file confirmation mismatch | Re-read the note path with `read_note` or `list_directory`, then retry with the exact string |
55| search_notes returns 0 results | Try single keywords instead of phrases, toggle `searchFrontmatter`, or broaden with partial terms |
56| read_multiple_notes partial `err` | Verify failed paths with `list_directory`, fix typos or missing extensions, retry only failed ones |
57
58## Git Sync Mode
59
60When the user asks to "sync", "backup", or "store my vault with git", use CLI git with this behavior:
61
621. Run a **preflight** before changing anything:
63 - `git` available
64 - current directory is a git repo (or prompt to initialize)
65 - `git config user.name` and `git config user.email` are set
66 - at least one remote exists for push/pull sync
67
682. If preflight is incomplete, ask exactly one targeted question with a recommended default.
69 - Use askuserquestion for decisions that materially change behavior.
70 - Good examples:
71 - "No git repo found. Initialize one in this vault now? (Recommended: Yes)"
72 - "No remote configured. Set up GitHub remote now via gh if available, or provide remote URL? (Recommended: Set up via gh)"
73 - "Local and remote diverged. Try `git pull --rebase` now? (Recommended: Yes)"
74
753. Safe sync sequence (never force push by default):
76 - `git add -A`
77 - `git commit -m "vault sync: YYYY-MM-DD HH:mm"` (skip commit if no changes)
78 - `git pull --rebase`
79 - `git push`
80
814. `gh` is optional:
82 - Use `gh` only for remote bootstrapping (create repo / set origin) when requested.
83 - Do not require `gh` for normal sync once remote is configured.
84
855. Stop on conflicts and report clear next steps.
86 - Do not auto-resolve merge conflicts silently.
87 - Explain what failed and what user should run next.
88
89## Obsidian CLI Mode
90
91When the user asks for app-context operations (active file, open in editor, daily notes with templates, backlinks), use the Obsidian CLI directly via shell commands.
92
931. Run a **preflight** before first CLI use:
94 - Resolve the CLI binary using the first match from these candidates:
95
96 | Priority | macOS | Linux | Windows |
97 |----------|-------|-------|---------|
98 | 1 | `obsidian` (PATH) | `obsidian` (PATH) | `obsidian.exe` or `Obsidian.com` (PATH) |
99 | 2 | `/Applications/Obsidian.app/Contents/MacOS/obsidian-cli` | — | — |
100 | 3 | `/Applications/Obsidian.app/Contents/MacOS/Obsidian` | — | — |
101
102 > **Obsidian 1.12.7+ installer** bundles a dedicated `obsidian-cli` binary (~10x
103 > faster than the legacy Electron-based CLI: ~25ms vs ~250ms per call). On macOS,
104 > after installing the 1.12.7+ installer, disable then re-enable the CLI in
105 > Settings > General > Advanced to update PATH registration. This replaces the old
106 > `~/.zprofile` PATH entry with a `/usr/local/bin/obsidian` symlink pointing to
107 > `obsidian-cli`.
108 >
109 > On Linux, PATH registration creates a symlink at `/usr/local/bin/obsidian`
110 > (or `~/.local/bin/obsidian` as fallback). On Windows, the installer places an
111 > `Obsidian.com` terminal redirector alongside `Obsidian.exe`.
112 >
113 > **Note:** The priority table and stale PATH check are verified on macOS only.
114 > Linux and Windows may also bundle `obsidian-cli` with the 1.12.7+ installer,
115 > but this has not been confirmed. Contributions welcome via issue or PR.
116
117 - **Stale PATH check (macOS):** If priority 1 resolved `obsidian` on PATH, check
118 whether it points to the fast binary or the slow Electron launcher:
119
120 | Resolved path | Meaning | Action |
121 |---------------|---------|--------|
122 | `/usr/local/bin/obsidian` → `obsidian-cli` | 1.12.7 symlink registration | None — fast binary |
123 | `/Applications/.../MacOS/obsidian` | Old `~/.zprofile` entry (pre-1.12.7 registration or 1.12.7 installer without re-registering) | Check if `obsidian-cli` exists in the bundle |
124
125 If `obsidian` resolves to the MacOS directory (not `/usr/local/bin`) AND
126 `/Applications/Obsidian.app/Contents/MacOS/obsidian-cli` exists, tell the user:
127 _"Obsidian 1.12.7+ is installed but PATH still points to the slower Electron
128 binary. In Obsidian, go to Settings > General > Advanced and disable then
129 re-enable the CLI to update PATH registration."_
130 Continue with whichever priority matched — this is advisory, not blocking.
131
132 - Check Obsidian is running: `pgrep -xiq obsidian` (macOS/Linux) or `tasklist /FI "IMAGENAME eq Obsidian.exe" /NH` (Windows)
133 - If either fails, tell the user and fall back to MCP tools + `obsidian://` URIs
134
1352. Vault targeting: `obsidian vault="VaultName" <command>`. The vault name is the folder basename unless `OBSIDIAN_VAULT_NAME` is set.
136
1373. Key commands:
138 ```bash
139 # Read the currently active file
140 obsidian read
141
142 # Read a specific file
143 obsidian read file="My Note"
144
145 # Open a file in Obsidian
146 obsidian open path="Notes/example.md"
147
148 # Open today's daily note
149 obsidian daily
150
151 # Append to daily note
152 obsidian daily:append content="- [ ] New task"
153
154 # Search (Obsidian's own search, different from MCP's BM25)
155 obsidian search query="meeting notes" limit=10
156
157 # List all tags with frequency
158 obsidian tags sort=count counts
159
160 # Get backlinks for a note
161 obsidian backlinks file="My Note"
162
163 # Find unresolved links
164 obsidian unresolved
165 ```
166
1674. Run `obsidian help` for the full command reference. The CLI evolves with Obsidian releases.
168
1695. **When to use CLI vs MCP:**
170 - MCP for reads/writes/search/tags/frontmatter (sandboxed, validated, works headless)
171 - CLI for active file, daily notes with template expansion, backlinks, open in editor, plugin commands
172 - If unsure, prefer MCP
173
174## Resources
175
176Load these only when needed, not on every invocation.
177
178- [Tool Patterns](resources/tool-patterns.md) - read when you need a tool's response shape, mode details, or the move_note vs move_file decision
179- [Obsidian Conventions](resources/obsidian-conventions.md) - read when creating/writing note content (link syntax, frontmatter fields, daily note format, template variables)
180- [Git Sync](resources/git-sync.md) - read when user asks for backup/sync/store-vault workflows with git/gh