Skip to main content

MCP server for semantic search and file management over Obsidian vaults using Qdrant and local embeddings

Project description

obsidian-qdrant-search

Obsidian Qdrant Search

Version PyPI Python License Qdrant MCP Obsidian

MCP server for semantic search and file management over an Obsidian vault. Uses Qdrant as vector store and FastEmbed for local embeddings. Provides a complete set of tools for AI agents to read, write, search, and manage vault content โ€” no external Obsidian plugins required.


Table of Contents

Section Description
? Why? The problem this solves
โœจ Features Full feature list
โšก Quick Start Installation and setup
๐Ÿ“– LLM Wiki Pattern Karpathy-inspired knowledge base model
๐Ÿค– Agent Skills Skill and agent for Claude Code
๐Ÿ”ง MCP Tools All 27 tools โ€” Search, Read, Write, Discover, Graph, Batch, Log, Health, Maintenance
๐Ÿ’ป CLI Commands Command-line interface for any agent
๐Ÿ—๏ธ Architecture How it works under the hood
๐Ÿ“ Project Structure File layout

Why?

Standard text search in Obsidian (and MCP tools like mcp-obsidian) is keyword-based โ€” it only finds exact matches. This means:

  • Searching for "API logs" won't find a section titled "Execution tracking endpoints"
  • Searching for "how does authentication work" returns nothing unless those exact words appear
  • Typos, synonyms, and rephrased concepts are invisible to keyword search

obsidian-qdrant-search uses vector embeddings to understand the meaning of your query and match it against the meaning of your documentation. It finds relevant results even when the wording is completely different.

Additionally, it provides full CRUD file operations directly on the vault filesystem, so AI agents can read, create, update, and manage notes without relying on external Obsidian community plugins like Local Rest API.

Features

  • LLM Wiki pattern โ€” Karpathy-inspired three-layer architecture for persistent, compounding knowledge bases
  • Semantic search โ€” find docs by meaning, not just keywords
  • Full file management โ€” read, create, update, append, patch, and delete vault files
  • Targeted patching โ€” modify specific sections by heading or frontmatter field
  • Text search โ€” case-insensitive keyword search across all markdown files
  • Markdown-aware chunking โ€” tables and code blocks are never split mid-block
  • Frontmatter filters โ€” narrow results by project, document type, or tags
  • Context expansion โ€” fetch adjacent chunks around a search result
  • Wikilink graph โ€” navigate backlinks, outgoing links, find broken links and orphan files
  • Vault health checks โ€” comprehensive lint with broken links, orphans, stale docs, missing metadata
  • Operation log โ€” chronological record of ingest, query, lint, and maintenance actions
  • Vault migration โ€” non-destructive upgrade of existing vaults to the LLM Wiki conventions
  • Multi-agent CLI โ€” 7 CLI commands with --json output for any agent (Codex, OpenCode, etc.)
  • Vault map โ€” visualize directory structure with file counts
  • Frontmatter schema discovery โ€” see all fields, types, and usage across the vault
  • Tag discovery โ€” list all tags (frontmatter + inline) with occurrence counts
  • Recent changes โ€” track recently modified files
  • Batch operations โ€” update frontmatter or rename tags across multiple files at once
  • Incremental indexing โ€” only re-embeds changed files
  • Auto-reindex on write โ€” modified files are automatically re-indexed (best-effort)
  • Auto-start Qdrant โ€” Docker container is managed automatically
  • Local embeddings โ€” no API keys needed, runs entirely on your machine
  • Path security โ€” all file operations are validated to prevent access outside the vault

Prerequisites

  • Python 3.11+
  • Docker (for Qdrant)
  • uv (recommended) or pip

Quick start

1. MCP Configuration

Add to your .mcp.json (project root or Claude Code settings):

{
  "mcpServers": {
    "obsidian-qdrant-search": {
      "command": "uvx",
      "args": ["obsidian-qdrant-search"],
      "env": {
        "VAULT_PATH": "/absolute/path/to/your/vault"
      }
    }
  }
}

Qdrant is started automatically via Docker when needed. If a qdrant container already exists, it will be reused.

2. Initial indexing

VAULT_PATH=/path/to/your/vault uvx --from obsidian-qdrant-search vault-index --full

LLM Wiki Pattern

Inspired by Karpathy's LLM Wiki, this project supports a three-layer architecture for building persistent, compounding knowledge bases maintained by LLMs:

