Back to Blog

Obsidian & Claude: A Knowledge Graph Love Story

AgentsMCPObsidianLocal-FirstSecond Brain

For about a year my physical inventory lived in a JSON store I wrote called organize2. Every box, cable, and passport had a record, parent/child containment, the works. It was clean. It was also dead weight, because the one thing I actually wanted (ask an LLM "where's my passport?" out loud from my phone and have it answer, then move an item when I move it) needed a custom API, a custom client, and a schema the model had to be taught every session. I had built a database a human had to operate. The whole point was to not operate it.

So I made a bet and graded it over a few months of daily use.

The bet: drop the database, keep the folder

The claim I wanted to falsify: a folder of plain Markdown can be a second brain an agent operates directly, with no database underneath, and it will beat the structured store it replaces. If that's wrong, retrieval gets worse, edits get sloppy, and I crawl back to JSON.

The one design decision, in a sentence: store everything as Markdown notes the model already reads natively, and put typed tools over the folder instead of a schema under it.

That folder is an Obsidian vault. iCloud syncs it, git versions it, and a private remote holds the history. Nothing proprietary sits between me and the data. The notes are the source of truth, and they're just files.

What's actually in there

The vault is 280 Markdown notes across 10 folders, and the shape of it is the argument for plain text:

foldernoteswhat it holds
inventory105physical items, containment by [[wikilink]]
people89one note per human
resources24references, ledgers, transcripts
places19cities I've actually been to
projects14active work stubs
employers10places I actually worked
daily5daily notes
notes / scripts / animals7the long tail

Containment is the test case. In organize2 a box-inside-a-box was a foreign key. Here it's [[Under-Desk Drawer]] typed into the item's note, the same wikilink Obsidian uses for everything else. The graph I get for free in the editor is the same graph the agent walks. There is no second model of the world to keep in sync with the first.

The tools go over the folder, not a server over a database

Two thin layers turn the folder into something an agent drives, and both are model-agnostic on purpose.

A skill teaches conventions, not storage. The /brain skill lives inside the vault at .claude/skills/brain/SKILL.md and is symlinked into ~/.claude/skills, so it travels with the data. It encodes the boring rules (Title Case filenames, where each note type goes, commit and push after non-trivial edits) so any Claude Code session, in any directory, edits the vault correctly without me re-explaining it.

An MCP server turns inventory into voice. brain-inventory-mcp exposes the inventory/ folder as typed tools, hosts its own single-user OAuth 2.1, and tunnels out over Cloudflare. The payoff is concrete: I can stand in a closet, say "add a USB-C cable to the under-desk drawer" into the Claude phone app, and it edits the real .md note and auto-commits it to git. The backend is still a file I can open and fix by hand.

Both layers are a few hundred lines and neither owns the data. Swap the model, swap the editor, delete the MCP server, and the vault is still 280 readable files in a git repo. That's the property organize2 never had: the day the client rotted, the data was trapped behind it.

The vault fills itself from sources I already have

A second brain is worth nothing empty, and hand-typing 280 notes is how these things die at note 30. So the same over-the-folder tools that let an agent read the vault also let it write the vault from data I already own.

Inventory populates from my own receipts. Point the agent at Gmail through an MCP server and hand it an order-confirmation email, and it writes the item note straight into Markdown: vendor, date, price, order number. A confirmation that reads "Your MechBoard order #A1729-B shipped, $89.00" becomes a line in inventory/Mechboard Keyboard.md: Purchased 2025-04-12 from MechBoard ($89.00, order #A1729-B). No form to fill, no receipts folder I'll never open again. The provenance is exact because the source is exact.

People populate from my own message history. The macOS chat.db is a local SQLite file of every iMessage I've sent or received. The agent reads it and seeds a people/ note with how we met and what the thread has been about since, and I fix whatever it got wrong. Roughly a fifth of my people-notes started this way before I hand-edited them.

Both flows are the same move: a source I already have, an agent with read access to it and write access to the folder, and a note that wasn't there a second ago.

The honest limit, said plainly: this points at my inbox and my messages, in a private repo, on my own machine. I would not run the people side against anyone else's data, and I won't pretend it's harmless if you did. It's a personal-scale tool that assumes the data is yours and the repo stays shut. Mine does.

Results: the database is gone and I didn't miss it

I graded the bet the only honest way, by killing the fallback and living on the replacement.

organize2 is archived and the local repo is deleted. I'm not running both. Inventory has been 105 Markdown notes, managed mostly by voice, for months. The structured store I spent real time on is a GitHub tombstone, and I have not once wished it back.

Conversational edits got strictly better, retrieval got no worse. This is the part I can argue but did not benchmark, so I'll say so plainly: I never ran a retrieval eval with a score. What I have is the daily reality that "where's my X" and "move Y into Z" now work by talking, which they flatly could not do against the JSON API, and the wikilink graph finds related items as well as the foreign keys ever did.

The agent and I edit the same file. When the model writes a note wrong, I fix it in Obsidian. When I'm lazy, the model fixes it from my phone. There's no export, no sync reconciliation, no "the app's copy disagrees with mine." One file, two editors.

The prior I got wrong

I went in certain that inventory needed structure. Containment, locations, quantities: that's a database's home turf, and Markdown felt like the sloppy choice that would rot into unsearchable prose. I built organize2 on that belief.

It was backwards. The structure I thought I needed was cheap to express as wikilinks, and the rigidity I'd paid for actively blocked the only interface I wanted. A schema is great when a program reads it and worthless when the reader is a model that already speaks Markdown and English. The expensive, correct part turned out to be the folder conventions, not the storage engine.

What this is and isn't

It is a single-user system that assumes you trust the model with edit access to your notes and that you keep a git history to undo it. It is not a multi-writer knowledge base, not encrypted at rest, and not a place for secrets. Passwords and keys stay out of the vault and live in a real password manager; the one rule I won't bend.

It also isn't magic retrieval. A 280-note vault fits this approach because an agent can hold the relevant slice in context. At fifty thousand notes you'd want real indexing, and the honest version of this post at that scale would have a vector store in it. I'm not there, and I won't pretend the folder-of-text trick scales forever.

The take

If you want an agent to run your second brain, store it as plain files the model already reads and put thin tools on top, instead of a database the model has to be taught and a human has to operate. The durable asset is the folder and its conventions. Everything else, the skill, the MCP server, the model behind them, is a one-line swap.

Steal the inventory layer: github.com/noahjohnson0/brain-inventory-mcp. It points at any inventory/ folder in any Obsidian vault. 🗃️