@axon/obsidian
Obsidian vault integration. Read, write, search, and surgically patch notes — your agent gets full access to your vault without clobbering structure.
Prerequisites
Install the Obsidian Local REST API plugin in Obsidian, then enable it and copy the API key from Settings → Local REST API.
Install
axon install @axon/obsidianaxon prepareAdd to your agent's .env:
OBSIDIAN_API_KEY=your-api-key-here# Optional — defaults to http://127.0.0.1:27123# Use https://127.0.0.1:27124 if HTTPS is enabled in the pluginOBSIDIAN_BASE_URL=http://127.0.0.1:27123Tools
obsidian.read(path)
Read a note with full metadata: content, tags, frontmatter, links, and backlinks.
const note = await obsidian.read("Projects/my-note.md")// → { path, content, tags, frontmatter, links, backlinks, stat }Returns: NoteMeta
obsidian.content(path)
Read just the markdown text of a note. Cheaper than read() when you don't need metadata.
const md = await obsidian.content("Projects/my-note.md")Returns: string
obsidian.write(path, content)
Create or overwrite a note. Creates parent directories automatically.
await obsidian.write("Projects/my-note.md", "# My Note\n\nHello.")obsidian.append(path, content)
Append content to the end of a note. Creates the note if it doesn't exist.
await obsidian.append("Daily/2026-06-25.md", "\n## Evening\n\nWrapped up the module.")obsidian.delete(path)
Delete a note.
await obsidian.delete("Drafts/scratch.md")obsidian.patch(path, opts)
Surgically edit a specific section without touching the rest of the note. Use this instead of write() whenever the note has existing structure worth preserving.
// Append a task under the "## TODO" headingawait obsidian.patch("Projects/my-note.md", { targetType: "heading", target: "TODO", operation: "append", content: "- [ ] Review PR",})// Update a frontmatter fieldawait obsidian.patch("Projects/my-note.md", { targetType: "frontmatter", target: "status", operation: "replace", content: "done",})// Create the heading if it doesn't exist yetawait obsidian.patch("Projects/my-note.md", { targetType: "heading", target: "Notes", operation: "append", content: "First entry.", createIfMissing: true,})Options:
| Option | Type | Description |
|---|---|---|
targetType |
"heading" | "frontmatter" | "block" |
What to target |
target |
string |
Heading text, frontmatter key, or block ref (e.g. ^ref1) |
operation |
"append" | "prepend" | "replace" |
What to do |
content |
string |
Content to apply |
createIfMissing |
boolean |
Create target if absent (default: false) |
obsidian.list(dir?)
List files and directories at a vault path. Omit dir to list the vault root. Directories end with /.
const root = await obsidian.list()// → ["Daily/", "Projects/", "Archive/", "index.md"]const files = await obsidian.list("Projects")// → ["Projects/my-note.md", "Projects/archive/"]Returns: string[]
obsidian.search(query, limit?)
Full-text search across all notes. Returns scored results with surrounding context snippets.
const results = await obsidian.search("attention mechanism transformer")// → [{ filename, score, matches: [{ context: "...surrounding text..." }] }]Returns: SearchResult[]
obsidian.find(logic)
Structured search using JsonLogic against vault metadata. More powerful than search() when you need to filter by frontmatter, tags, or links.
Available fields: path, content, frontmatter, tags, stat, links, backlinks.
Custom operators: glob(pattern, field), regexp(pattern, field).
// Notes with status "active" in the Projects folderconst notes = await obsidian.find({ "and": [ { "==": [{ "var": "frontmatter.status" }, "active"] }, { "glob": ["Projects/*", { "var": "path" }] }, ]})// Notes tagged #meetingconst notes = await obsidian.find({ "in": ["meeting", { "var": "tags" }]})// Notes that link to a specific fileconst notes = await obsidian.find({ "in": ["Projects/my-note.md", { "var": "links" }]})Returns: NoteMeta[]
obsidian.tags()
List all tags in the vault with usage counts.
const tags = await obsidian.tags()// → [{ name: "project/active", count: 5 }, { name: "todo", count: 12 }]Returns: TagCount[]
obsidian.periodic(period)
Get the current periodic note (daily, weekly, monthly, quarterly, or yearly). Requires the Periodic Notes plugin.
const today = await obsidian.periodic("daily")// → { path: "Daily/2026-06-25.md", content: "...", ... }Returns: NoteMeta
obsidian.appendPeriodic(period, content)
Append content to the current periodic note. Creates it if it doesn't exist.
await obsidian.appendPeriodic("daily", "\n## Notes\n\nBuilt the obsidian module.")The NoteMeta type
type NoteMeta = { path: string // vault-relative path content: string // full markdown content tags: string[] // e.g. ["project/active", "todo"] frontmatter: Record<string, unknown> // parsed YAML frontmatter stat: { ctime: number; mtime: number; size: number } links: string[] // paths this note links to backlinks: string[] // paths that link to this note}Notes
- All paths are vault-relative:
"Projects/my-note.md", not absolute filesystem paths. - The plugin must be running (Obsidian must be open) for any tool to work.
- HTTPS (
https://127.0.0.1:27124) uses a self-signed certificate. If you get TLS errors, switch to HTTP (http://127.0.0.1:27123) by settingOBSIDIAN_BASE_URL. periodic()andappendPeriodic()require the Periodic Notes plugin.