kObsidian
Filesystem-first MCP for Obsidian — an LLM-maintained wiki inspired by Karpathy's LLM Wiki.
Open source Open in the app JSON README (API)
About
Filesystem-first MCP for Obsidian — an LLM-maintained wiki inspired by Karpathy's LLM Wiki.
Details
- Kind
- MCP servers
- Topic
- Productivity
- Publisher
- bezata
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.2.1
- Stars
- 8
- Forks
- 1
- Last push
- 2026-09-02T09:21:42Z
- Repository state
- ativo
- Language
- TypeScript
- License
- Apache-2.0
- Added
- 2026-08-29 03:02:30
- Updated
- 2026-08-29 03:02:30
- Origin id
io.github.bezata/kobsidian-mcp
README
<div align="center">
# kObsidian MCP
**Filesystem-first MCP server for Obsidian vaults — with an LLM-Wiki layer on top.**
_Inspired by [Andrej Karpathy's **LLM Wiki**](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) idea._
You curate the sources; the LLM does the bookkeeping.
<br />
[](https://www.npmjs.com/package/kobsidian-mcp)
[](https://www.npmjs.com/package/kobsidian-mcp)
[](https://github.com/bezata/kObsidian/releases/latest)
[](LICENSE)
[](https://github.com/bezata/kObsidian/actions/workflows/release.yml)
[](https://modelcontextprotocol.io)
[](https://bun.sh)
[](https://www.typescriptlang.org/)
[](docs/tools.md)
[](docs/tools.md#resources)
[](docs/tools.md#prompts)
[](https://smithery.ai)
[](https://registry.modelcontextprotocol.io)
[](https://github.com/bezata/kObsidian/releases/latest)
<br />
[**Install**](#install) ·
[**Quick start**](#quick-start) ·
[**Architecture**](#architecture) ·
[**LLM Wiki**](#llm-wiki-60-seconds) ·
[**Tools**](#tool-surface) ·
[**Docs**](docs/README.md)
Docs: [简体中文](docs/i18n/zh-CN/README.md) / [日本語](docs/i18n/ja/README.md) / [한국어](docs/i18n/ko/README.md)
[](https://glama.ai/mcp/servers/bezata/kObsidian)
</div>
---
> **🧰 The only Obsidian MCP with workspaces.** `vault.list` / `vault.select`
> let an LLM discover and switch between your Obsidian vaults in-session —
> no restart, no config edit, no per-tool path threading. Backwards
> compatible with `OBSIDIAN_VAULT_PATH`. Added in v0.3.0.
> See **[docs/WORKSPACES.md](docs/WORKSPACES.md)**.
---
## Why kObsidian
- **Filesystem-first.** Operates on your vault directly. Obsidian doesn't need to be running for 55+ of the 66 tools.
- **66 typed MCP tools** across vaults, notes, links, tags, tasks, Dataview, Canvas, Kanban, fenced blocks, Marp, Templates — every one Zod-validated with `structuredContent` output and the full 4-hint MCP annotation set (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`).
- **Multi-vault `vault.*` (v0.3.0).** The LLM can `vault.list` your known Obsidian vaults (discovered from Obsidian's own registry or `OBSIDIAN_VAULT_<NAME>` env vars) and `vault.select` between them for the session. Fully backwards compatible: `OBSIDIAN_VAULT_PATH` stays the default and per-call `vaultPath` arguments always win.
- **LLM-Wiki orchestration** — a `wiki.*` namespace that turns your vault into a compounding knowledge base: ingest sources, auto-update an index + greppable log, lint for orphans / broken links / stale pages. Agent applies cross-refs via a `proposedEdits` contract so every write is visible in the transcript.
- **Both transports.** Classic stdio for local MCP clients and Streamable HTTP (Hono) for remote, with CORS preflight, `MCP-Protocol-Version` handling, origin 403, and optional bearer auth — all per the 2025-11-25 spec.
- **Ships everywhere.** npm (`npx -y kobsidian-mcp`), cross-platform `.mcpb` bundles for Claude Desktop drag-and-drop, a `smithery.yaml` for Smithery, and a `server.json` for the MCP Registry. Each `.mcpb` release asset is VirusTotal-scanned with links appended to the release body.
---
## Install
Pick your client below. **Every client supports the full hybrid mode**
— filesystem-first tools (80+ of them) run against the vault path
alone, and the same config can *simultaneously* carry the Local REST
API key to unlock `workspace.*`, `commands.*`, and live DQL via
`dataview.query*`. Set the whole env block once per client and every
tool namespace lights up; leave the REST key blank and the
filesystem-first tools keep working.
| Env var | Needed for |
|---|---|
| `OBSIDIAN_VAULT_PATH` | **Required everywhere.** Absolute path to the vault. |
| `OBSIDIAN_API_URL` | Base URL of the Local REST API plugin. Default `https://127.0.0.1:27124`. |
| `OBSIDIAN_API_VERIFY_TLS` | _Optional._ Defaults to `false` (the REST API plugin uses a self-signed cert on `127.0.0.1`). Set `true` only after trusting the cert in your OS keychain. |
| `OBSIDIAN_REST_API_KEY` | Local REST API plugin bearer key — only for `workspace.*` / `commands.*` / live `dataview.query*`. |
Full list in [`docs/ENVIRONMENT.md`](docs/ENVIRONMENT.md). Swap `npx`
for `bunx` anywhere if you want ≈10 ms cold-start instead of ≈200 ms.
<details>
<summary><b>Claude Code</b> — <code>claude mcp add</code></summary>
```bash
claude mcp add kobsidian -s user \
--env OBSIDIAN_VAULT_PATH=/absolute/path/to/vault \
--env OBSIDIAN_API_URL=https://127.0.0.1:27124 \
--env OBSIDIAN_REST_API_KEY=only-if-you-use-workspace-or-commands-tools \
-- npx -y kobsidian-mcp
```
On Windows, wrap the command in `cmd /c`:
`-- cmd /c npx -y kobsidian-mcp`.
</details>
<details>
<summary><b>Claude Desktop</b> — drag-and-drop <code>.mcpb</code></summary>
Download `kobsidian-<platform>.mcpb` from the
[latest release](https://github.com/bezata/kObsidian/releases/latest) and
drag it into Claude Desktop. The installer prompts for vault path +
optional API URL / key. Every release asset is VirusTotal-scanned — the
links are in the release body.
Build one locally:
```bash
bun install
bun run build:compile # → dist/kobsidian (or .exe on Windows)
bun run bundle:mcpb # → kobsidian.mcpb
```
</details>
<details>
<summary><b>Codex CLI</b> (OpenAI) — <code>codex mcp add</code></summary>
```bash
codex mcp add kobsidian \
--env OBSIDIAN_VAULT_PATH=/absolute/path/to/vault \
--env OBSIDIAN_API_URL=https://127.0.0.1:27124 \
--env OBSIDIAN_REST_API_KEY=only-if-you-use-workspace-or-commands-tools \
-- npx -y kobsidian-mcp
```
</details>
<details>
<summary><b>Cursor</b> — <code>~/.cursor/mcp.json</code> or deeplink</summary>
Edit `~/.cursor/mcp.json` (or the per-project `.cursor/mcp.json`):
```json
{
"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_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
}
}
}
}
```
Or hand a one-click deeplink to your users:
`cursor://anysphere.cursor-deeplink/mcp/install?name=kobsidian&config=<base64-encoded-config>`.
</details>
<details>
<summary><b>VS Code</b> (Copilot) — <code>code --add-mcp</code></summary>
```bash
code --add-mcp '{"name":"kobsidian","command":"npx","args":["-y","kobsidian-mcp"],"env":{"OBSIDIAN_VAULT_PATH":"/absolute/path/to/vault","OBSIDIAN_API_URL":"https://127.0.0.1:27124","OBSIDIAN_REST_API_KEY":"only-if-you-use-workspace-or-commands-tools"}}'
```
Or create `.vscode/mcp.json` in your workspace with the same shape under
a top-level `servers` key.
</details>
<details>
<summary><b>Gemini CLI</b> — <code>gemini mcp add</code></summary>
```bash
gemini mcp add kobsidian \
--env OBSIDIAN_VAULT_PATH=/absolute/path/to/vault \
--env OBSIDIAN_API_URL=https://127.0.0.1:27124 \
--env OBSIDIAN_REST_API_KEY=only-if-you-use-workspace-or-commands-tools \
-- npx -y kobsidian-mcp
```
Or hand-edit `~/.gemini/settings.json` under `mcpServers`.
</details>
<details>
<summary><b>Antigravity</b> (Google) — <code>mcp_config.json</code></summary>
Edit `~/.gemini/antigravity/mcp_config.json`
(Windows: `%USERPROFILE%\.gemini\antigravity\mcp_config.json`):
```json
{
"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_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
}
}
}
}
```
</details>
<details>
<summary><b>Zed</b> — <code>settings.json</code> under <code>context_servers</code></summary>
In `~/.config/zed/settings.json`:
```json
{
"context_servers": {
"kobsidian": {
"source": "custom",
"command": "npx",
"args": ["-y", "kobsidian-mcp"],
"env": {
"OBSIDIAN_VAULT_PATH": "/absolute/path/to/vault",
"OBSIDIAN_API_URL": "https://127.0.0.1:27124",
"OBSIDIAN_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
}
}
}
}
```
</details>
<details>
<summary><b>OpenCode</b> — <code>opencode.json</code> under <code>mcp</code></summary>
```json
{
"mcp": {
"kobsidian": {
"type": "local",
"command": ["npx", "-y", "kobsidian-mcp"],
"environment": {
"OBSIDIAN_VAULT_PATH": "/absolute/path/to/vault",
"OBSIDIAN_API_URL": "https://127.0.0.1:27124",
"OBSIDIAN_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
}
}
}
}
```
</details>
<details>
<summary><b>Factory Droid</b> — <code>droid mcp add</code></summary>
```bash
droid mcp add kobsidian "npx -y kobsidian-mcp" \
--env OBSIDIAN_VAULT_PATH=/absolute/path/to/vault \
--env OBSIDIAN_API_URL=https://127.0.0.1:27124 \
--env OBSIDIAN_REST_API_KEY=only-if-you-use-workspace-or-commands-tools
```
</details>
<details>
<summary><b>Other clients</b> (Cline, JetBrains AI, Continue, custom hosts) — generic <code>mcpServers</code> JSON</summary>
Any MCP client that reads a standard `mcpServers` object will accept:
```json
{
"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_REST_API_KEY": "only-if-you-use-workspace-or-commands-tools"
}
}
}
}
```
`"type": "stdio"` is optional on clients that infer transport from
`command` (Claude Code), but **required by Claude Desktop, Cursor,
VSCode, and Antigravity** — include it for maximum portability.
</details>
### Smithery
[smithery.ai](https://smithery.ai) renders an install UI straight from
[`smithery.yaml`](smithery.yaml) and collects the four env vars for you
— vault path plus the optional Local REST API URL / TLS / bearer key
trio, so hybrid mode works out of the box.
### From source (contributing / hacking)
```bash
git clone https://github.com/bezata/kObsidian
cd kObsidian
bun install
bun run dev:stdio # or dev:http
```
---
## Obsidian plugins
kObsidian is **filesystem-first** — 55+ of the 66 tools work against a
bare vault directory with no Obsidian plugins installed. The plugins
below only matter if you want the specific tool namespaces that depend
on them.
### Enabling community plugins (one-time, if not already on)
Obsidian ships with community plugins disabled by default. Enable them
once per vault:
1. Open your vault in Obsidian.
2. **Settings** (⚙️, bottom-left) → **Community plugins**.
3. Click **Turn on community plugins**.
4. **Browse** → search → **Install** → **Enable**.
### Required for the REST-bridged tools
**[Obsidian Local REST API](obsidian://show-plugin?id=obsidian-local-rest-api)** (by Adam Coddington) — needed for:
- `workspace.*` (activeFile, openFile, navigate, closeActiveFile, toggleEditMode)
- `commands.*` (execute, list)
- `dataview.query` / `dataview.listByTag` / `dataview.listByFolder` / `dataview.table` (runtime DQL — the offline `dataview.fields.*` / `dataview.index` / `blocks.*` tools work without it)
- `templates.use` with `engine: "templater"`
**Setup after install:**
1. Enable the plugin.
2. Open its settings — scroll to **API key** → click **Copy** (or **Reset** first if you want a fresh one).
3. Paste that key as `OBSIDIAN_REST_API_KEY` in your MCP client config's `env:` block. The `OBSIDIAN_API_URL` default (`https://127.0.0.1:27124`) works out of the box.
Leave the plugin running while you use the REST-bridged tools — the endpoint is local-only (`127.0.0.1`) so nothing leaves your machine.
### Enhances (but not required for) specific tool namespaces
| Plugin | Link | What it unlocks |
|---|---|---|
| **[Dataview](obsidian://show-plugin?id=dataview)** | `id=dataview` | All `dataview.*` tools still work on the raw markdown; Dataview plugin is what makes DQL queries in `dataview.query*` actually execute. Also renders your fields + queries visually inside Obsidian. |
| **[Templater](obsidian://show-plugin?id=templater-obsidian)** | `id=templater-obsidian` | Runtime template rendering via the REST API (`templates.use` with `engine:"templater"`). The offline filesystem engine (`templates.use` with `engine:"filesystem"`) and `templates.list` work without it. |
| **[Marp](obsidian://show-plugin?id=marp-slides)** | `id=marp-slides` | Marp `marp.*` tools parse + edit Marp-front-matter markdown even without the plugin; the plugin is what renders slides / exports to PDF inside Obsidian. |
| **[Kanban](obsidian://show-plugin?id=obsidian-kanban)** | `id=obsidian-kanban` | `kanban.*` tools read/write the plain markdown board format regardless of plugin; the plugin is what renders the board as draggable columns inside Obsidian. |
| **[Tasks](obsidian://show-plugin?id=obsidian-tasks-plugin)** | `id=obsidian-tasks-plugin` | `tasks.*` tools understand the Tasks-plugin emoji syntax (📅 ⏳ 🛫 ✅ 🔼 🔁) regardless of plugin; the plugin is what provides filtering / querying / toggling inside Obsidian. |
> The `obsidian://show-plugin?id=…` links jump straight to the plugin
> in Obsidian's in-app browser — click one with Obsidian open and it
> deep-links to the install screen.
### TLDR
| You want to … | Minimum you need |
|---|---|
| Use `notes.*` / `tags.*` / `links.*` / `stats.vault` / `tasks.*` / `wiki.*` / `kanban.*` / `blocks.*` / `marp.*` / `canvas.*` / `templates.list` + `templates.use` (`engine:"filesystem"`) / offline `dataview.*` | **Just a vault path.** No plugins required. |
| Use `workspace.*` / `commands.*` | + **Local REST API** plugin + API key env var |
| Run live DQL queries (`dataview.query` / `dataview.listBy*` / `dataview.table`) | + **Local REST API** + **Dataview** |
| Run Templater templates at runtime | + **Local REST API** + **Templater** |
No combination of plugins makes kObsidian depend on Obsidian being
running — the REST-bridged tools just return a clear error if the
plugin isn't reachable, and the filesystem-first tools keep working.
---
## Quick start
> **Before the first session** — kObsidian works on a bare Obsidian vault,
> but enabling a few Obsidian plugins unlocks the full tool surface.
> See [Obsidian plugins](#obsidian-plugins) below for the 5-minute
> setup (Local REST API, Dataview, Templater, Marp, Kanban, Tasks).
> Skip it if you only need the 80+ filesystem-first tools.
Once installed, a typical session opens with three natural-language
prompts. The `wiki.*` tools + the `.claude` skills handle the rest.
```
You: "Set up a wiki in this vault."
LLM: wiki.init → wiki/{Sources,Concepts,Entities}/ + index.md + log.md + wiki-schema.md
You: "Ingest this: https://… (paper on Memex)"
LLM: wiki.ingest → creates wiki/Sources/as-we-may-think.md + log entry
returns proposedEdits:
- insertAfterHeading index.md#Sources
- createStub Concepts/memex.md
- createStub Entities/vannevar-bush.md
LLM applies each via notes.* (you see every write in the transcript)
You: "What does the wiki say about memex vs hypertext?"
LLM: wiki.query memex → top-ranked pages
notes.read on each → cited synthesis
offers to file the synthesis back via wiki.summaryMerge
You: "Audit the wiki."
LLM: wiki.lint → {orphans, brokenLinks, stale, missingPages, tagSingletons, indexMismatch}
proposes concrete fixes; applies after you confirm
```
Full loop, frontmatter contracts, and the `proposedEdits` design in
[`docs/wiki.md`](docs/wiki.md).
---
## Example use cases
The same primitives cover several real-world flavors of knowledge base.
Three worked examples below; longer walkthroughs in [`docs/examples.md`](docs/examples.md).
### A. Personal research wiki
```
You: "Ingest this paper on in-context learning: <url or pasted markdown>"
LLM: wiki.ingest title="In-Context Learning — A Survey" sourceType=paper
tags=[icl, prompting] relatedConcepts=[In-Context Learning, Few-Shot Prompting]
relatedEntities=[Brown 2020]
→ wiki/Sources/in-context-learning-a-survey.md
→ proposedEdits:
• createStub wiki/Concepts/in-context-learning.md
• createStub wiki/Concepts/few-shot-prompting.md
• createStub wiki/Entities/brown-2020.md
• insertAfterHeading wiki/index.md#Sources
LLM applies each via notes.create / notes.edit (mode: "after-heading").
```
### B. Architecture Decision Records (ADRs) for a codebase
Model each ADR as a Source, architectural patterns as Concepts, and
services / teams / libraries as Entities. The wiki becomes your ADR
archive with cross-links you never have to maintain by hand.
```
You: "Record ADR-004: we're switching internal service comms from REST
to gRPC. Context: <paste>"
LLM: wiki.ingest title="ADR-004 — gRPC for internal service comms"
sourceType=note tags=[adr, architecture, rpc]
relatedConcepts=[gRPC, Service Mesh, Internal RPC]
relatedEntities=[order-service, payment-service, inventory-service]
→ wiki/Sources/adr-004-grpc-for-internal-service-comms.md
→ proposedEdits:
• createStub wiki/Concepts/grpc.md
• createStub wiki/Concepts/service-mesh.md
• insertAfterHeading wiki/Entities/order-service.md#Notable Facts
• insertAfterHeading wiki/Entities/payment-service.md#Notable Facts
• …
Three weeks later —
You: "Why did we pick gRPC for internal comms?"
LLM: wiki.query "grpc internal comms"
notes.read top matches
→ "Per [[wiki/Sources/adr-004-grpc-for-internal-service-comms.md|ADR-004]],
chosen over REST because of native streaming + typed schemas; tradeoff
accepted: browser clients still use REST via an edge gateway
([[wiki/Concepts/service-mesh.md]])."
```
### C. Codebase wiki (design docs + post-mortems + RFCs)
Engineering teams abandon wikis because nobody updates them. Let the
LLM do it. Ingest design docs, RFCs, and post-mortems as Sources;
architectural patterns become Concepts; services and teams become
Entities.
```
You: "We had an incident today — payment-service timeouts cascaded
into order-service. Here's the post-mortem: <paste>"
LLM: wiki.ingest title="Postmortem 2026-04-10 — Payment timeouts cascade"
sourceType=other tags=[postmortem, incident, reliability]
relatedConcepts=[Circuit Breaker, Cascade Failure, Timeout Budget]
relatedEntities=[payment-service, order-service]
→ wiki/Sources/postmortem-2026-04-10-payment-timeouts-cascade.md
→ proposedEdits:
• createStub wiki/Concepts/circuit-breaker.md
• createStub wiki/Concepts/cascade-failure.md
• insertAfterHeading wiki/Entities/payment-service.md#Notable Facts
• insertAfterHeading wiki/Entities/order-service.md#Notable Facts
Periodic housekeeping —
You: "Audit the codebase wiki."
LLM: wiki.lint
→ 3 orphan RFCs (unlinked from any Concept; link or archive?)
→ 1 broken link: [[wiki/Entities/legacy-auth-service.md]]
(deprecated in Q1; remove the link from
[[wiki/Sources/adr-002-session-migration.md]]?)
→ 4 post-mortems past the 180-day stale threshold — tag with
"needs-review" or re-ingest with updated lessons-learned?
→ 2 tag singletons: `retry-logic` (merge into `retry-policy`?),
`observability` (first use; keep).
```
**Why this works for engineering teams**
- The `proposedEdits` contract means every cross-reference write is
visible in the transcript — no silent vault corruption from an LLM
hallucination about which services a decision affects.
- The greppable log format (`## [YYYY-MM-DD] ingest | ADR-004 …`) makes
`grep '^## \[' wiki/log.md | tail -20` a valid "what did the team
decide recently" query.
- `wiki.lint` surfaces broken links to services that were deprecated
months ago — the bookkeeping humans never get around to.
---
## Architecture
```
┌──────────────────────────────────────────────────────────────────────┐
│ MCP Clients │
│ Claude Code · Claude Desktop · Cursor · VSCode · Antigravity · Zed │
│ JetBrains AI · Cline · Continue · ChatGPT · Smithery · … │
└────────────────────────────┬─────────────────────────────────────────┘
│ JSON-RPC 2.0 · MCP 2025-11-25
┌───────────────────┴──────────────────────┐
▼ ▼
┌──────────────────┐ ┌─────────────────────────┐
│ stdio transport │ │ Streamable HTTP (Hono) │
│ │ │ + OPTIONS / CORS │
│ │ │ + MCP-Protocol-Version │
│ │ │ + Origin 403 / bearer │
└────────┬─────────┘ └────────┬────────────────┘
│ │
└──────────────────┬───────────────────┘
▼
┌──────────────────────────────────┐
│ McpServer │
│ ┌────────────┐ ┌─────────────┐ │
│ │ 90 Tools │ │ 4 Resources │ │
│ └────────────┘ └─────────────┘ │
│ ┌────────────┐ ┌─────────────┐ │
│ │ 3 Prompts │ │ structured │ │
│ │ │ │ content │ │
│ └────────────┘ └─────────────┘ │
└────────────┬─────────────────────┘
│
▼
┌──────────────────────────────────┐
│ Domain layer (pure) │
│ notes · links · tags · tasks │
│ dataview · canvas · kanban │
│ blocks · marp · templates │
│ wiki/ orchestration │
└──────┬─────────────────┬─────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────────┐
│ vault/ (FS) │ │ Obsidian Local REST │
│ authoritative│ │ API plugin (optional)│
└──────────────┘ └──────────────────────┘
```
Full module map in [`docs/architecture.md`](docs/architecture.md).
---
## LLM Wiki (60 seconds)
> The tedious part of maintaining a knowledge base is not the reading or
> the thinking — it's the bookkeeping. Humans abandon wikis because the
> maintenance burden grows faster than the value. **LLMs don't get
> bored.**
kObsidian implements the **LLM Wiki** pattern from
[Andrej Karpathy's gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f):
a persistent, compounding knowledge base the LLM maintains. The vault becomes a
private, curated Memex (Vannevar Bush, 1945) where cross-references,
log-keeping, and lint are the LLM's job while you focus on curating
sources and asking questions.
> "Instead of just retrieving from raw documents at query time, the LLM
> incrementally builds and maintains a persistent wiki — a structured,
> interlinked collection of markdown files that sits between you and the
> raw sources." — Andrej Karpathy
```
User drops a source
│
▼
┌──────────────────────┐ proposedEdits
│ wiki.ingest │ ─────────────────────┐
└──────────┬───────────┘ │
│ creates 1 file ▼
│ ┌──────────────────────────────┐
▼ │ LLM applies edits via │
wiki/Sources/ │ notes.edit (after-heading) │
<slug>.md │ notes.edit (replace) │
│ │ notes.create │
│ appends └──────────────────────────────┘
▼
wiki/log.md
Anytime: wiki.query → top pages → notes.read → cited synthesis
Periodic: wiki.lint → orphans · broken · stale · missing · tag-drift
Curate: wiki.summaryMerge — add cited section to concept/entity page
```
Default layout under the vault:
```
wiki/
├── Sources/ per-source summary pages
├── Concepts/ topic / idea pages (LLM-maintained)
├── Entities/ people / places / orgs / works
├── index.md categorized catalog (wiki.indexRebuild)
├── log.md greppable chronological log
└── wiki-schema.md vault-local copy of the contract
```
**The key design decision** is that `wiki.ingest` never rewrites
cross-references blindly. It creates exactly one file (the Sources
page), appends one file (`log.md`), and returns a `proposedEdits` array
the agent applies with existing `notes.*` tools. Every write is visible
in the transcript — so LLM hallucinations show up as reviewable edits
rather than silent vault corruption.
Full contract in [`docs/wiki.md`](docs/wiki.md).
### Claude Code skills
Four skills at [`skills/`](skills/) trigger on natural language:
`wiki-bootstrap`, `wiki-ingest`, `wiki-query`, `wiki-lint`. Copy or
symlink them into `~/.claude/skills/` — see
[`skills/README.md`](skills/README.md).
---
## Tool surface
**66 MCP tools across 16 namespaces** (v0.2.5 consolidated from ~90 to 62;
v0.3.0 added the `vault.*` namespace for multi-vault support — see
[CHANGELOG](CHANGELOG.md) for the full history). Always-current
inventory at **[`docs/tool-inventory.json`](docs/tool-inventory.json)**.
| Namespace | Count | Highlights |
|---|---:|---|
| `vault.*` | 4 | `list` · `current` · `select` · `reset` — multi-vault discovery and session switching (v0.3.0) |
| `notes.*` | 8 | `read` (content/metadata/stats via `include`) · `create` (note or folder) · `edit` (replace/append/prepend/after-heading/after-block) · `frontmatter` · `delete` · `move` · `list` · `search` |
| `tags.*` | 4 | `modify` (add/remove/replace/merge) · `search` · `analyze` · `list` |
| `links.*` | 8 | Backlinks · outgoing · broken · orphans · hubs · graph · health · connections |
| `stats.*` | 1 | `stats.vault` (per-note stats moved into `notes.read`) |
| `tasks.*` | 5 | Tasks-plugin format (📅 ⏳ 🛫 ✅ 🔼 🔁) — search · create · toggle · updateMetadata · stats |
| `dataview.*` | 7 | `query` + sugar wrappers (`listByTag`/`listByFolder`/`table`) · `index` · `fields.read` · `fields.write` |
| `blocks.*` | 3 | Unified fenced-block API (`list`/`read`/`update`) across `dataview`, `dataviewjs`, `mermaid` |
| `marp.*` | 2 | `read` (deck/slides/slide) · `update` (slide/frontmatter) |
| `kanban.*` | 3 | `parse` · `stats` · `card` (add/move/toggle) |
| `canvas.*` | 4 | `create` · `parse` · `connections` · `edit` (add-node/add-edge/remove-node) |
| `templates.*` | 2 | `list` · `use` (engine × action) |
| `workspace.*` | 5 | Live Obsidian UI bridge (requires Local REST API plugin) |
| `commands.*` | 2 | `list` (with optional query) · `execute` |
| `wiki.*` | 7 | init · ingest · log · indexRebuild · query · lint · summaryMerge |
| `system.*` | 1 | `version` |
**Client-safety annotations** (MCP 2025-11-25):
| Hint | Tools |
|---|---:|
| `readOnlyHint: true` (clients can auto-approve) | 47 |
| `destructiveHint: true` (clients prompt more firmly) | 6 |
| `idempotentHint: true` (safe to retry) | 12 |
| `openWorldHint: true` (reaches outside the vault) | 16 |
**MCP resources** (URI-addressable; any client can browse without tool
calls):
```
kobsidian://wiki/index wiki/index.md
kobsidian://wiki/log wiki/log.md
kobsidian://wiki/schema wiki/wiki-schema.md
kobsidian://wiki/page/{+path} any Sources/Concepts/Entities page
```
**MCP prompts** (for clients that don't consume the `skills/` files):
`ingest-source`, `answer-from-wiki`, `health-check-wiki`.
Details in [`docs/tools.md`](docs/tools.md).
---
## Configuration
| Env var | Default | Purpose |
|---|---|---|
| `OBSIDIAN_VAULT_PATH` | — | **Required.** Absolute path to the vault. |
| `OBSIDIAN_API_URL` | `https://127.0.0.1:27124` | Obsidian Local REST API base; only for `workspace.*` / `commands.*` / `dataview.query*`. |
| `OBSIDIAN_API_VERIFY_TLS` | `false` | Set `true` if you've trusted the REST API's self-signed cert. |
| `OBSIDIAN_REST_API_KEY` | — | Bearer key for the REST API plugin (if used). |
| `KOBSIDIAN_HTTP_HOST` | `127.0.0.1` | Bind host for `dev:http`. |
| `KOBSIDIAN_HTTP_PORT` | `3000` | Bind port for `dev:http`. |
| `KOBSIDIAN_HTTP_BEARER_TOKEN` | — | Optional bearer for the Streamable HTTP transport. |
| `KOBSIDIAN_ALLOWED_ORIGINS` | `http://localhost,http://127.0.0.1` | Comma-separated CORS allowlist. |
| `KOBSIDIAN_WIKI_ROOT` | `wiki` | Wiki directory under the vault. |
| `KOBSIDIAN_WIKI_SOURCES_DIR` | `Sources` | Per-source summary pages. |
| `KOBSIDIAN_WIKI_CONCEPTS_DIR` | `Concepts` | Topic / idea pages. |
| `KOBSIDIAN_WIKI_ENTITIES_DIR` | `Entities` | People / places / orgs / works. |
| `KOBSIDIAN_WIKI_INDEX_FILE` | `index.md` | Wiki catalog filename. |
| `KOBSIDIAN_WIKI_LOG_FILE` | `log.md` | Wiki log filename. |
| `KOBSIDIAN_WIKI_SCHEMA_FILE` | `wiki-schema.md` | Seed schema filename. |
| `KOBSIDIAN_WIKI_STALE_DAYS` | `180` | `wiki.lint` stale-page threshold. |
| `KOBSIDIAN_WIKI_INDEX_SOURCES_HEADING` | `Sources` | `index.md` section that lists sources; `wiki.ingest` files new entries under it. |
| `KOBSIDIAN_WIKI_INDEX_CONCEPTS_HEADING` | `Concepts` | `index.md` section that lists concepts. |
| `KOBSIDIAN_WIKI_INDEX_ENTITIES_HEADING` | `Entities` | `index.md` section that lists entities. |
| `KOBSIDIAN_WIKI_CONCEPT_PAGE_HEADING` | `Discussion` | Concept-page heading that receives `wiki.ingest` citations / `wiki.summaryMerge` sections. |
| `KOBSIDIAN_WIKI_ENTITY_PAGE_HEADING` | `Notable Facts` | Entity-page heading that receives `wiki.ingest` citations / `wiki.summaryMerge` sections. |
| `KOBSIDIAN_VAULT_CONFIG_FILE` | `.kobsidian.json` | Vault-relative path of the per-vault config file (below). |
Every wiki tool also accepts a per-call `wikiRoot` override, and `wiki.ingest`
accepts per-call `indexHeading` / `conceptHeading` / `entityHeading` overrides
for vaults whose pages use localized headings (e.g. `## Fontes`). When a
target heading is missing from a page, the proposal degrades to `append` so it
can still be applied via `notes.edit`.
### Per-vault config file
Settings that belong to one vault rather than to the server — folder names,
filenames, and section headings of a localized wiki — go in a
`.kobsidian.json` at the vault root. Every key is optional and beats the
matching env var; a per-call tool argument beats both
(**per-call → `.kobsidian.json` → env → default**).
```json
{
"$schema": "https://raw.githubusercontent.com/bezata/kObsidian/main/docs/kobsidian.config.schema.json",
"wiki": {
"root": "wiki",
"sourcesDir": "Fontes",
"staleDays": 90,
"headings": {
"indexSources": "Fontes",
"indexConcepts": "Conceitos",
"indexEntities": "Entidades",
"conceptPage": "Discussão",
"entityPage": "Fatos Notáveis"
}
}
}
```
Unknown keys and malformed JSON are rejected with the file path in the error
so typos surface immediately; `vault.current` returns the effective
configuration under `config` (or the error).
---
## Docs
Localized docs are available in **[简体中文](docs/i18n/zh-CN/README.md)**,
**[日本語](docs/i18n/ja/README.md)**, and
**[한국어](docs/i18n/ko/README.md)**.
| | |
|---|---|
| [architecture.md](docs/architecture.md) | Stack, module map, layering rules |
| [wiki.md](docs/wiki.md) | LLM-Wiki contract, loop, frontmatter, lint categories |
| [examples.md](docs/examples.md) | Personal research wiki · engineering ADRs · codebase wiki — end-to-end |
| [tools.md](docs/tools.md) | Namespace table, annotations, resources, prompts |
| [SECURITY.md](docs/SECURITY.md) | Origin/CORS, VirusTotal scans, env hygiene |
| [TESTING.md](docs/TESTING.md) | `bun run …` commands + coverage |
| [ENVIRONMENT.md](docs/ENVIRONMENT.md) | Every env var with defaults |
| [MIGRATION.md](docs/MIGRATION.md) | Upgrade notes |
---
## Roadmap
The next two milestones are tracked in [`TODO.md`](TODO.md):
- **v0.4 — Obsidian LiveSync bridge.** Free, end-to-end-encrypted
vault access via the community
[Self-Hosted LiveSync](https://github.com/vrtmrz/obsidian-livesync)
plugin (CouchDB / S3 / R2 / WebRTC peer) — so an MCP client can
reach the same Obsidian vault from any machine the user owns,
without Obsidian itself being live.
- **v0.5 — Cross-semantic vault verification.** A `wiki.crossCheck`
tool that reconciles two or more LiveSync-paired vaults at the
wiki layer, gated by a new `schema_version` frontmatter field that
uses the project's semver discipline as the compatibility contract.
`TODO.md` carries the motivation, the per-milestone task breakdown,
and the rules for how items move from there into the CHANGELOG.
---
## Development
```bash
bun install
bun run typecheck
bun run lint
bun run test # 56 tests across 14 files
bun run build # node-target stdio.js + bun-target http.js
bun run inventory # regenerate docs/tool-inventory.json
```
Project conventions in [`AGENTS.md`](AGENTS.md).
---
## Security & supply chain
- **Every `.mcpb` release asset is VirusTotal-scanned.** The `Release`
workflow uploads each `kobsidian-<platform>.mcpb` bundle to
[VirusTotal](https://www.virustotal.com) via
[`crazy-max/ghaction-virustotal@v4`](https://github.com/crazy-max/ghaction-virustotal)
right after the release is published, then appends the analysis links
to the release body. Any user installing from a GitHub release can
click through to the public VirusTotal report for their platform's
bundle **before** they run it — no trust in the maintainer required.
- **Transport hardening.** Streamable HTTP validates `Origin` against
an allowlist (403 on mismatch), implements CORS preflight
(`OPTIONS /mcp` → 204 + `Access-Control-*`), requires or defaults
`MCP-Protocol-Version`, and supports optional bearer auth via
`KOBSIDIAN_HTTP_BEARER_TOKEN`. stdio has no network surface.
- **Pinned SDK floor.** `@modelcontextprotocol/sdk@^1.26.0` — mitigates
`GHSA-345p-7cg4-v4c7` (cross-client response leak) and
`CVE-2026-0621` (UriTemplate ReDoS). This repo pins `1.30.0`.
- **npm Trusted Publishing.** No long-lived `NPM_TOKEN` is stored in the
repo. GitHub Actions mints a short-lived OIDC token on every tag push
and the npm CLI exchanges it for a one-time publish token scoped to
this exact workflow file (`.github/workflows/release.yml` on the
`bezata/kObsidian` repo). Provenance attestations are automatic — every
published version has a cryptographically-linked build statement
pointing at the exact Actions run that produced it. Forks, other
branches, or modified workflow files cannot publish — the OIDC
audience claim won't match.
Full notes in [`docs/SECURITY.md`](docs/SECURITY.md).
---
## Compatibility notes
- **Protocol version** — `2025-11-25` (current MCP spec). HTTP clients
without `MCP-Protocol-Version` fall back to `2025-03-26` per spec;
explicit-but-unsupported versions return 400.
- **Dataview split** — offline tools index frontmatter / inline / list /
task / fenced `dataview` / fenced `dataviewjs` blocks. Runtime DQL is
delegated to Obsidian + Dataview through the Local REST API.
DataviewJS is source-preserving but not executed inside this server.
- **Mermaid + Marp** — source-preserving parse/edit only; rendering is
the client's job.
- **SDK floor** — `@modelcontextprotocol/sdk@^1.26.0` (mitigates
`GHSA-345p-7cg4-v4c7` cross-client response leak + `CVE-2026-0621`
UriTemplate ReDoS). This repo pins `1.30.0`.
---
## Credits
- **LLM Wiki pattern** — [Andrej Karpathy's gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).
kObsidian is one concrete, filesystem-first TypeScript implementation
of the idea.
- **Memex** — Vannevar Bush, [_As We May Think_, 1945](https://www.theatlantic.com/magazine/archive/1945/07/as-we-may-think/303881/).
The associative-trails concept is what the wiki's cross-reference
graph tries to be.
- **Model Context Protocol** — [Anthropic + the Agentic AI Foundation](https://modelcontextprotocol.io).
- **Obsidian** — [obsidian.md](https://obsidian.md). The vault format
is authoritative; kObsidian respects it, doesn't migrate it.
## License
MIT — see [LICENSE](LICENSE). Contributions welcome; open an issue first
for anything non-trivial.