Set Up AI Search for Your Obsidian Vault - qmd, MCP Servers and AI Assistants

Set Up AI Search for Your Obsidian Vault - qmd, MCP Servers and AI Assistants

This article is part 11 of my series on finding notes in Obsidian. The overview, with the map of all 12 parts, is The Complete Guide to Obsidian Search.

Knowing how AI search works is one thing. Getting it to run on your own vault, stay fresh and give your AI assistant the right answers is another. That's where my own setup broke: a third of my vault was invisible to semantic search, and nothing told me.

The previous part explained how keyword, semantic, hybrid and agentic search work, and how to test them. This part is the hands-on side: how I set up and maintain qmd, the MCP servers that let AI assistants search your vault, how my own AI assistant finds things in a vault of about 30,000 notes, and what I think of in-editor AI plugins. You'll get the commands, the configuration files, the schedule I use to keep the index fresh, and an AGENTS.md snippet you can copy.

TL;DR

  • qmd is the best local hybrid search I've found for a vault: BM25 keyword search, embeddings, query expansion and a reranker, all on your machine (about 2.2 GB of models).
  • Write typed queries when you know what you're after. lex: for exact words, vec: for meaning, hyde: for a hypothetical answer. Let the expansion model write them when you don't.
  • qmd needs upkeep. Check qmd status before you trust a result, run qmd update then qmd embed on a schedule, and scope the collection with ignore patterns. My index once missed a whole folder of about 10,000 notes.
  • MCP servers let any AI client search your vault: qmd for meaning (without Obsidian running), my REST and MCP server for Obsidian's own search, backlinks and Bases, and the OSK plugin's server for where things live.
  • Don't give your agent a map of your vault. Give it a way to ask the vault for one, plus a written search order: Obsidian first, qmd for meaning, ripgrep for certainty.
  • In-editor AI plugins are optional. An agent working on the vault folder does the same job. Pick a plugin (Smart Connections, Similarity, Seek, Claudian) if you want related notes while you write.

qmd, a powerful bonus layer (that needs upkeep)