Layer Directory Purpose
Raw sources raw/ Immutable source documents โ€” articles, papers, transcripts, web clips. The LLM reads from these but never modifies them.
Wiki wiki/ LLM-maintained pages โ€” entities, concepts, summaries, syntheses. The LLM owns this layer entirely.
Schema CLAUDE.md / AGENTS.md Conventions that tell the LLM how the wiki is structured and what workflows to follow.

Operations

  • Ingest โ€” Drop a new source into raw/, the LLM reads it, creates/updates wiki pages, adds wikilinks, and logs the operation
  • Query โ€” Search the wiki, synthesize answers, and optionally file valuable results back as new wiki pages
  • Lint โ€” Run lint_vault for a comprehensive health check (broken links, orphans, stale docs, missing metadata)

Migration from older versions

The migration tool upgrades an existing vault to the LLM Wiki three-layer structure. It always shows a preview first โ€” no changes are applied until you confirm.

Assisted mode (default)

The tool analyzes every file and classifies it:

Classification Heuristic Action
wiki Has type in frontmatter, or contains [[wikilinks]] Moved to wiki/
raw No frontmatter, no links (likely a source document) Moved to raw/
unknown Has some frontmatter but no clear signal Left in place for manual review

After moving files, it updates all path-based wikilinks (e.g. [[notes/article]] becomes [[raw/notes/article]]), adds missing frontmatter fields with sensible defaults, and initializes the operation log.

Safety guarantees:

  • Subdirectory structure is preserved (notes/article.md becomes raw/notes/article.md)
  • If a file already exists at the destination, the move is skipped (no overwrites)
  • Empty directories vacated by moves are cleaned up; unrelated empty directories are preserved
  • Idempotent โ€” running it again after a successful migration changes nothing

Manual mode

Creates empty raw/ and wiki/ directories and adds missing frontmatter, but does not move any files. Use this if you prefer to organize files yourself.

How to run

From Claude Code, ask in natural language:

"migrate my vault"

From the command line:

# Preview (assisted, default)
VAULT_PATH=/path/to/vault uvx --from obsidian-qdrant-search vault-search-migrate

# Apply
VAULT_PATH=/path/to/vault uvx --from obsidian-qdrant-search vault-search-migrate --apply

# Manual mode
VAULT_PATH=/path/to/vault uvx --from obsidian-qdrant-search vault-search-migrate --mode manual --apply

See CLAUDE.md for the full document structure rules and vault conventions.

Agent Skills

This repo includes Claude Code skills and agents in .claude/. Copy the .claude/ directory into your project to make them available. Claude will automatically discover and use them based on context.

/vault-search โ€” Skill (.claude/skills/vault-search/)

Guides the agent through semantic search, text search, file reading, and knowledge graph navigation. Claude can invoke it automatically or you can use it as a slash command:

/vault-search how does authentication work

doc-manager โ€” Agent (.claude/agents/)

An autonomous documentation agent that creates, updates, organizes, and maintains vault documentation. Includes templates, conventions, health check workflows, and restructuring procedures. Claude dispatches it as a subagent when documentation tasks are needed.

/doc-manager document the new authentication module in projects/core
/doc-manager run a vault health check
/doc-manager create an ADR for switching to PostgreSQL

Environment variables

Variable Default Description
VAULT_PATH Current working directory Path to the Obsidian vault directory
QDRANT_URL http://localhost:6333 Qdrant server URL
COLLECTION_NAME vault_docs Qdrant collection name
VAULT_LOG_FILE _log.md Operation log filename

MCP Tools

Search

search_vault

Semantic search over the vault documentation.

Parameter Type Default Description
query string required Natural language search query
project string null Filter by project name (e.g. "core")
doc_type string null Filter by document type (e.g. "api-contract", "service-layer")
tag string null Filter by frontmatter tag (e.g. "kubernetes", "database")
top_k int 5 Number of results to return

simple_search

Case-insensitive text search across all markdown files.

Parameter Type Default Description
query string required Text to search for
context_length int 100 Characters of context around each match

get_chunk_context

Expand context around a search result by fetching adjacent chunks.

Parameter Type Default Description
file_path string required The file_path from a search result
chunk_index int required The chunk_index from a search result
window int 1 Number of chunks before/after to include

Read

get_file_contents

Read the raw content of a vault file.

Parameter Type Default Description
filepath string required Path relative to vault root

get_file_metadata

Get frontmatter metadata, tags, and file stats (size, dates).

Parameter Type Default Description
filepath string required Path relative to vault root

list_files_in_dir

List files and subdirectories in a vault directory (non-recursive).

