Content
# Obsidian Agentic RAG
> Turn your Obsidian vault into a native AI knowledge base for Claude Code.
A self-hosted RAG (Retrieval-Augmented Generation) server that indexes your Obsidian vault and exposes it as native MCP tools for Claude Code. No frameworks, no cloud dependencies, no API keys — everything runs locally.
## What makes it "Agentic"
Traditional RAG has one search endpoint. This server exposes **7 specialized tools** via MCP — Claude **decides which to use** based on your question:
| Tool | Best for | Example |
|------|----------|---------|
| `search_hybrid` | Complex questions | "How does our pricing compare to competitors?" |
| `search_semantic` | Conceptual questions | "What's our go-to-market strategy?" |
| `search_keyword` | Exact terms, names, dates | "LGPD article 11", "meeting Q3" |
| `get_full_note` | Reading a complete note | "Show me the compliance checklist" |
| `list_topics` | Vault navigation | "What topics do we have documented?" |
| `reindex` | After editing notes | "I just updated some notes" |
| `rag_status` | System health | "Is the RAG running?" |
## Features
- **Hybrid search** — Vector similarity + BM25 keyword + cross-encoder reranking
- **Portuguese-optimized** — RSLP stemmer for BM25, multilingual embedding & reranker
- **Incremental indexing** — Only re-processes changed files (MD5 hash comparison)
- **Auto-sync** — File watcher detects Obsidian changes, re-indexes automatically
- **MCP native** — Tools appear directly in Claude Code, no curl or API instructions needed
- **100% local** — Ollama embeddings, local cross-encoder, ChromaDB on disk
- **Docker-based** — One command to start, persists data across restarts
## Architecture
```
Obsidian Vault (.md files)
|
v
[Docker Container — port 8082]
|
|-- watchdog: detects file changes, triggers re-index
|-- indexer.py:
| |-- Chunks by ## headings (1200 char fallback)
| |-- Embeds via Ollama (qwen3-embedding, 4096d)
| |-- Stores in ChromaDB (cosine similarity)
| |-- Builds BM25 index (RSLP stemmed)
| |-- Incremental: skips unchanged files
|-- main.py: FastAPI with 8 endpoints
|
v
[MCP Server — mcp_server.py]
|
|-- Thin wrapper: translates MCP tool calls to HTTP
|-- Runs as subprocess of Claude Code (stdio transport)
|
v
Claude Code (native tools)
```
## Prerequisites
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
- [Ollama](https://ollama.com/) running on your machine
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI
- Python 3.10+ (for the MCP server, runs on host)
## Quick Start
### 1. Clone and configure
```bash
git clone https://github.com/mthehang/obsidian-agentic-rag.git
cd obsidian-agentic-rag
# Copy and edit the docker-compose
cp docker-compose.example.yml docker-compose.yml
# Edit docker-compose.yml: replace the vault path with yours
# volumes:
# - "/path/to/your/obsidian/vault:/vault:ro"
```
### 2. Pull the embedding model
```bash
ollama pull qwen3-embedding
```
### 3. Start the server
```bash
docker compose up --build -d
# Check logs (wait ~60s for first indexing)
docker logs obsidian-rag --tail 20
```
You should see:
```
[indexer] indexed 316 chunks from 29 files
[bm25] index built with 316 chunks (PT stemming)
[watcher] monitoring /vault (debounce=5s)
```
### 4. Install MCP tools for Claude Code
```bash
# Install MCP SDK on your host
pip install mcp httpx
# Register the MCP server (global — works in any directory)
# Add to ~/.mcp.json:
```
Create or edit `~/.mcp.json`:
```json
{
"mcpServers": {
"obsidian-rag": {
"command": "python",
"args": ["/absolute/path/to/obsidian-agentic-rag/mcp_server.py"]
}
}
}
```
### 5. Restart Claude Code
Close and reopen Claude Code. The tools will appear natively. Try:
```
> What topics do I have documented in my vault?
> Search for LGPD compliance requirements
> Show me the full business model note
```
## Configuration
All configuration is via environment variables in `docker-compose.yml`:
| Variable | Default | Description |
|----------|---------|-------------|
| `VAULT_PATH` | `/vault` | Path to vault inside container |
| `CHROMA_PATH` | `/data/chroma` | ChromaDB persistent storage |
| `OLLAMA_HOST` | `http://host.docker.internal:11434` | Ollama API URL |
| `EMBED_MODEL` | `qwen3-embedding` | Ollama embedding model (4096d, multilingual) |
| `CONTEXT_MODEL` | `""` (disabled) | LLM for contextual chunking (e.g., `gemma3:1b`) |
| `RERANK_ENABLED` | `true` | Enable cross-encoder reranking |
| `WATCHER_ENABLED` | `true` | Enable auto-sync file watcher |
| `WATCHER_DEBOUNCE` | `5` | Seconds to wait after last change before re-indexing |
### Changing the embedding model
If you switch embedding models, you must clear the ChromaDB volume (dimensions may differ):
```bash
docker compose down
docker volume rm obsidian-rag_rag-chroma-data
docker compose up -d
```
### Contextual Chunking (optional)
Prepends LLM-generated context to each chunk before embedding. Reduces retrieval failures by ~35% (Anthropic data) but adds indexing time.
```yaml
CONTEXT_MODEL: gemma3:1b # or any Ollama model
```
## API Endpoints
The server also exposes a REST API (useful for non-Claude integrations):
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/health` | Health check |
| `GET` | `/status` | System config & stats |
| `POST` | `/index` | Trigger re-indexing |
| `POST` | `/search` | Hybrid search (legacy) |
| `POST` | `/search_semantic` | Vector similarity search |
| `POST` | `/search_keyword` | BM25 keyword search |
| `POST` | `/search_hybrid` | Vector + BM25 + reranking |
| `POST` | `/get_full_note` | Full note by path |
| `GET` | `/list_topics` | List vault folders |
### Example
```bash
curl -s http://localhost:8082/search_hybrid \
-H "Content-Type: application/json" \
-d '{"query": "business model", "n_results": 3}' | python3 -m json.tool
```
## How Search Works
### Semantic Search (`/search_semantic`)
1. Query is embedded via Ollama (same model as indexed chunks)
2. ChromaDB finds top-N by cosine similarity
3. Returns results with similarity score (0-1)
### Keyword Search (`/search_keyword`)
1. Query is tokenized: lowercase, accent removal, RSLP stemming
2. BM25Okapi scores all chunks against query tokens
3. "prontuarios" matches "prontuario", "prontuarios", "prontuário"
### Hybrid Search (`/search_hybrid`)
1. Runs semantic (top-20) + keyword (top-20) in parallel
2. Merges and deduplicates results
3. Cross-encoder (`mmarco-mMiniLMv2-L12-H384-v1`) re-scores each pair (query, chunk)
4. Returns top-N by rerank score
### Incremental Indexing
1. On `/index`, computes MD5 hash of each `.md` file
2. Compares with stored hashes from previous run
3. Only re-chunks and re-embeds changed/new files
4. Deletes chunks from removed files
5. Rebuilds BM25 index from all chunks
### Auto-Sync (File Watcher)
1. `watchdog` monitors the vault directory recursively
2. On any `.md` change (create/modify/delete), starts a debounce timer
3. After 5 seconds of no changes, triggers incremental re-index
4. Multiple rapid edits are batched into a single re-index
## Tech Stack
| Component | Technology | Why |
|-----------|-----------|-----|
| API | FastAPI + Uvicorn | Fast, async, auto-docs |
| Vector DB | ChromaDB (embedded) | Zero-config, persistent, cosine search |
| Embedding | qwen3-embedding (Ollama) | 4096d, multilingual, free/local |
| BM25 | rank-bm25 + NLTK RSLP | Portuguese stemming, accent-insensitive |
| Reranking | mmarco-mMiniLMv2 (sentence-transformers) | Multilingual cross-encoder, trained on PT-BR |
| File Watch | watchdog | Cross-platform, recursive monitoring |
| MCP | FastMCP (Python SDK) | Native Claude Code integration |
| Container | Docker + docker-compose | Portable, one-command setup |
## Limitations
- **BM25 in-memory** — Rebuilt on each startup. Fine for <10K chunks.
- **No auth** — API is open on localhost. Don't expose to the internet.
- **Single collection** — One vault per container. Run multiple containers for multiple vaults.
- **Reranker download** — First startup downloads ~134MB cross-encoder model from HuggingFace.
- **ChromaDB telemetry warnings** — Harmless log messages about failed analytics in container.
## Tested With
- Windows 11 + Docker Desktop + WSL2
- Ollama 0.9.x with `qwen3-embedding`
- Claude Code (Opus 4.6)
- Obsidian vault with 29 files / 316 chunks (Portuguese content)
- Python 3.11 (container) + 3.12 (host MCP server)
## Author
Created by **Matheus Godoi** ([@mthehang](https://github.com/mthehang)) — co-founder of [ClueMed](https://cluemed.com.br).
Built with the assistance of Claude Code (Opus 4.6).
## License
MIT — see [LICENSE](LICENSE).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
Python tool for converting files and office documents to Markdown.
awesome-claude-skills
A curated list of awesome Claude Skills, resources, and tools for...
antigravity-awesome-skills
The Ultimate Collection of 130+ Agentic Skills for Claude...
context-mode
MCP is the protocol for tool access. We're the virtualization layer for context.
claude-context-mode
claude-context-mode plugin reduces MCP context bloat, saving up to 99% of tokens.