qmd ("Quick Markdown Search") is a local search engine for Markdown files, created by Tobias Lütke (Shopify's CEO). It has over 30,000 stars on GitHub, and it's the best local hybrid search I've found for a vault. Everything runs on your machine:

  • keyword search with BM25 (SQLite full-text search)
  • vector search with a small embedding model (EmbeddingGemma 300M by default)
  • a fine-tuned 1.7B model that expands your query into keyword (lex), semantic (vec) and hypothetical-answer (hyde) variants
  • Reciprocal Rank Fusion to merge the results
  • a reranker model (Qwen3-Reranker 0.6B) that rescores the top candidates

The models take about 2.2 GB on disk, and they're downloaded on first use.

On my machine, the three models break down as 318 MB for the embedding model, 610 MB for the reranker and 1.2 GB for the query expansion model, all stored in ~/.cache/qmd/models. qmd needs Node.js 22 or later (or Bun 1 or later), and on macOS also brew install sqlite for the SQLite extensions it uses. The index itself lives in ~/.cache/qmd/index.sqlite (about 555 MB for the 29,475 notes in my index), and the configuration in ~/.config/qmd/index.yml. Both live outside your vault, so they don't end up in your sync or your Git repository by default, which is a good thing (see the privacy risks in part 10).

npm install -g @tobilu/qmd                       # or: bun install -g @tobilu/qmd
qmd collection add ~/vault --name notes --mask "**/*.md"
qmd context add qmd://notes "My Obsidian vault: permanent notes, journal, projects"
qmd update                                       # index the files (fast, keyword search ready)
qmd embed                                        # compute the vectors (slow the first time)

qmd search "project timeline"                    # keyword only, instant
qmd vsearch "how to find old notes quickly"      # semantic only
qmd query "how do I make notes easier to find"   # hybrid + expansion + reranking (best, slowest)
qmd query $'lex: "progressive summarization"\nvec: how to make notes easier to find again'
qmd get "journal/2026-10-01.md:50" -l 100        # read part of a note

A collection is a named folder plus a file pattern. You can have several: one per vault, or a separate one for a folder you want to search on its own. -c notes restricts a search to one collection, and qmd collection exclude notes keeps a collection out of searches that don't name it (it stays searchable with -c). Masks accept several patterns separated by commas (--mask "Projects/**/*.md,Areas/**/*.md"). A context is a short description attached to a collection or a folder inside it. qmd returns it with every result, and I'll come back to why that matters so much.

On my laptop, qmd search answers in 0.6 seconds. A full qmd query takes about 80 seconds cold (loading the models), and 10 to 16 seconds once they're loaded. That's slow, but for a natural-language question the results were clearly better than any keyword search. Asked how to find old notes quickly, it surfaced my Ways to locate notes note and the "How to find notes" lesson from my course, which no keyword query ranked first.

Querying: lex, vec, hyde and intent

qmd query with a plain question lets the expansion model write the sub-queries for you. That's the easiest way, and the slowest. When you know what you're after, write a typed query document yourself: one line per sub-query, each starting with lex:, vec: or hyde:.

# Keyword only: an exact phrase, minus a word you don't want
qmd query $'lex: "weekly review" -template'

# Meaning only
qmd query $'vec: how do I plan my week without burning out'

# Hypothetical answer: describe what the note you're looking for would say
qmd query $'hyde: My weekly review takes 30 minutes on Friday afternoon. I empty my inbox, check every active project and pick three priorities for next week.'

# All three together: usually the best results
qmd query $'lex: "weekly review"\nvec: how I plan and review my week\nhyde: My weekly review takes 30 minutes on Friday afternoon.'

# Disambiguate a vague word without searching for it
qmd query "review process" --intent "personal knowledge management, not code review"

# Useful options
qmd query "review process" --explain                  # show the score breakdown of each result
qmd query "review process" --no-rerank                # faster, especially on CPU
qmd query "review process" --files --min-score 0.4    # paths only, for scripts and agents
Sub-querySearches withUse it for
lex:BM25 (keywords). Supports "exact phrases" and -exclusionsNames, titles, acronyms, quotes, anything you remember word for word
vec:Vectors (meaning)A natural-language description of the idea
hyde:Vectors, using a hypothetical answerNuanced topics where a short question is too vague; write 50 to 100 words that look like the note

Through the MCP server, the first sub-query gets double weight, so put your strongest signal first. And intent doesn't search anything by itself: it tells the expansion model and the reranker what you mean, which helps with ambiguous words like "review", "graph" or "index".

qmd also has an MCP server (qmd mcp), so AI assistants can use it directly. Run it as a daemon (qmd mcp --http --daemon) to keep the models loaded between queries.

Its tools are query (the typed sub-queries above, with intent, minScore, a collection filter and a reranking toggle), get (a note by path or ID, with an optional line range), multi_get (several notes by glob, like journal/2026-01*.md) and status. The setup for Claude Code and the daemon is in the checklist below.

qmd is a bonus layer, and it needs maintenance. It doesn't watch your files. Nothing changes in the index until you run qmd update, and new or changed notes have no vectors until you run qmd embed. Here's the state of my own index on the day I checked:

  • 19,248 notes indexed, out of about 29,500 Markdown files on disk. A whole folder of about 10,000 notes I had added two days earlier wasn't indexed at all.
  • 10,025 of the indexed notes had no embeddings. So semantic search only saw about a third of my vault, while keyword search saw about two thirds.
  • No contexts configured, and no ignore patterns: my archives and templates were indexed too.

Semantic search failing silently is worse than no semantic search, because you trust the "nothing found". So if you adopt qmd, adopt the maintenance too.

Keeping qmd fresh

[!tip] Keeping qmd fresh: the checklist 1. Check its health. qmd status shows how many notes are indexed, how many still need embeddings ("Pending") and when the index was last updated. qmd doctor checks your GPU and the index. Compare qmd ls notes | wc -l with rg --files --glob '*.md' ~/vault | wc -l. 2. Scope the collection. In ~/.config/qmd/index.yml, add ignore patterns for archives, templates, version folders and agent configuration (60 Archives/**, Templates/**, .stversions/**, .claude/**). Changes apply at the next qmd update. 3. Add contexts. qmd context add "qmd://notes/Journal" "Daily and weekly journal entries", one per top-level folder or note type. The context is returned with every hit, and it helps AI assistants understand what kind of note they're looking at. It's config only, so no re-embedding needed. 4. Refresh in the right order. Always qmd update first, then qmd embed. Embed only processes what update registered. A lock file stops two embed runs from colliding, and recent versions clear the lock left by a crashed run on their own. 5. Schedule it. For example with cron: qmd update every 30 minutes (fast), qmd update && qmd embed every night, qmd cleanup once a month. A systemd user timer or a macOS LaunchAgent works too. Keep embed out of git hooks: it's slow. 6. Keep the MCP server warm with qmd mcp --http --daemon (localhost:8181). It's unauthenticated: keep it on localhost. 7. Only when needed: qmd embed -f re-embeds everything, which you only need after changing the embedding model. Run qmd cleanup after big renames or moves. 8. Budget for the first run. Embedding tens of thousands of notes can take an hour or more on a modest GPU, and several hours on CPU. Run it overnight. Daily incremental runs take seconds to minutes.

The sections below cover each step, with the commands and configuration files.

Check its health before you trust a result

qmd status                                      # indexed notes, "Pending: N need embedding", last update
qmd doctor                                      # GPU detection, embedding freshness
qmd ls notes | wc -l                            # notes in the index
rg --files --glob '*.md' ~/vault | wc -l        # Markdown files on disk

The last two numbers won't match exactly once you add ignore patterns, and that's fine. A gap of thousands of notes you didn't exclude on purpose means qmd update hasn't run since you added them (that's how I found my missing folder of 10,000 notes). "Pending" should be 0, or close to it, after a nightly run. If you use qmd through Claude Code, the MCP server also announces it at the start of each session ("N documents need embedding"), so the agent sees the problem too.

One warning you can ignore: qmd doctor may report that a few sampled chunks "differ from stored vectors" and suggest re-embedding everything. On my machine, the difference was 0.000375, which most likely comes from the GPU backend not being perfectly deterministic. Re-embedding 30,000 notes for that isn't worth it.

Scope the collection with ignore patterns

Ignore patterns can only be set in the YAML file; there's no CLI command for them. Here's the shape, with folders you'll probably want to leave out:

# ~/.config/qmd/index.yml
collections:
  notes:
    path: /home/me/vault
    pattern: "**/*.md"
    ignore:
      - "Archives/**"
      - "Templates/**"
      - "Prompts/**"
      - ".stversions/**"
      - ".history/**"
      - ".claude/**"

qmd always skips node_modules, .git, .cache, vendor, dist and build, and you can't un-ignore those. In my vault, hidden folders such as .claude didn't show up in qmd ls either, but listing them costs nothing. Your agent's configuration folder is worth excluding explicitly: skill files mention "search" and every other keyword hundreds of times.

Two more things to consider. Mirror Obsidian's Excluded files (Settings → Files and links), so that both tools see the same vault. And if a big catalog folder (thousands of movie or book notes, for example) drowns your other results, either ignore it or move it into its own collection and run qmd collection exclude on it, so it stays searchable with -c but stays out of everyday queries. Editing the YAML does nothing until the next qmd update.

Add contexts, and why they matter

When qmd returns a hit, it also returns the context of the folder the note lives in. For you, that's a label. For an AI assistant, it's the difference between "a note that mentions pricing" and "a private journal entry that mentions pricing" or "a literature note summarizing someone else's book on pricing". It helps the agent pick the right notes, rank them, and avoid presenting someone else's idea as yours. qmd's README calls contexts "the key feature".

qmd context add qmd://notes/ "My Obsidian vault: knowledge management, writing, business"
qmd context add "qmd://notes/Permanent notes" "Atomic notes with my own ideas, in my own words"
qmd context add "qmd://notes/Literature notes" "Notes on books, articles and videos: other people's ideas"
qmd context add "qmd://notes/Journal" "Daily and weekly journal entries (private)"
qmd context add "qmd://notes/Media" "Catalog of movies, games, TV shows and books"
qmd context list

The most specific (longest) matching path wins, and qmd context add / "..." sets a global context for every collection. Contexts are configuration only, so adding or changing one doesn't require re-embedding. If your vault has note types, use them as the guide: one context per note-type folder. With the Obsidian Starter Kit, osk-cli path <type> tells you the folder of each type, so you don't have to look them up.

Refresh in the right order: update, then embed

qmd update scans each collection (pattern minus ignores), hashes every file, and reports what's new, updated, unchanged or removed. It's fast, uses no AI model, and makes keyword search current immediately. If a collection has an update command (set with qmd collection update-cmd notes 'git pull --ff-only'), it runs that first, and a failing command aborts the whole run.

qmd embed then chunks and embeds only the content that has no vectors yet. It's incremental, so a nightly run only processes what changed that day. -c notes limits it to one collection. Order matters: embed only knows about what update registered.

Renames and moves show up as "removed" plus "new", but vectors are stored by content hash, so a note you moved without editing doesn't need to be re-embedded. Reorganizing folders is cheap.

While it runs, qmd embed holds a lock file (~/.cache/qmd/.qmd-embed.lock) with its process ID, so a second run started by your scheduler prints "Another embed process is already running. Skipping." and exits. If a run crashes, the next one sees that the process is gone and removes the stale lock itself (qmd 2.6 and later).

On the GPU side, QMD_LLAMA_GPU forces a backend (vulkan, cuda, metal) or disables it (false), and QMD_FORCE_CPU=1 forces the CPU. For the first big run, plug in your laptop and let it run overnight. As a rough planning estimate (not a measurement), 10,000 pending notes take somewhere between 30 and 90 minutes on a laptop GPU, and several hours on CPU.

Schedule it

Pick whatever scheduler your system already uses. The pattern is always the same: qmd update often (it's cheap), qmd embed once a day or once an hour, qmd cleanup once a month.

With cron (Linux and macOS), run crontab -e and add:

*/30 * * * *  /full/path/to/qmd update >/dev/null 2>&1
15 3 * * *    /full/path/to/qmd update && /full/path/to/qmd embed >>~/.cache/qmd/embed.log 2>&1
0 4 1 * *     /full/path/to/qmd cleanup

Use the full path from which qmd. Cron runs with a minimal PATH, and a qmd installed through npm (especially with a Node version manager) is usually not on it. That's a common reason why a "scheduled" index silently never updates.

With a systemd user timer (Linux), create two files:

# ~/.config/systemd/user/qmd-refresh.service
[Unit]
Description=Refresh the qmd index

[Service]
Type=oneshot
ExecStart=/bin/sh -lc 'qmd update && qmd embed'
# ~/.config/systemd/user/qmd-refresh.timer
[Unit]
Description=Refresh the qmd index every night

[Timer]
OnCalendar=*-*-* 03:15
Persistent=true

[Install]
WantedBy=timers.target

Then systemctl --user daemon-reload && systemctl --user enable --now qmd-refresh.timer. Persistent=true runs a missed refresh at the next boot if your laptop was off at 3:15, and systemctl --user list-timers shows when it last ran. The -l in sh -lc starts a login shell, so your usual PATH applies.

With launchd (macOS), create ~/Library/LaunchAgents/local.qmd.refresh.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>local.qmd.refresh</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/sh</string>
    <string>-lc</string>
    <string>qmd update &amp;&amp; qmd embed</string>
  </array>
  <key>StartInterval</key>
  <integer>3600</integer>
</dict>
</plist>

Note the &amp;&amp;: the plist is XML, so && has to be escaped. Load it with launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.qmd.refresh.plist. On Windows, Task Scheduler does the same job.

Hooks are the other option. A Git post-commit or post-merge hook can run (qmd update >/dev/null 2>&1 &) in the background after each commit or pull. And if you work with Claude Code, a SessionStart hook in .claude/settings.json refreshes the keyword index every time you start a session:

{
  "hooks": {
    "SessionStart": [
      { "hooks": [ { "type": "command", "command": "qmd update >/dev/null 2>&1" } ] }
    ]
  }
}

Keep qmd embed out of hooks: it's slow, and a hook that takes minutes blocks whatever triggered it. Leave embedding to the nightly timer.

Keep the MCP server warm

By default, an AI client starts qmd mcp as a child process, once per session, and the models load again every time. That's the same model loading that makes a cold qmd query take about 80 seconds on my laptop. Running the HTTP server as a daemon keeps the models in memory:

qmd mcp --http --daemon        # starts on localhost:8181, PID in ~/.cache/qmd/mcp.pid
curl -s localhost:8181/health  # liveness check
qmd status                     # shows "MCP: running (PID ...)"
qmd mcp stop                   # stops it

Then point your client at it. For Claude Code, the qmd docs recommend the plugin (claude plugin marketplace add tobi/qmd, then claude plugin install qmd@qmd), which starts the stdio server for you. To use the warm daemon instead, add it to the .mcp.json file at the root of your vault:

{
  "mcpServers": {
    "qmd": { "type": "http", "url": "http://localhost:8181/mcp" }
  }
}

The stdio version, for comparison, is "qmd": { "command": "qmd", "args": ["mcp"] }. To start the daemon at login, reuse the systemd or launchd setup above with qmd mcp --http as the command.

The HTTP server has no authentication. It binds to localhost by default and checks the origin of requests, which is fine on a single-user machine. --host 0.0.0.0 makes it reachable from your network: don't do that without putting authentication in front of it.

When to re-embed everything, and when to clean up

qmd embed -f throws away all vectors and recomputes them. You need it after changing the embedding model, and that's about it. It isn't the fix for qmd doctor's tiny-distance warning, and it isn't needed after moving notes around.

qmd cleanup removes caches and vectors that no longer belong to any note, then compacts the database. Run it once a month, or after a big reorganization.

If you use the Obsidian Starter Kit, its tutorial Obsidian Starter Kit - Tutorial - Installing and using qmd for semantic search walks you through the setup.

MCP servers: let other tools search your vault

The Model Context Protocol (MCP) is a standard way to give AI assistants access to tools. For a vault, an MCP server is how Claude, ChatGPT or any other client can search your notes. The main options:

ServerNeeds Obsidian running?What it searches with
qmd (qmd mcp)NoHybrid keyword + semantic + reranking
Local REST API (built-in MCP server since v4)YesObsidian's search, plus JsonLogic queries over frontmatter, tags, paths and content
My REST and MCP server pluginYesEvery Obsidian CLI command: search, backlinks, orphans, tags, properties, base:query
The OSK plugin's MCP serverYesNote types, properties, folders, sections (where things live, not what they contain)
Basic MemoryNo (its own Markdown store)Hybrid full-text + vector search
mcp-obsidianYes (through Local REST API)Simple and JsonLogic search
MCP Connector (plugin)YesOn-device semantic search
Readwise MCPNoYour highlights and Reader documents, outside the vault

What each one actually lets an agent do:

qmd is the only one in the list that searches by meaning across the whole vault without Obsidian running. Its four tools (query, get, multi_get, status) are covered above. It's the right choice for "find my notes about X" from any client, at the cost of the maintenance described above.

Local REST API is the long-standing plugin for talking to Obsidian over HTTP. It now ships its own MCP server, so you don't need a separate bridge anymore. Through it, an agent can run Obsidian's simple search or structured JsonLogic queries over frontmatter, tags, paths and content. Since version 5.4, it also tells clients whether Obsidian has finished indexing the vault, which avoids a classic problem: an agent querying Obsidian right after startup and concluding that a note doesn't exist. mcp-obsidian is an older, separate MCP server that goes through this same plugin.

My REST and MCP server plugin puts every command of the official Obsidian CLI behind an HTTP API and an MCP server. For search, that means search, search:context (grep-style results with line numbers), backlinks, orphans, unresolved links, tags, properties and Bases queries: everything that depends on Obsidian's own index, links and aliases included. It's useful when the agent can't run the Obsidian CLI itself (another machine, a sandbox, a desktop chat app). It needs Obsidian running with the CLI enabled. Like the other plugin servers here, it uses Code Mode: two tools (search and execute) instead of one per command.

The OSK plugin's MCP server answers "where" and "what kind" questions rather than "what does it say": the list of note types, their folders (with date tokens resolved), name prefixes and suffixes, sections, properties, and which type a given file is. That's what lets an agent create a note in the right place or search the right folder. It doesn't read or write note content in general.

Basic Memory is a different approach: it's its own Markdown knowledge store (compatible with Obsidian), with hybrid full-text and vector search, a graph-based build_context tool and a recent_activity tool. It's open source (AGPL-3.0). Choose it if you want an AI memory that writes Markdown, rather than a search layer over the vault you already have.

MCP Connector is an Obsidian plugin that runs an MCP server inside Obsidian, with on-device semantic search (through Transformers.js), 64 tools and a token per client. It's a fork of MCP Tools, which used to be the go-to plugin for this and is now archived: remove MCP Tools from your setup if you still have it. MCP Connector moves fast, so expect its tools to change. I'd start with Local REST API's built-in MCP server and use MCP Connector only if you specifically need semantic search exposed by a plugin.

Readwise MCP searches outside your vault: your Readwise highlights and Reader documents, combining full-text and semantic search. It answers "where did I read that?" before the idea ever became a note. It's also a good reason not to import every highlight into your vault, which would only bloat your search results.

In my setup, Claude Code and Codex use the OSK plugin's server, my REST and MCP server, the skill registry (more on it below) and TaskNotes, plus Readwise and qmd. For terminal agents that work directly in the vault folder, keep in mind that you may not need MCP for basic search at all: they can call rg, the Obsidian CLI and the qmd CLI through the shell, which costs no tool-definition tokens up front.

Tips for using them with an AI assistant:

  • Tool definitions cost tokens. Every MCP tool you connect sits in the assistant's context. Anthropic showed an example where calling tools through code instead of loading them all dropped usage from 150,000 to 2,000 tokens. My own MCP servers use that "Code Mode" pattern: two tools (search and execute) instead of dozens.
  • Prefer tools that return lists and snippets, not whole notes. List matching files first, read a few snippets, then open the 3 to 5 notes that matter.

Security defaults that matter

An MCP server that can search your vault can also read all of it, and some can write and delete. A few rules:

  • Keep servers on localhost. The plugin servers in the Obsidian Starter Kit all bind to 127.0.0.1, so only programs on your own computer can reach them. qmd's HTTP server does too, and it has no authentication at all.
  • Know what your tokens protect. The kit ships with known default tokens so everything works on first launch. On a single-user machine with nothing exposed, that changes little: a program already running on your computer could read your vault files directly anyway. As I put it in the kit's reference, a known token is "a door with the key taped to it, inside your house". Regenerate the tokens if anyone else uses your computer account, if you connect an agent from another machine, or before you share your setup. Never leave a token blank: on some servers, that turns authentication off.
  • Leave browser access (CORS) off. A web page you visit runs on your machine and can try to call 127.0.0.1. With CORS off, the browser doesn't let it read the answer. AI clients such as Claude Code aren't browsers, so they don't need CORS. Omnisearch's local HTTP server is the exception to watch: while it's on, it answers requests from any web page.
  • Limit the most powerful server. My REST and MCP server can delete notes and install plugins, and with "Allow dangerous commands" on (the kit's default), run code inside Obsidian. Turn that option off if your agents don't need it, and list commands you never want to allow (like delete or plugin:install) in its blocked commands.
  • Treat .mcp.json as a secret. Once it holds your own tokens, keep it out of anything you publish, and think twice before committing it to a public repository.

The full inventory, with every port, setting and the steps to change tokens, is in Obsidian Starter Kit - Reference - MCP servers.

If you want to expose your vault to an external AI assistant, the OSK tutorial Obsidian Starter Kit - Tutorial - Exposing your vault to external AI agents with the CLI REST MCP plugin shows how.

How my AI assistant finds things in my vault

I work with Claude Code (and sometimes Codex) directly in my vault, every day. With about 30,000 notes, 69 note types and more than 400 AI skills, finding the right note, folder or skill is a real challenge for the agent. Here's what I learned.

The principle: don't give the agent a map of your vault. Give it a way to ask the vault for one, plus a written search order.

Maps of your vault go stale. An agent that can ask the vault gets the current answer.
Maps of your vault go stale. An agent that can ask the vault gets the current answer.

Maps go stale. My own instruction file listed a path for my "Top of mind" note that no longer existed. A hardcoded path would have sent the agent to a dead end. Asking the vault (osk-cli search-note "Top of mind") returned the right path.

My setup has four kinds of questions, and each one has its own way to get an answer:

The agent needs to know…It asks…
Which skill handles the taskThe skill registry (search by description or by capability)
Where a type of note livesosk-cli, which reads the note-type registry
What I know about a topicObsidian (search, backlinks, Bases), then qmd, then rg
What was decided, and whenThe memory files, session summaries, daily notes and Git history

One instruction file, with rules for asking. My CLAUDE.md contains a single line pointing to AGENTS.md, which Claude Code, Codex and Gemini CLI all read. It doesn't describe my folders. It contains rules like: never explore the vault with ls or find; resolve the folder of a note type at runtime; never hardcode folder paths or heading names; check that a note exists before writing a wikilink to it; and a search order.

It also contains a pre-flight rule: once per session, before the first Obsidian CLI call, run obsidian version. If that fails (Obsidian closed, CLI disabled), the agent switches to fallback tools for the rest of the session instead of failing on every command. The same AGENTS.md and the same MCP servers are mirrored for Codex, so both agents behave the same way. And the rules aren't only written down: a health check scans my skills for hardcoded folder paths and heading names, so new skills can't quietly reintroduce them.

A CLI that answers "where". The osk-cli that ships with the Obsidian Starter Kit plugin reads the note-type registry, and it works with Obsidian closed:

osk-cli path permanent-notes            # → the permanent notes folder
osk-cli daily-note-path                 # → today's daily note path
osk-cli daily-note-path --date 2024-12-30
# → 40 Journal/41 Daily Notes/2025/01/2024-12-30.md
osk-cli sections daily-notes --heading Discoveries --field headings --format text
# → the exact heading text, emoji included
osk-cli search-note "Top of mind"       # → canonical note by name

Look at the second date. My daily notes are filed by ISO week, so December 30, 2024 lives in the 2025 folder (week 1 of 2025). An agent guessing the path would get it wrong. Asking the registry gets it right.

A few more commands the agent uses all the time. osk-cli path persons --field suffix returns (Person), so the agent knows that a note about a person is named "Jane Doe (Person)" and can check whether it exists before linking it. osk-cli vault roots lists the real top-level folders. osk-cli capabilities resolve meta.knowledge.search returns the skill that owns a capability (here, my qmd skill). And there's a trap the registry itself taught me: osk-cli path daily-notes returns the folder template (with {{isoyear}}/{{week}} placeholders), which matches nothing on disk. To search across all daily notes, the agent asks for --field folder-base, which returns the root folder without the date part.

You don't need my kit to apply the idea. A YAML file that maps each note type to its folder, name suffix and template, plus a 20-line script that answers "where do my meeting notes live?", gives your agent the same oracle. Then forbid guessing in your instruction file.

Hooks that block guesses. A pre-tool hook checks every file path the agent wants to touch. If the agent guesses a folder that doesn't exist (LLMs tend to guess PARA-style folder names from their training data), the call is blocked with an explanation and the list of real top-level folders. The agent then corrects the path itself.

Here's what the agent sees when it tries a folder called "20 Areas" (my areas live in "30 Areas"):

BLOCKED: this references OSK vault folder(s) that do not exist.
  missing: 20 Areas
  registered roots: 10 Meta, 20 Actions, 30 Areas, 40 Journal, 50 Resources, 60 Archives
Do not guess the numbered folder layout: it differs per vault.

The error message is part of the design: it says what's wrong, what exists, and what to do instead, so the agent fixes itself on the next step. Another hook reminds the agent to use the note-type registry when it reaches for ls. A third one saves the full conversation before Claude Code compacts its context, so past sessions stay searchable later. All of them fail open: if the plugin isn't there, they do nothing and the agent carries on.

Search the skills, don't list them. My agent doesn't load the catalog of 400+ skills at startup. It searches a registry (through my Agentic Resource Discovery Server plugin) when it needs one. That alone cut the context used at startup from 77,200 to 54,400 tokens. And finding the right skill is itself a search problem. When my agent searched for "search notes in the vault", the registry didn't return my qmd skill. Rephrased as "search and retrieve notes with keyword and semantic search", it came first. Filtering by capability (meta.knowledge.search) found it directly. So phrasing matters, and structured filters are more reliable than phrasing. A high score doesn't prove a hit is right either: in these tests, the top result always scored 85.

Here are more of those tests. The left column is what the agent sent to the registry:

QueryTop resultRight skill?
I want to know what I wrote about zettelkastenAn unrelated implementation skill (85)No
find notes on this day in previous yearsBuilt-in browser (85)No
surface vault notes from this date in past yearsDiscovery Surface (85)Yes
deep memory search history of a topicHistory (85)Yes
search knowledge base notes semantic keywordqmd - Quick Markdown Search (85)Yes
find which skill handles a taskSkill Finder (85)Yes

The queries that work are phrased like a skill's description, verb first then object ("surface vault notes from this date"), not like the user's sentence ("I want to know…"). So the protocol my agent follows, injected at the start of every session, is: phrase the query as verb plus object, try at least three phrasings, read the descriptions of the hits instead of trusting the score, and never conclude that a skill is missing after one low-scoring query. When the capability is known, it skips the phrasing problem entirely and filters by capability. The registry follows the Agentic Resource Discovery specification, and the OSK tutorial Obsidian Starter Kit - Tutorial - Serving your skills and agents to AI with the Agentic Resource Discovery Server shows the setup.

Memory files as an index. A few files live at fixed paths, and the agent reads them first: stable facts (including what each top-level folder is for), preferences, hard rules, and dated lessons learned. Significant sessions end with a summary note with a "Decisions Made" section and keywords: in the frontmatter. So "what did I decide about X last month?" becomes a quick search over a few structured notes.

FileWhat it's an index of
AGENTS.mdHow to find anything: tools, rules, search order, a handful of key notes
FactsStable facts, including the top-level folders and what each one is for
MemoryPreferences, conventions and past corrections
Hard RulesNon-negotiable rules ("never hardcode a folder path")
Lessons LearnedDated failures and fixes
Agents registryOne row per AI agent: what it handles, where its files live
Session summariesOne note per significant session, with fixed sections and keywords:

Three properties make these files work as indexes. Their paths are fixed and named in AGENTS.md, so the agent never has to search for them. Their structure is fixed, so the agent can search one section ("Decisions Made") instead of whole notes. And every entry is dated, which gives each fact a source and makes "last month" a filter instead of a guess. I also emptied Claude Code's own memory folder and left a single line there pointing to the vault, so Claude Code, Codex and I share one memory, visible in Obsidian.

For the broadest questions ("what did I ever write or decide about X?"), a history skill searches nine layers and doesn't stop at the first hit: permanent notes, literature notes, daily notes, person notes, AI memory files, Claude Code's memory, Git history, Readwise highlights and, last because it's expensive, old session transcripts. It attributes every finding to its layer, keeps what I wrote separate from what an AI wrote, and lists the gaps.

Three real examples.

"Find my notes on making notes easier to find." The agent checks the qmd index status first (if many notes lack embeddings, it leans on keyword search and tells me). Then it runs a qmd query combining lex: "progressive summarization" and vec: how to make notes easier to find again later. The top hit is my Progressive summarization note (score 0.93), followed by related literature notes and a 2022 daily note pointing to a note titled "Each tag and link you add to a note adds a path to resurface that note later on". Then obsidian backlinks file="Progressive summarization" returns 25 linked notes, and rg confirms exact-phrase coverage (111 files in 0.58 seconds, where a scoped obsidian search had timed out after 90 seconds that day).

"What did I decide about ARD mode last month?" The agent resolves the daily notes folder through osk-cli, runs rg over the daily notes and session summaries of that month (15 files in 0.11 seconds), and reads the "Decisions Made" section of the matching session summary. It answers with dates and sources, in seconds.

"Add this note to today's daily note." The agent gets today's path from osk-cli daily-note-path (a path filed by ISO week, which a guess would get wrong), gets the exact heading of the Discoveries section, emoji included, from osk-cli sections, and appends the link under it. No path or heading is written in any skill. Since my skills switched to asking osk-cli sections, renaming a heading in the template reaches all of them at once, and the health check caught four skills that still pointed at a retired heading.

And the problems I ran into:

  • Stale semantic index. Covered above. The agent now checks qmd status before trusting a semantic search.
  • Notes created outside Obsidian are invisible to Obsidian until it re-indexes. Backlinks and Obsidian search miss them for a while; rg doesn't.
  • The Obsidian CLI can hang on huge searches in a big vault. Once a session hits that, it switches to the fallback order instead of retrying.
  • AI-written notes compete with mine. Notes in my AI-maintained wikis sometimes rank next to notes I wrote myself. When I want my thinking, the search is scoped to exclude them.
  • Agent configuration contains every keyword imaginable. Skill files mention "search" hundreds of times. Exclude agent folders (.claude/, .codex/) from your searches unless you're looking for them.

Here's a snippet you can adapt for your own AGENTS.md:

## Finding things in this vault
- Never explore with `ls`/`find`/`tree`. Ask the vault: note-type folders come from <your registry or CLI>; the daily note path from <command>.
- Never hardcode folder paths or heading names; refer to notes by type ("permanent notes", "daily notes").
- Search order:
  1) Obsidian (search, backlinks, Bases) for exact titles, links and properties; always scope with path= and limit=;
  2) qmd for concepts (lex + vec; check `qmd status` first and warn if embeddings are stale);
  3) ripgrep for exact strings, regex and frontmatter values; 4) plain grep as a last resort.
