Skip to main content
Glama

claude-rag

Local RAG for Claude Code over a folder of Markdown files.

๐Ÿ‡ฌ๐Ÿ‡ง English ยท ๐Ÿ‡ฎ๐Ÿ‡น Italiano

claude-rag turns a folder of .md notes into a knowledge base that Claude Code can search. It runs entirely on your machine: no API keys, no cloud, no data leaving the PC.

  • Search tool: Claude calls kb_search("โ€ฆ") and gets back the most relevant sections, each with its source file and a confidence score.

  • Automatic recall (optional): hooks add matching notes to your prompt before Claude even sees it.

  • Measured, not guessed: a retrieval benchmark (eval/) backs every default.

How claude-rag works: indexing, search and hooks

๐Ÿ”Ž Interactive walkthrough: how kb_search finds an answer, step by step โ†’


Contents

  1. Quick start

  2. What Claude can do with it

  3. How it works

  4. Choosing the embedding model

  5. Optional: automatic recall with hooks

  6. Configuration

  7. Measuring retrieval quality

  8. Project layout

  9. Credits and license


Related MCP server: flightlog

Quick start

1. Clone the repo inside your KB folder. Your .md files live in the parent folder.

git clone https://github.com/sangelastro/claude-rag ~/.claude/my-kb/rag

2. Install the dependencies.

cd ~/.claude/my-kb/rag
pip install -r requirements.txt

3. Register the server in Claude Code (~/.claude.json, under mcpServers):

"my-kb": {
  "command": "python",
  "args": ["/absolute/path/to/rag/server.py"]
}

If the repo is not inside the KB folder, add "env": {"KB_RAG_DIR": "/absolute/path/to/your/kb"}.

4. Restart Claude Code. On first start the server downloads the embedding model (~120 MB) and indexes every .md file. After that, only changed files are re-indexed.


What Claude can do with it

Tool

What it does

kb_search(query, top_k=5)

Returns the top_k most relevant chunks, each with file, section and score. Warns when the answer is probably not in the KB

kb_reindex(force=False)

Re-indexes the files changed since the last run (force=True: all of them)

kb_stats()

Files indexed, chunks per file, last update

kb_savings()

Tokens saved: chunks served vs reading the whole files, split by tool and hook


How it works

The picture above has three parts.

โ‘  Indexing, at startup and on kb_reindex()

  1. Each .md file is split on its ## sections. Long sections become overlapping chunks (400 characters, 80 shared with the next one). YAML frontmatter is dropped; every chunk keeps a header with its file and section.

  2. Every chunk is turned into a vector by the embedding model.

  3. Text and vectors go into kb.db (SQLite). The plain text is also exported to kb_chunks.json for the hooks.

  4. Only files whose modification time changed are processed again.

โ‘ก Search, when Claude calls kb_search()

  1. BM25 ranks the chunks by exact words: table names, ports, hostnames, error codes.

  2. Semantic search ranks them by meaning (cosine similarity between vectors): paraphrases and natural-language questions.

  3. The two top-50 lists are merged with Reciprocal Rank Fusion.

  4. A local cross-encoder reranker re-reads the best 10 together with the question and puts them in order. Its top score is turned into a calibrated confidence; below 0.5 Claude is told the answer is probably not in the KB.

โ‘ข Hooks, optional and automatic: see below.


Choosing the embedding model

MiniLM (default)

EmbeddingGemma 2 (optional)

Model

paraphrase-multilingual-MiniLM-L12-v2

google/embeddinggemma-2, Apache-2.0

Reads per chunk

128 tokens

8K tokens, chunk header used as the document title

Vector size

384

768 (can be cut to 512, 256, 128)

Right section in top 5 (semantic only)

0.71

0.92

Tells "not in the KB" apart

69%

93%

Full reindex (6,900 chunks, 4 CPU threads)

a few minutes

~1 hour, once

Per search

a few ms

~0.5 s with the reranker

RAM per server

~0.5 GB

~2 GB

Runtime

fastembed (ONNX)

sentence-transformers + torch, in its own virtualenv

Start with MiniLM. Switch to EmbeddingGemma 2 if your questions are often in natural language, or if your notes are long and full of context.

Switching to EmbeddingGemma 2

python -m venv ~/.venvs/embgemma
~/.venvs/embgemma/Scripts/python -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
~/.venvs/embgemma/Scripts/python -m pip install -r requirements-gemma.txt

On Linux and macOS the interpreter is in bin/ instead of Scripts/. Then point the server at it:

"my-kb": {
  "command": "/home/you/.venvs/embgemma/Scripts/python",
  "args": ["/absolute/path/to/rag/server.py"],
  "env": {"KB_EMBED_MODEL": "embeddinggemma-2"}
}

Things to know:

  • The model is recorded in kb.db. When it changes, the next start re-embeds the whole KB instead of mixing vectors of different sizes.

  • Every Claude Code session starts its own server. Close or reconnect (/mcp โ†’ Reconnect) all open sessions after switching; a session still on the old settings would re-index the KB back to the old model.

  • The server keeps only the text part of Gemma in memory (271M of 744M parameters) and runs it in fp32 on CPU: bf16 is ~2.5ร— slower without hardware support, fp16 is not supported by the model.


Optional: automatic recall with hooks

Two Claude Code hooks add KB context without an explicit kb_search call. They use keyword matching on kb_chunks.json: ~10 ms, no model loaded.

Hook

Script

What it does

SessionStart

hooks/kb_session_start.py

Adds the KB table of contents at the start of each session

UserPromptSubmit

hooks/kb_recall.py

Adds the best-matching chunks before each prompt, within a size budget

