MCP.so
Sign In
K

Kobsidian

@bezata

About Kobsidian

Filesystem-first MCP server for Obsidian vaults with an LLM-Wiki layer on top.

Config

Add this server to your MCP-compatible client using the configuration below.

{
  "mcpServers": {
    "kobsidian": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "kobsidian-mcp"
      ],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/absolute/path/to/vault",
        "OBSIDIAN_API_URL": "https://127.0.0.1:27124",
        "OBSIDIAN_API_VERIFY_TLS": "false",
        "OBSIDIAN_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
      }
    }
  }
}

Tools

66

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_<NAME>=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: ```json {} ``` Example 2 — Force a rescan (obsidian.json changed, new env vars added): ```json { "refresh": true } ```

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.

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': ```json { "name": "Work" } ``` Example 2 — Select by id from vault.list: ```json { "id": "58f115bd2c2febd2" } ``` Example 3 — Point at an ad-hoc path (e.g. a fresh vault to initialise): ```json { "path": "/Users/alice/FreshVault" } ```

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.

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.

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: ```json { "kind": "note", "path": "Journal/2026-04-24.md", "content": "# Today\n" } ``` Example 2 — Ensure a folder exists (idempotent): ```json { "kind": "folder", "path": "Projects/Alpha/Reports" } ```

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: ```json { "mode": "append", "path": "Journal/2026-04-24.md", "content": "\n## Afternoon\n\nFinished the tool consolidation." } ``` Example 2 — Insert content after a specific heading: ```json { "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: ```json { "mode": "after-block", "path": "Notes/idea.md", "anchor": "idea-1", "content": "Follow-up thought …" } ```

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: ```json { "path": "Projects/Alpha.md", "set": { "status": "in-progress", "owner": "behzat" } } ``` Example 2 — Remove a field: ```json { "path": "Projects/Alpha.md", "unset": [ "draft" ] } ```

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.

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.

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.

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.

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): ```json { "path": "Projects/Alpha.md", "op": "add", "tags": [ "in-progress", "priority/high" ] } ``` Example 2 — Replace a note's entire tag set: ```json { "path": "Inbox/today.md", "op": "replace", "tags": [ "processed" ] } ```

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.

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.

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.

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.

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.

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.

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.

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.

Return notes with at least `minOutlinks` outgoing links (default 10), sorted by outbound count descending — the vault's connective tissue / MOCs / curated indexes. Each result carries `{path, title, outbound, inbound}`. Read-only. Use this to find pages that already act as navigational anchors (good seeds for `links.connections`); use `links.health` for a single rolled-up score across the whole vault, and `links.graph` when you need the full raw edge list rather than just the dense nodes. Operates on the session-active vault (see `vault.current` — selectable via `vault.select`) unless an explicit `vaultPath` argument is passed, which always wins.

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.

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.

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.

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.

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: ```json { "filePath": "Tasks.md", "content": "Write the v0.3.0 migration doc", "dueDate": "2026-05-01" } ``` Example 2 — Append a high-priority weekly recurring task: ```json { "filePath": "Tasks.md", "content": "Weekly review", "priority": "high", "recurrence": "every week on Sunday" } ```

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.

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.

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.

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.

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.

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.

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.

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.

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.

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: ```json { "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: ```json { "op": "remove", "filePath": "Projects/Alpha.md", "key": "status", "scope": "all" } ```

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.

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.

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: ```json { "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: ```json { "filePath": "Dashboards/Inbox.md", "language": "dataview", "blockId": "inbox-open", "source": "TASK\nFROM #inbox\nWHERE !completed" } ```

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.

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: ```json { "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: ```json { "part": "frontmatter", "filePath": "Decks/launch.md", "fields": { "theme": "gaia", "title": "Launch plan" } } ```

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.

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.

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: ```json { "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: ```json { "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: ```json { "op": "toggle", "filePath": "Boards/Project.md", "cardText": "Write migration doc" } ```

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.

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.

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.

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: ```json { "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: ```json { "op": "add-edge", "filePath": "Boards/map.canvas", "fromNode": "n1", "toNode": "n2", "label": "depends on" } ``` Example 3 — Remove a node and all its edges: ```json { "op": "remove-node", "filePath": "Boards/map.canvas", "nodeId": "n3" } ```

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.

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): ```json { "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: ```json { "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: ```json { "engine": "templater", "action": "create-note", "templateFile": "Templates/meeting.md", "targetFile": "Meetings/Kickoff.md", "openFile": true } ```

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.

Open a vault-relative note `filePath` in the live Obsidian UI. `newPane:true` opens it in a new split; default reuses the active pane. UI-only — does not create, modify, or read file contents (use `notes.read` for content). Returns `{ ok: true }` on success; errors when the file does not exist or the Local REST API plugin (OBSIDIAN_API_URL / OBSIDIAN_REST_API_KEY) is unreachable. The opened file targets the live Obsidian process's vault, which may differ from the filesystem session vault — see `vault.current`. 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. Examples: Example 1 — Reveal a daily note in the current pane.: ```json { "filePath": "Daily/2026-04-25.md" } ``` Example 2 — Open a reference note in a side split.: ```json { "filePath": "wiki/Concepts/grpc.md", "newPane": true } ```

Close whatever file is currently active in the Obsidian UI. UI-only — does not delete, save, or modify file contents. No-op when no file is active. Returns `{ ok: true }` on success; errors when the Local REST API plugin is unreachable. Use after `workspace.openFile` when you want to dismiss a temporarily-revealed note. Pair with `workspace.activeFile` first if you need to know what was closed. 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. Examples: Example 1 — Dismiss the currently active pane.: ```json {} ```

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.

Flip the active file in Obsidian between edit (source) mode and preview (reading) mode. Takes no arguments — always toggles whichever mode is currently active. UI-only: does not modify file contents. No-op when no file is active. Returns `{ ok: true, mode: 'edit' | 'preview' }` reflecting the new mode; errors when the Local REST API plugin is unreachable. Useful when an agent has finished a multi-step edit and wants the user to see the rendered result. 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. Examples: Example 1 — Flip the active note from edit to preview (or vice versa).: ```json {} ```

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.

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.

Scaffold the LLM-Wiki layout under the vault: creates `Sources/`, `Concepts/`, `Entities/` folders and seeds `index.md`, `log.md`, and `wiki-schema.md` (the schema reference the agent reads back later). Use this once per vault before calling any other `wiki.*` tool. Idempotent by default — existing files are preserved; pass `force:true` to re-seed `index.md`/`log.md`/`wiki-schema.md` (folders are never deleted). Returns `{ created: string[], skipped: string[] }` so the agent can confirm what changed. Resolves the wiki location from `wikiRoot` arg → `KOBSIDIAN_WIKI_ROOT` env → `wiki/`. 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 — First-time scaffold in the active vault.: ```json {} ``` Example 2 — Re-seed schema/index/log files in a custom wiki directory.: ```json { "wikiRoot": "knowledge", "force": true } ```

File one new source into the wiki: writes `Sources/<slug>.md` with canonical frontmatter, appends an `ingest` entry to `log.md`, and returns a `proposedEdits` array the agent applies via existing `notes.*` tools (`createStub` → `notes.create`; `insertAfterHeading` / `append` → `notes.edit` with the matching `mode`). Cross-reference writes are deliberately NOT applied here so every edit shows up in the transcript. Provide either `sourcePath` (existing vault note) OR `content` (inline markdown) — never both. Use `wiki.summaryMerge` instead when you want to file a follow-up section into an EXISTING concept/entity page; use `wiki.query` to look something up without writing. 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 — Ingest a paper from inline markdown with two related concepts and one entity.: ```json { "title": "In-Context Learning — A Survey", "content": "# In-Context Learning\n\nA survey of …", "sourceType": "paper", "url": "https://arxiv.org/abs/2301.00234", "tags": [ "icl", "prompting" ], "relatedConcepts": [ "In-Context Learning", "Few-Shot Prompting" ], "relatedEntities": [ "Brown 2020" ] } ``` Example 2 — Ingest an existing vault note as a 'note' source.: ```json { "title": "ADR-004 — gRPC for internal service comms", "sourcePath": "drafts/adr-004.md", "sourceType": "note", "tags": [ "adr", "architecture" ] } ```

Append one typed entry to `wiki/log.md` in the canonical format `## [YYYY-MM-DD] <op> | <title>`, optionally followed by a body and a `Refs:` list. The format is chosen so `grep '^## \[' log.md | tail -20` is a valid 'recent activity' query. Use this when the agent makes a wiki-meaningful action that no other `wiki.*` tool already logs (e.g. a `decision` or `note`); `ingest` and `merge` log themselves. Auto-runs `wiki.init` if the wiki has not been scaffolded yet. Idempotent only in the trivial sense — every call appends a new entry. 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 — Log an architectural decision with two refs.: ```json { "op": "decision", "title": "Adopt gRPC for internal RPC", "body": "Streaming + typed schemas outweigh the browser-edge tax.", "refs": [ "wiki/Sources/adr-004.md", "wiki/Concepts/grpc.md" ] } ``` Example 2 — Quick freeform note dated today.: ```json { "op": "note", "title": "Reviewed orphan pages from last sprint" } ```

Regenerate `wiki/index.md` from a fresh scan of `Sources/`, `Concepts/`, and `Entities/`. Pages are grouped by category and sorted alphabetically; pass `includeCounts:true` to render counts on the section headings (e.g. `## Sources (12)`). Idempotent and destructive — the existing `index.md` body is replaced wholesale, so any hand-edits there are lost. Use after bulk-creating pages outside the wiki tools, or as the cleanup step after `wiki.lint` reports `indexMismatch`. For incremental upkeep on a single source, prefer the `proposedEdits` returned by `wiki.ingest` instead. 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 — Plain rebuild.: ```json {} ``` Example 2 — Rebuild with counts on each section heading.: ```json { "includeCounts": true } ```

Rank wiki pages by relevance to a free-text `topic`, scanning Sources/Concepts/Entities pages. Hits are weighted in this order: filename match > frontmatter aliases > frontmatter tags > frontmatter summary > body. Returns up to `limit` pages (default 10, max 50) as `{path, type, score, hitFields}` so the agent can drill into the strongest candidates with `notes.read`. Read-only; never writes. Use this for 'what does the wiki know about X?' lookups; use `wiki.lint` instead for whole-vault health audits, and `notes.search` for raw full-text search outside the wiki layout. 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 — Top 10 pages relevant to 'memex vs hypertext'.: ```json { "topic": "memex vs hypertext" } ``` Example 2 — Top 25 pages on a narrow topic, custom wiki dir.: ```json { "topic": "circuit breaker pattern", "limit": 25, "wikiRoot": "knowledge" } ```

Read-only health check across the wiki. Returns grouped findings under fixed keys: `orphans` (pages with zero in/out wiki-links), `brokenLinks` (links whose target does not resolve), `staleSources` and `stalePages` (older than `staleDays`, default 180 / `KOBSIDIAN_WIKI_STALE_DAYS`), `missingPages` (concept/entity names referenced from Sources but with no page), `tagSingletons` (tags used by exactly one page — likely typos), and `indexMismatch` (entries in `index.md` that no longer match disk). Each group includes a count plus per-finding details. Never writes. Use periodically; pair the result with `notes.move`/`notes.edit`/`wiki.indexRebuild` to apply fixes. 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 — Default audit.: ```json {} ``` Example 2 — Stricter staleness threshold (90 days) for an active codebase wiki.: ```json { "staleDays": 90 } ```

Add a cited section to an EXISTING `Concepts/` or `Entities/` page, or create the page with canonical frontmatter if `targetPath` does not exist. The new section is rendered under `heading` (default: `Update YYYY-MM-DD`); `citationSource` adds a `[[wiki-link]]` to the source and pushes it onto the page's `sources:` frontmatter list, and `citationQuote` renders as a blockquote under the citation. On existing pages, `updated:` frontmatter is bumped to today. Use this when filing a follow-up onto a known page; use `wiki.ingest` instead when bringing in a NEW source (which auto-creates `Sources/<slug>.md`). When creating a new entity page, `entityKind` is required. 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 'Notable Facts' section to an existing concept page, citing one source with a quote.: ```json { "targetPath": "wiki/Concepts/circuit-breaker.md", "heading": "Notable Facts", "newSection": "Adopted by payment-service after the 2026-04-10 cascade incident.", "citationSource": "wiki/Sources/postmortem-2026-04-10-payment-timeouts-cascade.md", "citationQuote": "Timeouts in payment-service propagated to order-service within 14s." } ``` Example 2 — Create a new entity page for an organization on first reference.: ```json { "targetPath": "wiki/Entities/anthropic.md", "pageType": "entity", "entityKind": "org", "newSection": "AI safety lab; publisher of the Model Context Protocol.", "summary": "AI safety company behind Claude and MCP." } ```

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.

Overview

What is Kobsidian?

Kobsidian is a filesystem-first MCP (Model Context Protocol) server for Obsidian vaults, adding an LLM-Wiki layer inspired by Andrej Karpathy’s LLM Wiki idea. It provides 90 typed MCP tools for notes, links, tags, tasks, Dataview, Canvas, Kanban, Mermaid, Marp, and Templates — all validated with Zod. The server operates directly on the vault directory; Obsidian does not need to be running for over 80 of the tools. It is built for users who want to curate sources while an LLM handles bookkeeping, turning a vault into a compounding knowledge base.

How to use Kobsidian?

Install via npx -y kobsidian-mcp (or bunx for faster cold start), by dragging a .mcpb bundle into Claude Desktop, through Smithery, or from source. Configure three environment variables: OBSIDIAN_VAULT_PATH, OBSIDIAN_API_URL, and OBSIDIAN_REST_API_KEY (the latter only required for REST-bridged tools). A typical session starts with prompts like “Set up a wiki in this vault,” “Ingest this URL,” or “Audit the wiki,” using the wiki.* tool namespace.

Key features of Kobsidian

  • Filesystem-first — works directly on the vault without Obsidian running
  • 90 typed MCP tools with Zod validation and client‑safety hints
  • LLM‑Wiki orchestration: ingest, index, lint, and cross‑reference pages
  • Both stdio and Streamable HTTP transports with optional bearer auth
  • Ships via npm, .mcpb bundles, Smithery, and the MCP Registry

Use cases of Kobsidian

  • Personal research wiki — ingest papers, auto‑generate concept and entity stubs, maintain an index
  • Architecture Decision Records (ADRs) — model ADRs as sources, patterns as concepts, services as entities
  • Any knowledge‑base that benefits from automatic cross‑linking and linting via an LLM

FAQ from Kobsidian

What is the minimum setup needed to use Kobsidian?

Just an Obsidian vault path. No plugins are required for 80+ of the 90 tools (notes, tags, links, tasks, kanban, mermaid, marp, canvas, many dataview tools, template expansion/list/create, and the entire wiki.* namespace).

Which Obsidian plugins unlock the full tool surface?

The Obsidian Local REST API plugin is required for workspace., commands., live DQL queries (dataview.query*), and Templater runtime rendering. The Dataview, Templater, Marp, Kanban, and Tasks plugins enhance specific namespaces but are not required for the filesystem‑first tools.

Does Kobsidian require Obsidian to be running?

No. Kobsidian is filesystem‑first; it operates on the vault directory directly. REST‑bridged tools (workspace., commands., live DQL, Templater rendering) need the Local REST API plugin and its API key, but Obsidian itself does not need to be open for the majority of tools.

What transport methods does Kobsidian support?

It supports classic stdio for local MCP clients and Streamable HTTP (Hono) for remote access, with CORS preflight, MCP‑Protocol‑Version handling, origin 403, and optional bearer authentication — all per the 2025‑11‑25 MCP specification.

How can I install Kobsidian?

Frequently asked questions

What is the minimum setup needed to use Kobsidian?

Just an Obsidian vault path. No plugins are required for 80+ of the 90 tools (notes, tags, links, tasks, kanban, mermaid, marp, canvas, many dataview tools, template expansion/list/create, and the entire wiki.* namespace).

Which Obsidian plugins unlock the full tool surface?

The Obsidian Local REST API plugin is required for workspace.*, commands.*, live DQL queries (dataview.query*), and Templater runtime rendering. The Dataview, Templater, Marp, Kanban, and Tasks plugins enhance specific namespaces but are not required for the filesystem‑first tools.

Does Kobsidian require Obsidian to be running?

No. Kobsidian is filesystem‑first; it operates on the vault directory directly. REST‑bridged tools (workspace.*, commands.*, live DQL, Templater rendering) need the Local REST API plugin and its API key, but Obsidian itself does not need to be open for the majority of tools.

What transport methods does Kobsidian support?

It supports classic stdio for local MCP clients and Streamable HTTP (Hono) for remote access, with CORS preflight, MCP‑Protocol‑Version handling, origin 403, and optional bearer authentication — all per the 2025‑11‑25 MCP specification.

Comments

More Memory & Knowledge MCP servers