- Before writing a [[wikilink]], confirm the note exists (Glob `**/<Name>.md`).
- For "what did I decide/know about X": search notes, daily notes, AI memory, session summaries and git log; cite the source of each finding; say so if nothing is found.
- Exclude agent config (.claude/, .codex/) and AI-generated folders unless asked.

To adapt it, replace the two placeholders with whatever tells your agent where things live (a CLI, a script, or a short table in the file itself if your vault is small and stable), drop qmd from the order if you don't run it, and add the names of the folders you want excluded. Then test it: ask your agent three questions from your 20-query set (built in part 10) and watch which tools it calls.

My search order in one line: Obsidian first (it knows your links and properties), qmd for meaning, ripgrep for certainty (and speed). For agents and scripts, scope the Obsidian CLI with path= and limit=, or go straight to rg when speed matters.

I described my daily AI use cases in How I Use AI With My Obsidian Vault Every Day, and the whole automation stack in the automation guide.

How I Use AI With My Obsidian Vault Every Day: 16 Practical Use Cases
How to use AI with Obsidian - give an AI tool direct access to your vault's Markdown files so it works from your real context. Here are 16 practical use cases I rely on every day.
The Complete Guide to Obsidian Automation - From Templates to an AI Operating System
Obsidian automation is three layers stacked on one idea: your notes are plain files, so anything can operate on them. Here is the complete map, from your first template to an AI Operating System.