Copy the sections of hooks/hooks_example.json into ~/.claude/settings.json and replace the paths:

{
  "hooks": {
    "SessionStart": [{"matcher": "*", "hooks": [
      {"type": "command", "command": "python /absolute/path/to/rag/hooks/kb_session_start.py"}]}],
    "UserPromptSubmit": [{"matcher": "*", "hooks": [
      {"type": "command", "command": "python /absolute/path/to/rag/hooks/kb_recall.py", "timeout": 5}]}]
  }
}

Configuration

All settings are environment variables in the env block of the server (or of the hooks).

Where the KB lives

Variable

Default

Meaning

KB_RAG_DIR

parent of server.py

Folder with the .md files

KB_RAG_DB

kb.db next to server.py

SQLite database

KB_RAG_NAME

kb-rag

MCP server name

Indexing and embedding

Variable

Default

Meaning

CHUNK_MAX_CHARS

400

Maximum chunk size; longer sections are split

CHUNK_OVERLAP

80

Characters shared by consecutive chunks

KB_EMBED_MODEL

minilm

minilm or embeddinggemma-2. Changing it re-embeds the whole KB

KB_EMBED_DIM

768

EmbeddingGemma 2 only: cut vectors to 512, 256 or 128

KB_EMBED_THREADS

4

EmbeddingGemma 2 only: CPU threads

Search

Variable

Default

Meaning

KB_SEARCH_MODE

hybrid

hybrid (BM25 + semantic) or semantic (cosine only)

KB_RERANK

mmarco-mminilm

Reranker: mmarco-mminilm (AVX-512), mmarco-mminilm-avx2 (older CPUs), bge-m3 (slower, not calibrated) or off. If it cannot load, search continues without it

KB_RERANK_CANDIDATES

10

Candidates passed to the reranker

KB_RERANK_THREADS

4

Reranker CPU threads

KB_RERANK_CACHE

~/.cache/claude-rag/models

Where reranker models are downloaded

KB_MIN_CONFIDENCE

0.5

Below this confidence kb_search warns that the answer is probably not in the KB

Hooks

Variable

Default

Meaning

KB_RAG_HOOK_TOP_K

3

Maximum chunks added per prompt

KB_RAG_HOOK_MIN_SCORE

0.15

Minimum score to add anything

KB_RAG_HOOK_MIN_WORDS

4

Prompts shorter than this are skipped

KB_RAG_HOOK_TOKEN_BUDGET

6000

Maximum characters added (~4 characters per token)


Measuring retrieval quality

eval/eval_retrieval.py checks how often the right section comes back, on a set of real questions labelled with their correct file and section. It compares every stage of the pipeline, only reads kb.db and records nothing.

cp eval/gold_set.example.json eval/gold_set.json   # then write questions about your KB
python eval/eval_retrieval.py              # add --no-rerank to skip the reranker

Add some questions the KB cannot answer ("kind": "neg"): the script also reports whether a score threshold tells them apart. --calibrate fits the reranker's confidence on your own data. KB_EVAL_GOLD and KB_RAG_DB point it to another gold set or database.

Results on the author's KB (6,885 chunks, 48 answerable and 7 unanswerable questions, October 2026). The cells show how often the right section is first / in the top 5, then the MRR:

Pipeline

MiniLM

EmbeddingGemma 2

semantic only

0.58 / 0.71 ยท 0.65

0.69 / 0.92 ยท 0.80

hybrid (BM25 + semantic)

0.65 / 0.83 ยท 0.74

0.73 / 0.90 ยท 0.81

hybrid + reranker (default)

0.77 / 0.92 ยท 0.83

0.79 / 0.90 ยท 0.83

How each stage got here, release by release, is in the CHANGELOG.


Project layout

rag/
โ”œโ”€โ”€ server.py                  MCP server: indexing, hybrid search, tools
โ”œโ”€โ”€ reranker.py                Local cross-encoder and calibrated confidence
โ”œโ”€โ”€ requirements.txt           Default dependencies (fastembed)
โ”œโ”€โ”€ requirements-gemma.txt     Extra dependencies for EmbeddingGemma 2
โ”œโ”€โ”€ hooks/
โ”‚   โ”œโ”€โ”€ kb_session_start.py    SessionStart hook
โ”‚   โ”œโ”€โ”€ kb_recall.py           UserPromptSubmit hook
โ”‚   โ””โ”€โ”€ hooks_example.json     Hook configuration template
โ”œโ”€โ”€ eval/
โ”‚   โ”œโ”€โ”€ eval_retrieval.py      Retrieval benchmark
โ”‚   โ””โ”€โ”€ gold_set.example.json  Gold set format (yours stays local)
โ””โ”€โ”€ docs/
    โ”œโ”€โ”€ architecture.svg       The diagram above (architecture.it.svg in Italian)
    โ””โ”€โ”€ how_search_works.html  Interactive walkthrough of kb_search

kb.db, kb_chunks.json, eval/gold_set.json and eval/results.json are created locally and excluded from git, because they contain your notes.

architecture.html and kb_rag_slides.html are older presentations written for release 1.3: they do not cover EmbeddingGemma 2.


Credits and license

The RAG system (chunking, embedding, SQLite vector store, MCP server, hybrid search, reranking, evaluation) is original work by Sergio Angelastro, released under the MIT License.

The hook system is inspired by agd-memory by @Pinperepette (MIT): see CREDITS.md.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that indexes Claude Code conversation history into SQLite, enabling full-text search across past sessions for context recovery and cross-agent observability.
    10
    3
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server that indexes Claude.ai chats and local Claude Code sessions, enabling semantic and keyword search across all your conversations with Claude.
    6
    34 PyPI
    5
    MIT