Parameter Type Default Description
dirpath string "" Relative directory path (empty for root)

list_files_in_vault

List all top-level files and directories in the vault root.

Write

create_or_update_file

Create a new file or overwrite an existing one. Parent directories are created automatically.

Parameter Type Default Description
filepath string required Path relative to vault root
content string required Full file content

append_content

Append content to a file (creates the file if it doesn't exist).

Parameter Type Default Description
filepath string required Path relative to vault root
content string required Content to append

patch_content

Targeted modification of a specific section by heading or frontmatter field.

Parameter Type Default Description
filepath string required Path relative to vault root
operation string required "append", "prepend", or "replace"
target_type string required "heading" or "frontmatter"
target string required Heading text (e.g. "Setup" or "Setup::Installation") or frontmatter field name
content string required Content to insert or replace with

delete_file

Delete a file from the vault.

Parameter Type Default Description
filepath string required Path relative to vault root
confirm bool false Must be true to actually delete (safety guard)

Discover

list_projects

Lists all indexed projects with file and chunk counts.

list_tags

Lists all tags (frontmatter + inline #tag) with occurrence counts across the vault.

get_recent_changes

Returns recently modified markdown files sorted by modification date.

Parameter Type Default Description
days int 14 Only include files modified within this many days
limit int 10 Maximum number of results

get_vault_map

Get the vault's directory structure as a tree with file counts.

Parameter Type Default Description
max_depth int 3 Maximum directory depth to show

get_frontmatter_schema

Discover all frontmatter fields used across the vault with types, frequency, and examples.

Graph

get_backlinks

Find all files that contain wikilinks pointing to the given file.

Parameter Type Default Description
filepath string required Relative path to the target file

get_outgoing_links

List all files that the given file links to via wikilinks.

Parameter Type Default Description
filepath string required Relative path to the source file

find_broken_links

Find all wikilinks in the vault that point to non-existent files.

find_orphan_files

Find files that have no incoming wikilinks from other files.

Batch

batch_update_frontmatter

Update a frontmatter field across multiple files matching a filter.

Parameter Type Default Description
filter_type string required "project", "tag", or "glob"
filter_value string required Filter value (project name, tag, or glob pattern)
field string required Frontmatter field to update
value string required Value to set/append/remove (YAML parsed)
operation string "set" "set", "append", or "remove"
confirm bool false Set to true to apply (default returns preview)

batch_rename_tag

Rename a tag across all vault files (both frontmatter and inline #tags).

Parameter Type Default Description
old_tag string required Tag to rename (without #)
new_tag string required New tag name (without #)
confirm bool false Set to true to apply (default returns preview)

Log

log_operation

Append a structured entry to the vault operation log.

Parameter Type Default Description
operation_type string required "ingest", "query", "lint", or "maintenance"
title string required Short title for the entry
summary string "" Optional description
pages_touched list [] Optional list of modified file paths
source string "" Optional source file path (for ingest)

get_operation_log

Read recent entries from the operation log.

Parameter Type Default Description
last_n int 20 Number of recent entries to return
filter_type string "" Filter by operation type (e.g. "ingest")

Health

lint_vault

Comprehensive vault health check. Reports broken wikilinks (critical), orphan files (warning), missing frontmatter (warning), stale documents (info), stub documents (info), and isolated pages (info).

Parameter Type Default Description
stale_days int 90 Flag files not modified within this many days

Maintenance

reindex_vault

Re-indexes the vault into Qdrant. After upgrading, run with full=true to rebuild the index.

Parameter Type Default Description
full bool false If true, drops and recreates the collection

migrate_vault

Migrate an existing vault to the LLM Wiki pattern. See Migration from older versions for a detailed explanation of both modes.

Parameter Type Default Description
confirm bool false Set to true to apply (default returns preview)
mode string "assisted" "assisted" (classify, move, update links) or "manual" (dirs + frontmatter only)

CLI Commands

MCP Server & Indexing

Command Description
obsidian-qdrant-search Start the MCP server (stdio transport)
vault-index [--full] Run indexing (incremental by default, --full drops and recreates)

Multi-Agent CLI

All commands support --json for structured output that any agent can parse.

Command Description
vault-search-search "<query>" [--project X] [--top-k 5] [--json] Semantic search
vault-search-read <filepath> Read a vault file
vault-search-write <filepath> --content "..." Create or update a file
vault-search-lint [--stale-days 90] [--json] Vault health check
vault-search-log <type> "<title>" [--summary "..."] [--source "..."] Log an operation
vault-search-log --read [--last 20] [--filter <type>] [--json] Read log entries
vault-search-map [--depth 3] [--json] Show vault structure
vault-search-migrate [--apply] [--json] Migrate vault to LLM Wiki pattern

These CLI commands make the vault accessible to any agent that can shell out (Codex, OpenCode, etc.), not just MCP-aware clients. See AGENTS.md for the full agent-oriented reference.

Architecture

Obsidian vault (.md files)
    |
    v
indexer โ”€โ”€ chunk by H2/H3 sections โ”€โ”€> Qdrant (vector DB)
    |         preserves tables              |
    |         preserves code blocks         |
    v                                       v
fastembed (BAAI/bge-small-en-v1.5)    server (MCP stdio)
    local embeddings, 384 dim            search / CRUD / reindex

Chunking strategy: Documents are split by ## headings, then ### if needed. Tables and fenced code blocks are never split mid-block. Large sections fall back to a block-aware sliding window with overlap.

Incremental indexing: Files are tracked by SHA-256 hash. Only changed files are re-embedded on reindex_vault(). Deleted files are automatically cleaned up.

Auto-reindex on write: When files are created, updated, or deleted via MCP tools, the search index is automatically updated for the affected file. This is best-effort โ€” if Qdrant is unavailable, the write still succeeds.

Project structure

obsidian-qdrant-search/
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ CHANGELOG.md
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ CLAUDE.md              # schema layer for Claude Code (auto-loaded)
โ”œโ”€โ”€ AGENTS.md              # schema layer for other agents (Codex, OpenCode)
โ”œโ”€โ”€ .claude/
โ”‚   โ”œโ”€โ”€ skills/
โ”‚   โ”‚   โ””โ”€โ”€ vault-search/SKILL.md   # /vault-search slash command
โ”‚   โ””โ”€โ”€ agents/
โ”‚       โ””โ”€โ”€ doc-manager.md           # documentation manager agent
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ test_path_utils.py
โ”‚   โ”œโ”€โ”€ test_vault_ops.py
โ”‚   โ”œโ”€โ”€ test_indexer.py
โ”‚   โ”œโ”€โ”€ test_log.py
โ”‚   โ”œโ”€โ”€ test_lint.py
โ”‚   โ””โ”€โ”€ test_migrate.py
โ””โ”€โ”€ src/
    โ””โ”€โ”€ vault_search/
        โ”œโ”€โ”€ __init__.py
        โ”œโ”€โ”€ __main__.py      # python -m vault_search
        โ”œโ”€โ”€ cli.py            # CLI entry points (index + 7 commands)
        โ”œโ”€โ”€ config.py         # env-based configuration
        โ”œโ”€โ”€ path_utils.py     # path security & validation
        โ”œโ”€โ”€ vault_ops.py      # CRUD, batch, log, lint operations
        โ”œโ”€โ”€ migrate.py        # vault migration to LLM Wiki pattern
        โ”œโ”€โ”€ qdrant.py         # auto-start Qdrant Docker container
        โ”œโ”€โ”€ indexer.py        # markdown parsing, chunking, wikilinks, embedding
        โ””โ”€โ”€ server.py         # MCP server + 27 tools

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

obsidian_qdrant_search-0.4.2.tar.gz (888.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

obsidian_qdrant_search-0.4.2-py3-none-any.whl (42.6 kB view details)

Uploaded Python 3

File details

Details for the file obsidian_qdrant_search-0.4.2.tar.gz.

File metadata

  • Download URL: obsidian_qdrant_search-0.4.2.tar.gz
  • Upload date:
  • Size: 888.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for obsidian_qdrant_search-0.4.2.tar.gz
Algorithm Hash digest
SHA256 57ccd6b1d1b38a9bd59a43da244b346aa1a885199c3d138c286bee788dd8ae36
MD5 8c642ca21a9c47cf51f385124fe9d6bd
BLAKE2b-256 851071a8cb5d89ff43ab71cd22601aee36f070425d00612a11d7010f56bc2183

See more details on using hashes here.

File details

Details for the file obsidian_qdrant_search-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: obsidian_qdrant_search-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 42.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"25.10","id":"questing","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for obsidian_qdrant_search-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d3e1b606a41e3c455a2c3592e797c81b90768f9054fba12185ec2e5aadbda306
MD5 947bf17787db44eec0f70946287a8f96
BLAKE2b-256 3954eabc804fdd0843590141b727ee8d40ab8a882b5d471716182f949830ae3f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page