In-editor AI plugins: my take

There are many AI plugins that add semantic search or chat inside Obsidian. I don't use most of them anymore. For retrieval, I let Claude Code use the MCP servers configured in my vault (the OSK plugin, my CLI REST MCP server, the skill registry, TaskNotes, and optionally Readwise and qmd) rather than an embedding index inside Obsidian. I explain why I prefer agents working on the vault folder in Obsidian AI Plugins.

In short: thanks to the way Obsidian works (plain Markdown files in a folder), there's not much need to integrate AI inside the app. An agent pointed at the vault folder reads, searches and edits the same files, with the same tools across Claude Code, Codex and the others. Many AI plugins also split into free and paid versions, which makes them one more subscription for limited added value. And every plugin that builds its own embedding index adds one more index to keep in sync, on every device.

That said, in-editor plugins are a great fit if you want related notes while you write, or if you don't use a terminal. The main ones:

  • Smart Connections by Brian Petro ships with a local embedding model and works offline by default. Its Connections view shows notes related to the one you're reading; Lookup (also available as the separate Smart Lookup plugin) is a semantic search box. Chat and many advanced features moved to paid Pro plugins, and the license changed in early 2026 to a source-available license with a non-compete clause, which caused some backlash on Reddit. Semantic queries don't behave like plain text search: a note containing the exact text might not appear.
  • Copilot now runs Claude Code, Codex and opencode as agents inside Obsidian. Its semantic search moved to Miyo, a separate desktop app from the same team. Without Miyo, it falls back to keyword search and Obsidian-aware search through the CLI.
  • Similarity is the privacy-first option: a tiny on-device model, semantic search and related notes, no API keys, no network, and it works on mobile.
  • Seek is a new hybrid plugin (keyword + a multilingual embedding model) that claims support for 52 languages and a synced index across devices, including mobile. Its author published a self-reported benchmark (details in part 9). Early users reported a slow first indexing run, and WebGPU problems on some Linux machines.
  • Smart Second Brain 2.0 combines free full-text search (PDFs included), a Tab key to rerun the same query semantically, a topic graph and AI agents that cite notes.
  • Claudian embeds Claude Code (and other agents) in an Obsidian pane, with your vault as the working directory. If you want agentic search without leaving Obsidian, that's the most direct option.
  • Khoj and lilbee are full local RAG stacks with chat and cited sources; more setup, more moving parts.

