Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx skills add cyanheads/obsidian-mcp-server --skill obsidian-guidegit clone --depth 1 https://github.com/cyanheads/obsidian-mcp-serverWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/cyanheads/obsidian-mcp-server/obsidian-guide)<a href="https://agentmods.dev/skills/cyanheads/obsidian-mcp-server/obsidian-guide"><img src="https://agentmods.dev/badge/skills/cyanheads/obsidian-mcp-server/obsidian-guide/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/skills/cyanheads/obsidian-mcp-server/obsidian-guide"><img src="https://agentmods.dev/badge/skills/cyanheads/obsidian-mcp-server/obsidian-guide.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00101 | $0.02076 |
| Opus 5.5 | $0.00040 | $0.00830 |
| Sonnet 5.5 | $0.00020 | $0.00415 |
| Haiku 4.5 | $0.00010 | $0.00208 |
Grade A, and why
obsidian-guide scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 62 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Scope
One Obsidian vault, reached through the Local REST API plugin while Obsidian runs. Each note tool reads or edits one note per call. No tool moves or renames a note, and none rewrites links in other notes. The multi-note jobs are the workflows below.
Identifiers
- Note path:
hits[].filenamefrom obsidian_search_notes,entries[].pathfrom obsidian_list_notes, and thepathevery note tool returns. Writes and deletes match it exactly, letter case included. - Heading locator: the full
Parent::Childpaths in obsidian_get_note'sdocument-mapheadings, and thesectionTargeta section read or write returns. - Tag: no leading
#. A note's owntags(obsidian_get_notefull, the JSONLogictagsfield) hold its tags as written, letter case included. Parent tags show up only in tag counts.
Gates
- Pin
activeandperiodictargets after the first call. Each call resolves them again.activeis whichever file has focus in Obsidian at that moment, and aperiodictarget with nodateis the current period. For the rest of the task, address the note by thepaththe first call returned. - A missing periodic note can't be created through a
periodictarget. Every tool reads the target before acting, so a write to a period with no note also failsperiodic_not_found. Ask the user to create it in Obsidian. Or, if they agree, write it by path: read an earlier period's note (passdate) to learn the folder and filename pattern. A note written this way holds only what you send, without the user's periodic-note template. On Local REST API 5.0.2 and later without the periodic-notes extension, everyperiodictarget failsperiodic_unsupported, whether or not the note exists; find the note with obsidian_search_notes or obsidian_list_notes and address it by path. - Write a heading back with the
sectionTargetthe read returned, never the bare name you sent. A bare name resolves differently on each side. A read takes the first heading with that text in document order. A write takes the top-level heading with that text when there is one. ReadingNotesand then writingNotescan touch two different sections. - A heading section is its whole subtree. obsidian_get_note
format: "section"returns the heading line, its body, and every sub-section under it. A replace rewrites all of that, so send the sub-sections back or target the child heading instead. An append lands below the last sub-section. A prepend lands right under the heading line. Round-trip a read through obsidian_write_note withsection, which drops the leading heading line. obsidian_patch_notereplacekeeps a leading#heading line, and plugin v5.0 and later refuse that withheading_outside_section. - Page a search to the end before editing its hits. Without OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS, every page re-runs the search. The cursor is an offset into the new result, so edits that remove matches shift later pages and skip notes. Under a read scope, later pages come from the first page's hits for at most 5 minutes past the last page read. Collect every
filenamefirst, then edit. - A text hit doesn't promise a replace match. Text mode needs each token somewhere in the filename or body, in any case. obsidian_replace_in_note needs the exact string, case-sensitive by default, and searches the body only by default. Read each note's
perReplacement[].count:0is a success that wrote nothing.useRegexpatterns run without themflag, so^and$match the ends of the scoped text, not of a line. To anchor a line, use(?<=^|\n)and(?=\n|$). - Tags match exactly.
{"in": ["work", {"var": "tags"}]}misses notes tagged onlyWork, or onlywork/tasks, which obsidian_list_tags still counts underwork. To catch nested tags, use{"some": [{"var": "tags"}, {"regexp": ["^work(/|$)", {"var": ""}]}]}. obsidian_manage_tagsremoveis exact the same way, and its defaultlocation: "frontmatter"leaves inline#tags in the body. Passlocation: "both"and check the returnedtags. - A JSONLogic hit is a note whose tree returns something truthy, and that value is the hit's
result. A note whose tree returnsfalse,null,0,"",[], or{}is left out. Return the field you need and one search reads it from every match:{"if": [<test>, {"var": "tags"}, null]}gives each match's tags. An empty value still drops the note. - After
obsidian_timeoutorobsidian_unreachableon a write, assume it may have landed. One write call makes several requests under one shared budget (45 s at the default timeout): a read before the write, the write, and a read after. The error can arrive after the write applied, and the server never re-sends a whole-file append or a section write. Repeating is safe for obsidian_write_note, obsidian_patch_notereplace, obsidian_manage_frontmatter, and obsidian_manage_tags. On a retry,file_exists(a create),note_missing(a delete), andcontent_preexists(a section append or prepend sent withoutapplyIfContentPreexists) each mean the first attempt landed. Don't force acontent_preexiststhrough withapplyIfContentPreexists. Read the note before repeating a whole-file obsidian_append_to_note, or an obsidian_replace_in_note whosereplacestill contains itssearch. - Confirm before deleting. obsidian_delete_note deletes on the first call unless its description says it asks. Get the user's go-ahead on the exact path first.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 2d ago First seen · 62 lines · 101 tokens per session scan A fb3a81c3e3fc
obsidian-guide is a skill published in the GitHub repository cyanheads/obsidian-mcp-server (706 stars, last pushed 2d ago), licensed Apache-2.0. It adds 101 tokens to every session and 2,076 once invoked, about $0.0004 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-11-12.
obsidian-guide compared with skills from other repositories
| Mod | Type | Stars | Last push | Licence | Scan | Tokens | Works with |
|---|---|---|---|---|---|---|---|
| obsidian-guidecyanheads/obsidian-mcp-server | Skill | 706 | 2d ago | Apache-2.0 | A | 101 | Claude Code |
| pkm-session-endAdrianV101/obsidian-pkm-plugin | Skill | 15 | 9d ago | MIT | A | 49 | Claude Code |
| wiki-dashboardAr9av/obsidian-wiki | Skill | 3.7k | yesterday | MIT | A | 42 | Claude Code |
| wiki-switchAr9av/obsidian-wiki | Skill | 3.7k | yesterday | MIT | A | 42 | Claude Code |
| daily-updateAr9av/obsidian-wiki | Skill | 3.7k | yesterday | MIT | B | 51 | Claude Code |
| pkm-writeAdrianV101/obsidian-pkm-plugin | Skill | 15 | 9d ago | MIT | A | 54 | Claude Code |
| pkm-exploreAdrianV101/obsidian-pkm-plugin | Skill | 15 | 9d ago | MIT | A | 55 | Claude Code |