| vault.listA | List every Obsidian vault kObsidian knows about, merged and deduplicated across three sources: the operator's OBSIDIAN_VAULT_PATH (the default — always included), any OBSIDIAN_VAULT_=path env vars (explicit named vaults), and — when KOBSIDIAN_VAULT_DISCOVERY is on (the default) — the user's local Obsidian application registry at obsidian.json. Each item reports its source, isDefault, isActive, and exists so the LLM can flag stale or missing vaults. Pass refresh: true to force a fresh scan instead of using the 30s cache. Read-only. NOTE: the obsidian-app source is EXPERIMENTAL — it parses Obsidian's undocumented obsidian.json registry (stable since 1.0 but internal to Obsidian) and may silently stop returning results if Obsidian changes the format; the env-var sources are the documented, stable path. Examples: Example 1 — List vaults using the 30-second cache: Example 2 — Force a rescan (obsidian.json changed, new env vars added): |
| vault.currentA | Return the vault that filesystem tools (notes., tags., dataview., blocks., canvas., kanban., marp., templates., tasks., links., wiki., stats.vault) would resolve to right now, plus the full precedence chain so the LLM can explain to the user why that vault was picked. reason is session-selected (vault.select was called), env-default (fell back to OBSIDIAN_VAULT_PATH), or none (nothing configured — tools will fail until vault.select or an env var is set). When OBSIDIAN_API_URL is configured, the response also carries an obsidianLiveInstance note reminding the caller that workspace. and commands.* tools target whichever vault the live Obsidian process has open, NOT the filesystem vault selected here. Read-only. |
| vault.selectA | Set the session-active vault for subsequent filesystem tool calls. Identify the target by EXACTLY ONE of id (stable id from vault.list), name (case-insensitive match), or path (absolute directory path — need not appear in vault.list; lets the LLM point at a fresh/empty vault to initialise). Precedence chain becomes: per-call vaultPath argument (highest) → this session selection → OBSIDIAN_VAULT_PATH → error. Explicit vaultPath arguments on individual tool calls always override this selection. Respects KOBSIDIAN_VAULT_ALLOW / KOBSIDIAN_VAULT_DENY operator gating (though OBSIDIAN_VAULT_PATH is never filtered). Does NOT change which vault the live Obsidian process has open — workspace.* and commands.* tools remain tied to OBSIDIAN_API_URL. HTTP deployments: this server shares the selection across HTTP clients, so concurrent multi-client HTTP setups should pass vaultPath per call instead. Examples: Example 1 — Switch to the vault named 'Work': Example 2 — Select by id from vault.list: {
"id": "58f115bd2c2febd2"
}
Example 3 — Point at an ad-hoc path (e.g. a fresh vault to initialise): {
"path": "/Users/alice/FreshVault"
}
|
| vault.resetA | Clear the session-selected vault so the precedence chain falls back to OBSIDIAN_VAULT_PATH. Use this to signal 'I'm done with the scratch vault, go back to the default'. Idempotent — running on an already-cleared session is a no-op that reports changed: false. Does not change per-call vaultPath behaviour. |
| notes.readA | Read a note and return any combination of its body, parsed frontmatter metadata, and lightweight statistics. include selects which sections to return — default is ['content', 'metadata']. Ask for ['stats'] alone when you only need word/character/heading/link/task counts and want to skip loading the full body. Read-only. Fails with not_found when path does not exist. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| notes.createA | Create a new note or folder in the vault. kind:'note' creates a markdown note at path with the given content; ifExists controls collision behavior (error = fail, default; replace = overwrite; skip = no-op). kind:'folder' creates a directory at path (intermediate folders are created automatically; idempotent — re-creating an existing folder is a no-op). Returns the standard mutation envelope. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Create a new note, failing if it exists: {
"kind": "note",
"path": "Journal/2026-04-24.md",
"content": "# Today\n"
}
Example 2 — Ensure a folder exists (idempotent): {
"kind": "folder",
"path": "Projects/Alpha/Reports"
}
|
| notes.editA | Mutate the body of an existing note. The mode field selects how content is applied: replace overwrites the whole note; append adds to the end; prepend adds after the frontmatter (or at the top if none); after-heading inserts after the first heading whose text matches anchor (no leading #); after-block inserts after the block reference ^anchor. Fails if the note does not exist — use notes.create first. replace mode is idempotent-destructive; the others are additive. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Append a new journal entry to the end of today's note: {
"mode": "append",
"path": "Journal/2026-04-24.md",
"content": "\n## Afternoon\n\nFinished the tool consolidation."
}
Example 2 — Insert content after a specific heading: {
"mode": "after-heading",
"path": "Projects/Alpha.md",
"anchor": "Open questions",
"content": "- Do we need to bump the Zod major?\n"
}
Example 3 — Insert after a block reference: {
"mode": "after-block",
"path": "Notes/idea.md",
"anchor": "idea-1",
"content": "Follow-up thought …"
}
|
| notes.frontmatterA | Set or unset fields in a note's YAML frontmatter. set is a map of {field: value} pairs to write; unset is a list of field names to delete. strategy:'merge' (default) leaves unspecified fields untouched; strategy:'replace' overwrites the entire frontmatter block with set (any field not in set is dropped). At least one of set or unset is required. Idempotent — re-running with the same arguments converges on the same frontmatter state. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Set two fields, merging with existing frontmatter: {
"path": "Projects/Alpha.md",
"set": {
"status": "in-progress",
"owner": "behzat"
}
}
Example 2 — Remove a field: {
"path": "Projects/Alpha.md",
"unset": [
"draft"
]
}
|
| notes.deleteA | Delete a note from the vault. Destructive — the file is removed from disk. Fails with not_found when the path does not exist. There is no undo; use with care. For folders, call notes.move to an archive location instead (folder deletion is not exposed as a tool to avoid accidental cascading deletes). Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| notes.moveA | Move a note or folder to a new path. kind:'note' moves a single .md file; kind:'folder' moves a directory and every note beneath it. When updateLinks:true (the default), wiki and markdown links elsewhere in the vault that reference the moved path are rewritten to point at the new location. Destructive — overwrites or replaces existing content at the destination. Fails when the source does not exist. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| notes.listA | List notes and/or folders in the vault, optionally scoped to a folder and filtered by creation/modification date. include selects what to return (notes, folders, or both). recursive:true descends into subfolders. since/until (ISO dates) combined with dateField (created or modified, default modified) narrow the result by date. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| notes.searchA | Full-text search across every note in the vault. The query supports plain text and lightweight prefix filters: tag:foo restricts to notes carrying #foo, and path:Journal/ restricts to notes under a folder. contextLength controls how many characters of surrounding context are returned per match (default 80). Read-only. For pure tag or date filtering, tags.search and notes.list are faster. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tags.modifyA | Mutate the frontmatter tags list of a single note. Four ops are supported: add unions the incoming tags with the existing list (duplicates dropped); remove drops any incoming tag currently present; replace overwrites the list entirely; merge is an alias for add. Leading # on incoming tags is stripped automatically. This tool only touches the frontmatter block — inline #tag occurrences in the body are left untouched. Idempotent: repeated calls with the same op and tags converge on the same result. Returns {changed, target, summary, op, tagsAfter}. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Add two tags to a note (idempotent): {
"path": "Projects/Alpha.md",
"op": "add",
"tags": [
"in-progress",
"priority/high"
]
}
Example 2 — Replace a note's entire tag set: {
"path": "Inbox/today.md",
"op": "replace",
"tags": [
"processed"
]
}
|
| tags.searchA | Find every note in the vault that contains a given tag, either in frontmatter tags or as an inline #tag in the body. Leading # on the query is stripped. For each hit, the result carries {file, absolutePath, tagLocations: {frontmatter, inline}} so callers can distinguish where the tag came from. Read-only. For analyzing tags of ONE specific note (not a vault-wide search), use tags.analyze instead. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tags.analyzeA | Return the tags present in a single note, split into frontmatterTags, inlineTags, and their de-duplicated union allTags. Use this when you have one note and want to know what tags it carries — contrast with tags.search, which scans the whole vault for one specific tag. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tags.listA | List every unique tag used across the vault (frontmatter and inline combined). With includeCounts: true, each item includes how many notes carry the tag; sortBy lets you sort by name or count (the latter requires counts). Read-only. For finding notes carrying a specific tag, use tags.search. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.backlinksA | Find every note that links TO a target note (inbound references). Supports both wiki-style [[Note]] and markdown-style [text](Note.md) links. When includeContext:true, each result carries a contextLength-char snippet of surrounding text so the agent can judge link intent without re-reading each source. Read-only. For outbound links (what a note points AT), use links.outgoing. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.outgoingA | Extract every link FROM a note (outbound references) — wiki-style [[…]] and markdown-style […](…). When checkValidity:true, each entry carries a valid flag indicating whether the target path resolves in the vault. Read-only. For inbound references (what points AT the note), use links.backlinks. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.brokenA | Find every link in the vault (or a directory subtree) whose target does not resolve to an existing note. Each result carries the source file, line number, link text, and unresolved target. Read-only. Pair with notes.move (with updateLinks:true) to fix them after moves. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.graphA | Build a full vault link graph: every note becomes a node, every outbound link becomes a directed edge. Return shape is {nodes, edges, stats} where nodes carry basic metadata (path, title) and edges carry source/target and link kind. Expensive for large vaults — prefer links.backlinks, links.outgoing, or links.connections for targeted queries. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.orphanedA | Return every note with zero incoming AND zero outgoing links — i.e., notes that are disconnected from the rest of the vault graph. Useful for cleanup passes. Read-only. Often paired with links.hubs and links.broken in a weekly vault-health routine. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.hubsA | Return notes with at least minOutlinks outgoing links (default 10) — the vault's connective tissue / MOCs / indexes. Each result carries the path, title, outbound-link count, and inbound-link count. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.healthA | Summarise link health for the whole vault: total link count, broken-link count and ratio, orphan-note count, average outbound/inbound link density, and a list of the top hub notes. Read-only. Use this as a dashboard check; call links.broken/links.orphaned/links.hubs for the full per-item lists. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| links.connectionsA | Explore the graph neighbourhood around a seed note — direct and multi-hop connections up to depth hops (default 2). Returns the set of reachable notes plus the paths that reach them. Higher depth values blow up result size quickly; keep it ≤3 unless you know the graph is sparse. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| stats.vaultA | Return aggregate statistics for the whole vault: total note count, total word count, total character count, total task count (open and completed), tag usage summary, and file size footprint. Read-only, scans every .md file. For per-note statistics use notes.read with include: ['stats']. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tasks.searchA | Scan the vault for Tasks-plugin-style markdown task lines (- [ ] / - [x]) and filter by status, priority, due date range, recurrence, or tag. Result items include the task text, source file, line number, status, and parsed metadata — enough to locate and further manipulate each task via tasks.toggle or tasks.updateMetadata. sortBy controls ordering; limit caps the result count. Read-only. For vault-wide counts without per-task detail, use tasks.stats. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tasks.createA | Append a new task line to a note. The task is written in Tasks-plugin format: - [ ] <content> {metadata emojis}. Optional metadata (priority, dueDate, scheduledDate, startDate, doneDate, createdDate, recurrence) is encoded as the plugin's convention emojis (🔺⏫📅⏳🛫✅➕🔁). Returns the standard mutation envelope with the 1-based lineNumber where the task was inserted. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Append a simple task with a due date: {
"filePath": "Tasks.md",
"content": "Write the v0.3.0 migration doc",
"dueDate": "2026-05-01"
}
Example 2 — Append a high-priority weekly recurring task: {
"filePath": "Tasks.md",
"content": "Weekly review",
"priority": "high",
"recurrence": "every week on Sunday"
}
|
| tasks.toggleA | Flip a task line between [ ] and [x] in place, identified by sourceFile and 1-based lineNumber. When marking a task done, a ✅ YYYY-MM-DD date is stamped into the line (default today; override with doneDate). Fails if the target line is not a task checkbox. Use tasks.search to find the right sourceFile/lineNumber pair. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tasks.updateMetadataA | Update a task's dates, priority, or recurrence expression in place without touching the task body text. Identified by sourceFile + 1-based lineNumber. Pass only the fields you want to change. Idempotent — re-running with identical inputs converges on the same line. Fails if the target line is not a task. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| tasks.statsA | Return aggregate task statistics for the whole vault: total tasks, incomplete count, completed count, overdue count (due date passed and still incomplete), upcoming counts by horizon (today/this-week/next-week), and per-priority breakdown. Read-only. Use tasks.search to get the individual task records. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.queryA | Execute an arbitrary Dataview Query Language (DQL) query through the Obsidian Local REST API. The query string is raw DQL — e.g. LIST FROM #inbox, TASK WHERE !completed, TABLE file.mtime FROM "Journal". Requires the Dataview plugin to be enabled in Obsidian and the Local REST API plugin to be configured (OBSIDIAN_API_URL/OBSIDIAN_REST_API_KEY). For common patterns (list-by-tag, list-by-folder, table) the sugar tools dataview.listByTag/listByFolder/table are easier to use — prefer those when applicable and fall back to dataview.query for custom DQL. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.listByTagA | Convenience wrapper that runs LIST FROM #tag (optionally with WHERE, SORT, and LIMIT clauses). Returns the same shape as dataview.query. Requires the Dataview and Local REST API plugins. Use this instead of authoring raw DQL when filtering by a single tag. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.listByFolderA | Convenience wrapper that runs LIST FROM "folder" (optionally with WHERE, SORT, and LIMIT clauses). Useful when you want every note under a vault folder. Requires the Dataview and Local REST API plugins. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.tableA | Convenience wrapper that runs TABLE field1, field2, … FROM … with optional WHERE, SORT, and LIMIT clauses. Use this when you need structured columnar output. Requires the Dataview and Local REST API plugins. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.indexA | Parse a single note and return everything Dataview would index from it: page-level metadata (title, aliases, tags, frontmatter fields), list-item fields, task-line fields, and both DQL and DataviewJS block locations. Read-only, runs locally (does NOT require the Local REST API). Use this to understand what Dataview sees in a note without running a query. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.fields.readA | Read Dataview fields from the vault. op:'extract' returns every field declared in a single note (page, list-item, and task-line fields combined). op:'search' scans the whole vault for notes whose fields match a key (and optionally a value coerced by valueType); use scope to restrict which field kinds are considered. Read-only. For mutating fields, use dataview.fields.write. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| dataview.fields.writeA | Insert or remove a Dataview field in a single note. op:'add' inserts a key:: value field; syntaxType picks the rendering (full-line = own line; bracket = [key:: value]; paren = (key:: value)); insertAt chooses placement (start, end, afterFrontmatter) unless lineNumber is given for precise control. op:'remove' deletes every occurrence of key (optionally restricted to a single lineNumber or a Dataview scope). Idempotent — re-running with the same args converges on the same document state. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Add a full-line priority field after the frontmatter: {
"op": "add",
"filePath": "Projects/Alpha.md",
"key": "priority",
"value": "high",
"syntaxType": "full-line",
"insertAt": "afterFrontmatter"
}
Example 2 — Remove every occurrence of the status field from a note: {
"op": "remove",
"filePath": "Projects/Alpha.md",
"key": "status",
"scope": "all"
}
|
| blocks.listA | List fenced code blocks of the supported knowledge-base languages (dataview, dataviewjs, mermaid) in a single note or across the vault. Use this to discover what DQL, DataviewJS, or Mermaid blocks exist before reading or updating them. Omit language to list blocks of all three types in one call. Vault-wide scanning is only supported for Mermaid; for Dataview languages a filePath is required. Returns {total, items} where each item carries at minimum {filePath, language, index, id?}. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| blocks.readA | Read one fenced block's source and language-specific metadata. Locate the block by blockId (preferred, stable) or index (0-based within the language group in the file; defaults to 0). language is required so the tool can dispatch to the correct parser and return the right metadata (Mermaid directives, Dataview DQL parts, etc.). Fails with not_found when no block matches the locator. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| blocks.updateA | Replace one fenced block's body source-preservingly — the surrounding fences, language tag, and neighbouring content are untouched. Locate the block by blockId or index. language acts as a guard: if the located block is not of the declared language, the update fails. source is the replacement body WITHOUT the surrounding ``` fences. Idempotent — re-running with identical inputs is a no-op on the file contents. Destructive — overwrites the previous block body in place. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Replace the first Mermaid diagram in a note: {
"filePath": "Diagrams/system-overview.md",
"language": "mermaid",
"index": 0,
"source": "flowchart TD\n A --> B"
}
Example 2 — Update a DQL query block by stable id: {
"filePath": "Dashboards/Inbox.md",
"language": "dataview",
"blockId": "inbox-open",
"source": "TASK\nFROM #inbox\nWHERE !completed"
}
|
| marp.readA | Read some or all of a Marp presentation deck (a markdown file with marp: true frontmatter and --- slide separators). The part field selects what to return: deck returns the whole deck (frontmatter, all slides, directives); slides returns a list of slide summaries (separator and directive metadata, no body); slide returns one slide's full source, located by slideId or 0-based index. Output shape varies by part — see the description of each variant. Read-only. Use marp.update to mutate. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| marp.updateA | Mutate a Marp deck in place. part:'slide' replaces one slide's body (located by slideId or index) without touching neighbouring slides. part:'frontmatter' merges fields into the deck's frontmatter — unspecified fields are preserved; pass null to a field to unset it. Idempotent — re-running with identical inputs is a no-op on the file contents. Destructive — overwrites in place. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Replace the second slide's body: {
"part": "slide",
"filePath": "Decks/launch.md",
"index": 1,
"source": "# New headline\n\nUpdated body"
}
Example 2 — Change the deck's theme and set a new title: {
"part": "frontmatter",
"filePath": "Decks/launch.md",
"fields": {
"theme": "gaia",
"title": "Launch plan"
}
}
|
| kanban.parseA | Parse a markdown Kanban board file into its column/card structure. Use this when you need the full board content — each column's name and its cards with their completion state. Works with the obsidian-kanban plugin's markdown format. Read-only. For completion counts and ratios instead of the full card list, use kanban.stats. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| kanban.statsA | Summarise a Kanban board: total cards, completed count, incomplete count, completion rate, and per-column breakdown. Use this for dashboards or progress checks where you don't need each card's full text. Read-only. Use kanban.parse when you need the actual card content. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| kanban.cardA | Add, move, or toggle a card on a Kanban board. The op field selects the mutation and determines which other fields are required: add needs columnName and cardText (plus optional status, dueDate, position); move needs cardText, fromColumn, toColumn (plus optional position); toggle needs cardText (plus optional columnName to scope the search). Missing destination columns are created automatically. Returns a {changed, target, summary, ...} mutation envelope. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Add a new card to the Todo column with a due date: {
"op": "add",
"filePath": "Boards/Project.md",
"columnName": "Todo",
"cardText": "Write migration doc",
"dueDate": "2026-05-01",
"position": "end"
}
Example 2 — Move a card from In Progress to Done: {
"op": "move",
"filePath": "Boards/Project.md",
"cardText": "Write migration doc",
"fromColumn": "In Progress",
"toColumn": "Done"
}
Example 3 — Toggle a card's completion in any column: {
"op": "toggle",
"filePath": "Boards/Project.md",
"cardText": "Write migration doc"
}
|
| canvas.createA | Create a new empty Obsidian canvas (.canvas) file at the given path. Fails if the path already exists unless overwrite: true is passed. Canvas files are JSON documents that Obsidian renders as an infinite spatial whiteboard of nodes and edges. Use canvas.edit to add nodes/edges once the file exists. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| canvas.parseA | Parse an Obsidian canvas file and return its full structure: every node (text, file, link, group) and every edge. Use this when you need the complete graph; for just the neighbours of a specific node, call canvas.connections instead. Read-only. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| canvas.connectionsA | Return the incoming and outgoing edges of a single canvas node. Use this to walk the canvas graph one node at a time without loading the full document. Read-only. For full-graph parsing, use canvas.parse. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| canvas.editA | Mutate a canvas: add a node, add an edge, or remove a node. The op field selects the mutation. add-node needs nodeType (text for inline markdown or file for an embedded note), content, x, y (plus optional width/height). add-edge needs fromNode and toNode ids (plus optional label). remove-node needs nodeId — removing a node also removes every edge incident to it (destructive). Returns a standard mutation envelope. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Add a text node to a canvas: {
"op": "add-node",
"filePath": "Boards/map.canvas",
"nodeType": "text",
"content": "Research question",
"x": 0,
"y": 0,
"width": 280,
"height": 80
}
Example 2 — Connect two existing nodes with a labelled edge: {
"op": "add-edge",
"filePath": "Boards/map.canvas",
"fromNode": "n1",
"toNode": "n2",
"label": "depends on"
}
Example 3 — Remove a node and all its edges: {
"op": "remove-node",
"filePath": "Boards/map.canvas",
"nodeId": "n3"
}
|
| templates.listA | List markdown templates in the vault's templates folder (or a folder of your choosing via templateFolder). Use this to discover what templates are available before calling templates.use. Read-only. Only returns markdown (.md) files. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| templates.useA | Render or apply a template using one of two engines. The engine field selects the engine and the action field selects the operation: engine:'filesystem' — kObsidian's built-in {{variable}} substitution; no Obsidian plugin required. Actions: render (return the expanded text) or create-note (write a new note from the template).
engine:'templater' — delegate to the Templater Obsidian plugin via the Local REST API. Requires OBSIDIAN_API_URL and OBSIDIAN_REST_API_KEY. Actions: render (execute the template and return output), create-note (execute and write to targetFile), or insert-active (insert into the currently active note in Obsidian).
The filesystem engine is pure text substitution — it does NOT evaluate Templater's <% … %> scripts. Use engine:'templater' when you need dynamic evaluation. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. Examples: Example 1 — Render a filesystem template to text (no file written): {
"engine": "filesystem",
"action": "render",
"templatePath": "Templates/daily.md",
"variables": {
"date": "2026-04-24",
"topic": "kObsidian release planning"
}
}
Example 2 — Create a new note from a filesystem template: {
"engine": "filesystem",
"action": "create-note",
"templatePath": "Templates/daily.md",
"targetPath": "Journal/2026-04-24.md",
"variables": {
"date": "2026-04-24"
}
}
Example 3 — Use Templater to create a note via the Obsidian plugin: {
"engine": "templater",
"action": "create-note",
"templateFile": "Templates/meeting.md",
"targetFile": "Meetings/Kickoff.md",
"openFile": true
}
|
| workspace.activeFileA | Return information about the file currently open and focused in Obsidian — its path, modification time, and whether it's in edit or preview mode. Read-only. Requires the Local REST API plugin (OBSIDIAN_API_URL/OBSIDIAN_REST_API_KEY). Use this to orient the agent before issuing other workspace-level mutations. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| workspace.openFileA | Open a vault-relative note path in the live Obsidian UI. newPane:true opens in a split; otherwise the current pane is reused. Requires the Local REST API plugin. This tool mutates the UI but does not touch file contents. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| workspace.closeActiveFileA | Close the currently active file in Obsidian. Does not affect file contents — only the UI. Requires the Local REST API plugin. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| workspace.navigateA | Navigate the Obsidian back/forward file history, like the arrow buttons in the top-left. direction:'back' = back one step; direction:'forward' = forward one step. No-op when the stack is empty in the given direction. Requires the Local REST API plugin. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| workspace.toggleEditModeA | Toggle the active file between edit (source) and preview (reading) mode in Obsidian. Requires the Local REST API plugin. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| commands.executeA | Execute an Obsidian command by its internal id (as returned by commands.list). args is an optional argument map passed to the command (most built-in commands take no arguments). Requires the Local REST API plugin. Destructive — the effect depends entirely on what the command does, so verify the command id before calling. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| commands.listA | List Obsidian commands. With no query, returns every registered command (both built-in and plugin-provided). With a query string, returns commands whose id or display name matches — substring match, case-insensitive. Read-only. Use this to discover command ids before calling commands.execute. Requires the Local REST API plugin. Targets the vault the live Obsidian process has open via the Local REST API. Not affected by vault.select — that only changes filesystem-tool routing. |
| wiki.initA | Scaffold the wiki layout: Sources/, Concepts/, Entities/ folders plus seed index.md, log.md, and wiki-schema.md. Idempotent; use force:true to re-seed index/log/schema files. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.ingestA | File a new source into the wiki: create Sources/.md with canonical frontmatter, append a log entry, and return proposedEdits for index + related concept/entity pages for the LLM to apply via notes.create (for new stubs) or notes.edit (mode: 'after-heading' / 'append' for inserts into existing pages). Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.logAppendA | Append a typed chronological entry to the wiki log using the convention '## [YYYY-MM-DD] | '. Initializes the wiki if needed. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.indexRebuildA | Regenerate the wiki index.md from the current state of Sources/, Concepts/, and Entities/. Groups entries by category; supports optional per-category counts. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.queryA | Rank wiki pages relevant to a topic using filename, alias, tag, summary and body hits. Returns the top pages so the LLM can drill in with notes.read. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.lintA | Health-check the wiki: orphans, broken links, stale sources, missing concept pages, singleton tags, and index.md parity. Read-only; returns grouped findings with totals. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| wiki.summaryMergeA | Add a cited section into an existing concept or entity page (or create the page with canonical frontmatter if missing). Bumps 'updated' on existing pages. Operates on the session-active vault (see vault.current — selectable via vault.select) unless an explicit vaultPath argument is passed, which always wins. |
| system.versionA | Return the running kObsidian server's package name, semver version, host runtime (bun or node), and runtime version. Use this as a health-check or to confirm which server build a client is talking to. Read-only; zero side effects. |