How I'd choose among them. If you want related notes in a side panel while you write, and you're fine with a desktop-first tool, Smart Connections is the established option. If privacy or mobile matter most, look at Similarity. If you want one hybrid index synced to your phone, Seek is promising but young, so test it before relying on it. If you want an agent rather than an index, Claudian (or Copilot) brings Claude Code into Obsidian. And if you want a full chat-with-your-notes stack you host yourself, Khoj.

Whichever you pick, two checks. First, run the 20-query test from part 10 on it, next to Obsidian Search and Omnisearch; a related-notes panel that looks smart in a demo can still miss the notes you actually look for. Second, find out where it stores its index. Smart Connections, for example, keeps its embeddings in a .smart-env/ folder inside your vault, so your sync tool copies it to every device unless you exclude it, and the plugin re-imports everything when you change its model.

Your AI assistant, preconfigured

Everything in this part and in the previous one (the note-type registry and osk-cli, the AGENTS.md rules, the hooks, the MCP servers, the skill registry and a large library of AI skills, including search skills that follow the order above, and qmd if you choose to install it) ships with my Obsidian Starter Kit. Open the vault in Claude Code or Codex, and the agent follows the same rules and search order. I described the latest release in Give your AI superpowers with the Obsidian vault I run my whole business from.

Give your AI superpowers with the Obsidian vault I run my whole business from
Obsidian Starter Kit v5: the AI-ready vault I run my whole business from. Your AI knows your context, does the work, and it's ready on day one.
Obsidian Starter Kit - Give Your AI Superpowers | Knowledge Forge
My own Obsidian vault, ready for yours: templates, automation, Quick Actions and an AI assistant that finds skills on demand. 1,000+ users.

Cheat sheet

qmd

qmd status                                  # health first
qmd update && qmd embed                     # refresh, in that order
qmd search "keyword"                        # fast keyword search
qmd query "a question in plain language"    # hybrid search

More:

qmd vsearch "how to find old notes quickly"                                  # semantic only
qmd query $'lex: "progressive summarization"\nvec: how to make notes easier to find again'
qmd get "journal/2026-10-01.md:50" -l 100                                    # read part of a note
qmd mcp --http --daemon                                                      # keep the models warm for AI assistants

Conclusion

AI search on a vault is mostly plumbing: an index that stays fresh, servers that expose the vault to your assistant, and written rules that tell the agent where to look first. qmd gives you search by meaning on your own machine, as long as you schedule its updates and check its status before you trust a "nothing found". And the more your agent can ask the vault instead of guessing, the less it gets wrong.

Next in the series: Resurfacing in Obsidian - Find the Notes You Forgot You Had. Previous: AI Search for Your Obsidian Vault - Keyword, Semantic, Hybrid and Agentic Search Explained.

Prefer to read the whole series in one place? I turned it into an ebook, The Obsidian Search Handbook, with an exercise at the end of every chapter (the Complete edition adds a starter pack and worksheets).

Free cheat sheet. The commands and operators worth remembering, and the search escalation ladder (which tool to try first, depending on what you remember about the note), on 13 pages you can keep next to your vault. Enter your email and I'll send it right away: the Obsidian Search Cheat Sheet.

Free Obsidian Search Cheat Sheet: operators and the search escalation ladder
A free 13-page PDF with the Obsidian search operators worth remembering and the search escalation ladder: which tool to try first, depending on what you remember about the note.

Get the free Obsidian Search Cheat Sheet. 13 pages to keep next to your vault: the search operators worth remembering and the search escalation ladder. Enter your email and I'll send it right away. You'll also get a few short follow-up emails about finding notes in Obsidian, and you can unsubscribe at any time.


About Sébastien

Ready to get to the next level?

Found this valuable? Share it with someone who needs it.

Join 6,000+ readers. Get practical systems for knowledge & AI. Free.

Subscribe ✨

Free: Knowledge System Checklist

A clear roadmap to building your own knowledge system. Subscribe and get it straight to your inbox.

6,000+ readers. No spam. Unsubscribe anytime.

Subscribe