Knowledge RAG

PyPI NPM PyPI Downloads Python License Platform GPU CI CodeQL Quality Gate Glama Score

Your docs, your machine, zero cloud. Claude Code searches them natively.

Drop your PDFs, markdown, code, notebooks — 1800+ files, 39K chunks, indexed in under 3 minutes. Hybrid search (BM25 + semantic vectors + cross-encoder reranking) through 13 MCP tools. Everything runs locally via ONNX. No Docker, no Ollama, no API keys, no data leaves your machine.

pip install knowledge-rag → restart Claude Code → search_knowledge("your query")

13 MCP Tools | Hybrid Search + Reranking | 20 File Formats | Optional NVIDIA GPU | 100% Local

What's New | Supported Formats | Installation | Configuration | API Reference | Architecture


Star History


What's New in v4.2.0

Search Performance & Output Quality (v4.2.0)

128× faster BM25 search — replaced rank-bm25 full-corpus scan with a custom inverted-index implementation. Only documents containing query terms are scored, using numpy.argpartition for O(n) top-k selection. Adjacent chunk fetching now uses a single batched ChromaDB call instead of N round-trips, and an O(1) reverse lookup (_source_to_docid) eliminates linear scans.

Smarter output — two new parameters on search_knowledge:

  • snippet_mode (default: true) — truncates content to ~500 characters at natural break points, reducing token consumption by ~72%. Adds content_length field with original size; use get_document() for full content.
  • min_score — filters results below a normalized relevance threshold (0.0–1.0). Eliminates low-quality noise from results. Response includes filtered_by_score count for transparency.

Both parameters are fully backwards-compatible (existing callers see no change in behavior).

Enterprise Concurrent Access — SSE/HTTP Transport (v4.0.0)

The server now supports SSE and streamable-http transport modes. Instead of spawning a separate process per client (stdio), a single server process serves all clients with shared resources — 1 embedding model, 1 ChromaDB, 1 query cache.

# config.yaml
server:
  transport: "sse"        # "stdio" | "sse" | "streamable-http"
  host: "127.0.0.1"
  port: 8179

Or via CLI: knowledge-rag --transport sse

Optional enterprise features (all disabled by default):

  • Rate limiting: Sliding-window counter, configurable RPM and burst
  • Prometheus metrics: /metrics endpoint on separate port
  • Bearer auth: Token validation for SSE/HTTP connections

All 13 MCP tools are instrumented with @rate_limited and @instrument decorators — zero overhead when features are disabled. Default transport remains stdio for full backwards compatibility.

Migration: Existing users need zero changes. SSE mode is opt-in via server.transport: "sse" in config.yaml. See Configuration for details.

Quality Gate — 7-Pillar PR Validation

Every PR (including dependabot bumps and one-line fixes) is now evaluated against 35+ automated checks spread across 7 pillars before any human review:

Pillar What it enforces Tools
1 Security SAST, secrets, CVEs, supply chain bandit, semgrep, gitleaks, pip-audit, dependency-review, Snyk, CodeQL, Socket
2 Stability Flake detection, coverage trend, test count, deterministic runs pytest-rerunfailures, codecov ±0.5pp, test-count guard
3 Memory Leak RSS bounded under 1000-query load, no idle bloat psutil-based baseline tests + nightly 50K-iteration soak
4 Versatility 9 OS×Python combos, 14 format parsers, 4 config presets, locale tolerance, property-based fuzzing matrix CI on Linux+Windows+macOS × 3.11+3.12+3.13, Hypothesis
5 Scalability Performance regression > 10% blocks merge, public bench dashboard pytest-benchmark, GH Pages chart
6 Versioning Atomic version sync, API surface diff, conventional commits, CHANGELOG enforcement, backwards compat griffe-style AST diff, custom guards
7 Quality Type strictness, docstring coverage, complexity, dead code mypy strict, interrogate ≥80%, radon, vulture

Plus a nightly resilience workflow that runs chaos failure-injection (HF down, ChromaDB corruption, watchdog crash, ONNX zero-byte replay), determinism check (full suite × 3), and mutation testing on selected modules.

Read the full philosophy in CONTRIBUTING.md. Report bugs via SECURITY.md or the issue templates.

Critical Hotfix — No More Silent Zero-Vector Corruption (v3.8.1)

FastEmbedEmbeddings.__call__ no longer swallows exceptions and returns [[0.0]*dim, ...] when the ONNX model fails to load. That bug pre-existed in master but was silent: ChromaDB happily stored zero embeddings, count() reported normal numbers, smart-reindex skipped them as "already indexed", and queries returned garbage similarity with no error visible. Now raises EmbeddingModelLoadError / EmbeddingError loudly. All v3.8.0 users should upgrade. Full details in Changelog.

Lazy-Loaded Embeddings — Cheaper Idle Processes (v3.8.0)

The FastEmbed ONNX model (~200MB resident) now loads on the first query, not at startup. Idle knowledge-rag processes are now genuinely cheap. Why this matters: MCP stdio is one-process-per-client by protocol — multiple Claude Code windows, Claude Desktop + IDE simultaneously, or review/approval flows that open extra connections all spawn their own processes. Before v3.8.0, every one of them paid the full embedding-model cost up front. Now only processes that actually serve queries load the model. Public API is unchanged.

Opt-In Single-Instance Guard (v3.8.0)

For users who measured their setup and want a hard cap of one server per data_dir:

export KNOWLEDGE_RAG_SINGLE_INSTANCE=1

A second instance exits immediately with code 75. OFF by default so multi-client MCP usage continues to work unchanged. Stale-PID recovery + SIGINT/SIGTERM cleanup wired correctly. Full guide in docs/single-instance.md. Sample MCP config in examples/mcp-config-single-instance.json.

5 Ways to Install

npx -y knowledge-rag                    # NPM — zero setup, auto-manages Python venv
pip install knowledge-rag               # PyPI — classic Python install
curl -fsSL .../install.sh | bash        # One-line installer (Linux/macOS/Windows)
docker pull ghcr.io/lyonzin/knowledge-rag  # Docker — models pre-downloaded
git clone ... && pip install -r ...     # From source

All methods produce the same MCP server. See Installation for full instructions.

Recent Highlights

  • v4.0.0Enterprise concurrent access: SSE/HTTP transport (1 server → N clients), thread-safe shared state, optional rate limiting + Prometheus metrics, ChromaDB WAL mode, --transport CLI
  • v3.9.0Quality Gate activated: 35+ automated PR checks across 7 pillars (Security, Stability, Memory Leak, Versatility, Scalability, Versioning, Quality) + nightly resilience suite (chaos, soak, determinism, mutation)
  • v3.8.1 — Critical hotfix: loud-fail embeddings (no more silent zero-vector corruption); Windows CI flake erradicated (HF_HUB_OFFLINE + shell:bash + atexit wrapper)
  • v3.8.0 — Lazy-load embeddings, opt-in single-instance guard, version sync across PyPI/NPM/Docker
  • v3.6.0 — Multi-language code parsing (C/C++/JS/TS/XML), NPM wrapper, Docker image, automated release pipeline
  • v3.5.2 — CUDA DLL auto-discovery from pip packages, graceful GPU→CPU fallback, explicit CPU provider (no CUDA noise when gpu: false), BASE_DIR resolution fix for editable installs
  • v3.5.1 — Remove Python <3.13 upper bound — 3.13 and 3.14 now supported
  • v3.5.0 — Optional GPU acceleration, supported formats table, full README rewrite
  • v3.4.3 — MCP stdout save/restore fix (v3.4.2 broke JSON-RPC responses)
  • v3.4.0 — Persistent model cache, exclude patterns, Jupyter Notebook parser, inotify resilience, MetaTrader support

See Changelog for full history.


Supported Formats

Format Extension Parser Default Notes
Markdown .md Section-aware (splits at ##) Yes Headers preserved as chunk boundaries
Plain Text .txt Fixed-size chunking Yes 1000 chars + 200 overlap
PDF .pdf PyMuPDF extraction Yes Text-based PDFs only (no OCR)
Python .py Code-aware parser Yes Functions/classes as chunks
JSON .json Structure-aware Yes Flattened key-value extraction
CSV .csv Row-based parser Yes Headers + rows as text
Word .docx python-docx Yes Headings preserved as markdown
Excel .xlsx openpyxl Yes Sheet-by-sheet extraction
PowerPoint .pptx python-pptx Yes Slide-by-slide extraction
Jupyter Notebook .ipynb Cell-aware parser Yes Markdown + code cells only, no outputs/base64
C Source .c Code-aware parser Yes Functions/structs/includes extracted
C/C++ Header .h Code-aware parser Yes Function declarations/structs extracted
C++ Source .cpp Code-aware parser Yes Classes/structs/includes extracted
JavaScript .js Code-aware parser Yes Functions/classes/imports (ESM + CJS)
React JSX .jsx Code-aware parser Yes Same as JS parser
TypeScript .ts Code-aware parser Yes Functions/classes/interfaces/enums/imports
React TSX .tsx Code-aware parser Yes Same as TS parser
XML .xml XML parser Yes Root element and namespace extraction
MQL4 Header .mqh Code parser No MetaTrader — add to supported_formats to enable
MQL4 Source .mq4 Code parser No MetaTrader — add to supported_formats to enable

Tip: The parser dispatch is extensible. Any format mapped in _parsers can be enabled via supported_formats in config.yaml.


Features

Feature Description
Hybrid Search Semantic + BM25 keyword search with Reciprocal Rank Fusion
Cross-Encoder Reranker Xenova/ms-marco-MiniLM-L-6-v2 re-scores top candidates for precision
GPU Acceleration Optional ONNX CUDA support for 5-10x faster indexing
YAML Configuration Fully customizable via config.yaml with domain-specific presets
Query Expansion Configurable synonym mappings (69 security-term defaults)
Markdown-Aware Chunking .md files split by ##/### sections instead of fixed windows
In-Process Embeddings FastEmbed ONNX Runtime (BAAI/bge-small-en-v1.5, 384D)
Keyword Routing Word-boundary aware routing for domain-specific queries
20 Format Parsers MD, TXT, PDF, PY, C, H, CPP, JS, JSX, TS, TSX, JSON, XML, CSV, DOCX, XLSX, PPTX, IPYNB + opt-in MQH/MQ4
Category Organization Organize docs by folder, auto-tagged by path
Incremental Indexing Change detection via mtime/size — only re-indexes modified files
Chunk Deduplication SHA256 content hashing prevents duplicate chunks
Query Cache LRU cache with 5-min TTL for instant repeat queries
Document CRUD Add, update, remove documents via MCP tools
URL Ingestion Fetch URLs, strip HTML, convert to markdown, index
Similarity Search Find documents similar to a reference document
Retrieval Evaluation Built-in MRR@5 and Recall@5 metrics
File Watcher Auto-reindex on document changes via watchdog (5s debounce)
Exclude Patterns Glob-based file/directory exclusion during indexing
MMR Diversification Maximal Marginal Relevance reduces redundant results
Persistent Model Cache Embedding models cached in models_cache/ — survives reboots
Auto-Migration Detects embedding dimension mismatch and rebuilds automatically
13 MCP Tools Full CRUD + search + evaluation via Claude Code

Architecture

System Overview

flowchart TB
    subgraph MCP["MCP SERVER (FastMCP)"]
        direction TB
        TOOLS["13 MCP Tools<br/>search | get | add | update | remove<br/>reindex | reindex_status | list | stats | url | similar | evaluate"]
    end

    subgraph SEARCH["HYBRID SEARCH ENGINE"]
        direction LR
        ROUTER["Keyword Router<br/>(word boundaries)"]
        SEMANTIC["Semantic Search<br/>(ChromaDB)"]
        BM25["BM25 Keyword<br/>(inverted-index + expansion)"]
        RRF["Reciprocal Rank<br/>Fusion (RRF)"]
        RERANK["Cross-Encoder<br/>Reranker"]

        ROUTER --> SEMANTIC
        ROUTER --> BM25
        SEMANTIC --> RRF
        BM25 --> RRF
        RRF --> RERANK
    end

    subgraph STORAGE["STORAGE LAYER"]
        direction LR
        CHROMA[("ChromaDB<br/>Vector Database")]
        COLLECTIONS["Collections<br/>security | ctf<br/>logscale | development"]
        CHROMA --- COLLECTIONS
    end

    subgraph EMBED["EMBEDDINGS (In-Process)"]
        FASTEMBED["FastEmbed ONNX<br/>BAAI/bge-small-en-v1.5<br/>(384D, CPU or GPU)"]
        CROSSENC["Cross-Encoder<br/>ms-marco-MiniLM-L-6-v2"]
        FASTEMBED --- CROSSENC
    end

    subgraph INGEST["DOCUMENT INGESTION"]
        PARSERS["20 Parsers<br/>MD | PDF | TXT | PY | C | H | CPP | JS | JSX | TS | TSX | JSON | XML | CSV<br/>DOCX | XLSX | PPTX | IPYNB | MQH | MQ4"]
        CHUNKER["Chunking<br/>MD: section-aware<br/>Other: 1000 chars + 200 overlap"]
        PARSERS --> CHUNKER
    end

    CLAUDE["Claude Code"] --> MCP
    MCP --> SEARCH
    SEARCH --> STORAGE
    STORAGE --> EMBED
    INGEST --> EMBED
    EMBED --> STORAGE

Query Processing Flow

flowchart TB
    QUERY["User Query<br/>'mimikatz credential dump'"] --> EXPAND

    subgraph EXPANSION["Query Expansion"]
        EXPAND["Synonym Expansion<br/>mimikatz -> mimikatz, sekurlsa, logonpasswords"]
    end

    EXPAND --> ROUTER

    subgraph ROUTING["Keyword Routing"]
        ROUTER["Keyword Router"]
        MATCH{"Word Boundary<br/>Match?"}
        CATEGORY["Filter: redteam"]
        NOFILTER["No Filter"]

        ROUTER --> MATCH
        MATCH -->|Yes| CATEGORY
        MATCH -->|No| NOFILTER
    end

    subgraph HYBRID["Hybrid Search"]
        direction LR
        SEMANTIC["Semantic Search<br/>(ChromaDB embeddings)<br/>Conceptual similarity"]
        BM25["BM25 Inverted-Index<br/>(posting lists + numpy top-k)<br/>Exact term matching"]
    end

    subgraph FUSION["Result Fusion + Reranking"]
        RRF["Reciprocal Rank Fusion<br/>score = alpha * 1/(k+rank_sem)<br/>+ (1-alpha) * 1/(k+rank_bm25)"]
        RERANK["Cross-Encoder Reranker<br/>Re-scores top 3x candidates<br/>query+doc pair scoring"]
        SORT["Sort by Reranker Score<br/>Normalize to 0-1"]
        ADJ["Adjacent Chunk Expansion<br/>(batch fetch ±1 chunk)"]

        RRF --> RERANK --> SORT --> ADJ
    end

    subgraph OUTPUT["Output Processing"]
        MINSCORE["min_score Filter<br/>(discard below threshold)"]
        SNIPPET["snippet_mode Truncation<br/>(~500 chars at natural break)"]

        MINSCORE --> SNIPPET
    end

    CATEGORY --> HYBRID
    NOFILTER --> HYBRID
    SEMANTIC --> RRF
    BM25 --> RRF

    ADJ --> MINSCORE
    SNIPPET --> RESULTS["Results<br/>search_method: hybrid|semantic|keyword<br/>score + filtered_by_score + content_length"]

Document Ingestion Flow

flowchart LR
    subgraph INPUT["Input"]
        FILES["documents/<br/>├── security/<br/>├── development/<br/>├── ctf/<br/>└── general/"]
    end

    subgraph PARSE["Parse (20 formats)"]
        MD["Markdown"]
        PDF["PDF<br/>(PyMuPDF)"]
        OFFICE["DOCX | XLSX<br/>PPTX | CSV"]
        CODE["PY | C | H | CPP | JS | JSX<br/>TS | TSX | JSON | XML | IPYNB"]
    end

    subgraph CHUNK["Chunk"]
        MDSPLIT["MD: Section-Aware<br/>Split at ## headers"]
        TXTSPLIT["Other: Fixed-Size<br/>1000 chars + 200 overlap"]
        DEDUP["SHA256 Dedup<br/>Skip duplicate content"]
    end

    subgraph EMBED["Embed"]
        FASTEMBED["FastEmbed ONNX<br/>bge-small-en-v1.5<br/>(384D, CPU or GPU)"]
    end

    subgraph STORE["Store"]
        CHROMADB[("ChromaDB")]
        BM25IDX["BM25 Index"]
    end

    FILES --> MD & PDF & OFFICE & CODE
    MD --> MDSPLIT
    PDF & OFFICE & CODE --> TXTSPLIT
    MDSPLIT --> DEDUP
    TXTSPLIT --> DEDUP
    DEDUP --> EMBED
    EMBED --> STORE

hybrid_alpha Parameter Effect

flowchart LR
    subgraph ALPHA["hybrid_alpha values"]
        A0["0.0<br/>Pure BM25<br/>Instant"]
        A3["0.3 (default)<br/>Keyword-heavy<br/>Fast"]
        A5["0.5<br/>Balanced"]
        A7["0.7<br/>Semantic-heavy"]
        A10["1.0<br/>Pure Semantic"]
    end

    subgraph USE["Best For"]
        U0["CVEs, tool names<br/>exact matches"]
        U3["Technical queries<br/>specific terms"]
        U5["General queries"]
        U7["Conceptual queries<br/>related topics"]
        U10["'How to...' questions<br/>conceptual search"]
    end

    A0 --- U0
    A3 --- U3
    A5 --- U5
    A7 --- U7
    A10 --- U10

Installation

Prerequisites

  • Python 3.11+
  • Claude Code CLI
  • …or any other MCP client (Claude Desktop, Cursor, VS Code, Antigravity, opencode, Windsurf) — see Use with other MCP clients
  • ~200MB disk for model cache (auto-downloaded on first run)
  • Optional: NVIDIA GPU + CUDA 12 for accelerated embeddings (see GPU Acceleration below)

GPU Acceleration

GPU mode accelerates embedding generation during indexing and search. It requires an NVIDIA GPU with CUDA 12 support. No GPU? No problem — the server runs on CPU by default and GPU is entirely optional.

Requirements:

Component Minimum How to check / get it
NVIDIA GPU (Turing+) RTX 20xx / 30xx / 40xx / 50xx, or Tesla T4+ nvidia-smi
NVIDIA Driver ≥ 525 nvidia-sminvidia.com/drivers
CUDA 12 runtime Provided by pip packages below Automatic

Setup (2 steps):

# 1. Install GPU dependencies (onnxruntime-gpu + all CUDA 12 runtime DLLs)
pip install knowledge-rag[gpu]

# 2. Enable in config.yaml
# models:
#   embedding:
#     gpu: true

The [gpu] extra installs onnxruntime-gpu plus 7 NVIDIA CUDA 12 packages (cublas, cudnn, cuda-runtime, cufft, cusparse, cusolver, curand, nvjitlink) so you don't need a full CUDA Toolkit install.

Verify GPU is active:

On server startup, look for the GPU status banner:

============================================================
  GPU STATUS: ACTIVE
  Provider:   CUDAExecutionProvider
  Device:     NVIDIA GeForce RTX 3080 Ti
  VRAM:       12.0 GB
============================================================

Or programmatically:

python -c "import onnxruntime; print(onnxruntime.get_available_providers())"
# Should include: 'CUDAExecutionProvider'

Fallback: If CUDA is unavailable at runtime (wrong driver, missing DLLs, no GPU), the server falls back to CPU automatically with a [WARN] log — it never crashes. The gpu: true config is a preference, not a requirement.

Install Methods

Pick one — all produce the same running server.

Option A: NPX (fastest)

Requires Node.js 16+. Handles Python venv, pip install, and version upgrades automatically.

claude mcp add knowledge-rag -s user -- npx -y knowledge-rag

That's it. On first run, npx creates a venv at ~/.knowledge-rag/, installs the PyPI package, and starts the MCP server. Subsequent runs reuse the cached venv.

Option B: One-line installer

# Linux/macOS:
curl -fsSL https://raw.githubusercontent.com/lyonzin/knowledge-rag/master/install.sh | bash

# Windows (PowerShell):
irm https://raw.githubusercontent.com/lyonzin/knowledge-rag/master/install.ps1 | iex

Then configure Claude Code:

claude mcp add knowledge-rag -s user -- ~/knowledge-rag/venv/bin/python -m mcp_server.server

Windows: claude mcp add knowledge-rag -s user -- %USERPROFILE%\knowledge-rag\venv\Scripts\python.exe -m mcp_server.server

Option C: pip install

mkdir ~/knowledge-rag && cd ~/knowledge-rag
python3 -m venv venv && source venv/bin/activate
pip install knowledge-rag
knowledge-rag init              # Exports config template, presets, creates documents/

Then configure Claude Code:

claude mcp add knowledge-rag -s user -- ~/knowledge-rag/venv/bin/python -m mcp_server.server

Windows users: Use python instead of python3, venv\Scripts\activate instead of source venv/bin/activate. Windows path: claude mcp add knowledge-rag -s user -- %USERPROFILE%\knowledge-rag\venv\Scripts\python.exe -m mcp_server.server

Option D: Clone from source

git clone https://github.com/lyonzin/knowledge-rag.git ~/knowledge-rag
cd ~/knowledge-rag
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt

Then configure Claude Code:

claude mcp add knowledge-rag -s user -- ~/knowledge-rag/venv/bin/python -m mcp_server.server

Option E: Docker

docker pull ghcr.io/lyonzin/knowledge-rag:latest
claude mcp add knowledge-rag -s user -- \
  docker run -i --rm \
  -v ~/knowledge-rag/documents:/app/documents \
  -v ~/knowledge-rag/data:/app/data \
  ghcr.io/lyonzin/knowledge-rag:latest

Models are pre-downloaded in the image — no first-run delay.

Add to ~/.claude.json:

Windows:

{
  "mcpServers": {
    "knowledge-rag": {
      "command": "C:\\Users\\YOUR_USER\\knowledge-rag\\venv\\Scripts\\python.exe",
      "args": ["-m", "mcp_server.server"]
    }
  }
}

Linux / macOS:

{
  "mcpServers": {
    "knowledge-rag": {
      "command": "/home/YOUR_USER/knowledge-rag/venv/bin/python",
      "args": ["-m", "mcp_server.server"]
    }
  }
}

Replace YOUR_USER with your username, or use the full path from echo $HOME.

Option F: SSE Server Mode (multi-agent)

For multi-agent setups where multiple clients query the same knowledge base simultaneously:

pip install knowledge-rag[server]    # Adds uvicorn for SSE/HTTP
knowledge-rag --transport sse        # Starts on http://127.0.0.1:8179

Then configure each MCP client to connect via SSE:

{
  "mcpServers": {
    "knowledge-rag": {
      "type": "sse",
      "url": "http://127.0.0.1:8179/sse"
    }
  }
}

One server process serves all agents — shared embedding model, shared cache, shared ChromaDB. See Configuration > Server for rate limiting, metrics, and auth options.

Use with other MCP clients

knowledge-rag supports both stdio (default, 1:1) and SSE (1:N) transport modes. In stdio mode, it works with any MCP-compatible client, not only Claude Code. The launch command is the same everywhere (the python -m mcp_server.server from whichever install method you picked); only the config file location and JSON shape differ per client.

Clients using the standard mcpServers format

For Claude Desktop, Cursor, Antigravity, and Windsurf, use the same block — only the file location changes:

{
  "mcpServers": {
    "knowledge-rag": {
      "command": "/home/YOUR_USER/knowledge-rag/venv/bin/python",
      "args": ["-m", "mcp_server.server"]
    }
  }
}

Windows: set command to the full path of venv\Scripts\python.exe.

Client Config file Notes
Claude Code use claude mcp add … (see install methods above) The CLI writes ~/.claude.json for you — manual edits to it aren't reliably picked up.
Claude Desktop macOS: ~/Library/Application Support/Claude/claude_desktop_config.json · Windows: %APPDATA%\Claude\claude_desktop_config.json Easiest: Settings → Developer → Edit Config opens the correct file (avoids the Windows Store/MSIX path quirk).
Cursor ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project)
Antigravity macOS/Linux: ~/.gemini/antigravity/mcp_config.json · Windows: %USERPROFILE%\.gemini\antigravity\mcp_config.json Open via Agent panel → "…" → Manage MCP Servers → View raw config.
Windsurf ~/.codeium/windsurf/mcp_config.json (global only) Easiest: Cascade panel → MCP → View raw config.

VS Code — uses a servers key

VS Code (Copilot MCP) nests servers under servers, not mcpServers. Put this in .vscode/mcp.json (workspace) or the file opened by the MCP: Open User Configuration command:

{
  "servers": {
    "knowledge-rag": {
      "type": "stdio",
      "command": "/home/YOUR_USER/knowledge-rag/venv/bin/python",
      "args": ["-m", "mcp_server.server"]
    }
  }
}

opencode — uses an mcp key

opencode nests servers under mcp, takes command as a single array, and uses environment instead of env. Put this in opencode.json (project root) or ~/.config/opencode/opencode.json (global):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "knowledge-rag": {
      "type": "local",
      "command": ["/home/YOUR_USER/knowledge-rag/venv/bin/python", "-m", "mcp_server.server"],
      "enabled": true
    }
  }
}

Any other MCP client: point it at the same command + args (…/venv/bin/python -m mcp_server.server). If it speaks stdio MCP, knowledge-rag works — only the config file's location and key naming differ. Check your client's docs for the exact path.

Verify

claude mcp list

On first start, the server will:

  1. Download the embedding model (~50MB, cached in models_cache/)
  2. Auto-index any documents in the documents/ directory
  3. Start watching for file changes (auto-reindex)

Usage

Adding Documents

Place your documents in the documents/ directory, organized by category:

documents/
├── security/          # Pentest, exploit, vulnerability docs
├── development/       # Code, APIs, frameworks
├── ctf/               # CTF writeups and methodology
├── logscale/          # LogScale/LQL documentation
└── general/           # Everything else

Or add documents programmatically via MCP tools:

# Add from content
add_document(
    content="# My Document\n\nContent here...",
    filepath="security/my-technique.md",
    category="security"
)

# Add from URL
add_from_url(
    url="https://example.com/article",
    category="security",
    title="Custom Title"
)

Searching

Claude uses the RAG system automatically when configured. You can also control search behavior:

# Pure keyword search — instant, no embedding needed
search_knowledge("gtfobins suid", hybrid_alpha=0.0)

# Keyword-heavy (default) — fast, slight semantic boost
search_knowledge("mimikatz", hybrid_alpha=0.3)

# Balanced hybrid — both engines equally weighted
search_knowledge("SQL injection techniques", hybrid_alpha=0.5)

# Semantic-heavy — better for conceptual queries
search_knowledge("how to escalate privileges", hybrid_alpha=0.7)

# Pure semantic — embedding similarity only
search_knowledge("lateral movement strategies", hybrid_alpha=1.0)

Indexing

Documents are automatically indexed on first startup. All reindex operations run in background — they return immediately and you poll progress via get_reindex_status():

# Incremental: only re-index changed files (fast)
reindex_documents()

# Smart reindex: detect changes + rebuild BM25
reindex_documents(force=True)

# Nuclear rebuild: delete everything, re-embed all (use after model change)
reindex_documents(full_rebuild=True)

# Poll progress (lightweight, no full stats computation)
get_reindex_status()
# → {"reindex": {"active": true, "percent": 56, "progress": "2090/3734", ...}}

Evaluating Retrieval Quality

evaluate_retrieval(test_cases='[
    {"query": "sql injection", "expected_filepath": "security/sqli-guide.md"},
    {"query": "privilege escalation", "expected_filepath": "security/privesc.md"}
]')
# Returns: MRR@5, Recall@5, per-query results

API Reference

Search & Query

search_knowledge

Hybrid search combining semantic search + BM25 keyword search with cross-encoder reranking.

Parameter Type Default Description
query string required Search query text (1-3 keywords recommended)
max_results int 5 Maximum results to return (1-20)
category string null Filter by category
hybrid_alpha float 0.3 Balance: 0.0 = keyword only, 1.0 = semantic only
min_score float 0.0 Minimum relevance score (0.0-1.0) to include a result. Use 0.2-0.4 to cut noise
snippet_mode bool true Truncate content to ~500 chars at natural break points. Adds content_length field

Returns:

{
  "status": "success",
  "query": "mimikatz credential dump",
  "hybrid_alpha": 0.5,
  "result_count": 3,
  "filtered_by_score": 2,
  "cache_hit_rate": "0.0%",
  "results": [
    {
      "content": "Mimikatz can extract credentials from memory...",
      "source": "documents/security/credential-attacks.md",
      "filename": "credential-attacks.md",
      "category": "security",
      "score": 0.9823,
      "raw_rrf_score": 0.016393,
      "reranker_score": 0.987654,
      "semantic_rank": 2,
      "bm25_rank": 1,
      "search_method": "hybrid",
      "keywords": ["mimikatz", "credential", "lsass"],
      "routed_by": "redteam"
    }
  ]
}

Search Method Values:

  • hybrid: Found by both semantic and BM25 search (highest confidence)
  • semantic: Found only by semantic search
  • keyword: Found only by BM25 keyword search

get_document

Retrieve the full content of a specific document.

Parameter Type Description
filepath string Path to the document file

Returns: JSON with document content, metadata, keywords, and chunk count.


reindex_documents

Index or reindex all documents in the knowledge base. Runs in background — returns immediately. Poll progress via get_reindex_status().

Parameter Type Default Description
force bool false Smart reindex: detects changes, rebuilds BM25. Fast.
full_rebuild bool false Nuclear rebuild: deletes everything, re-embeds all documents. Use after model change.

Returns: {"status": "started", "operation": "..."} immediately. If already running, returns {"status": "already_running", "progress": "1200/3734"}.


get_reindex_status

Get the current status of a background reindex operation. Lightweight — does not compute full index statistics.

Returns (active):

{
  "status": "success",
  "reindex": {
    "active": true,
    "operation": "nuclear_rebuild",
    "progress": "1200/3734",
    "percent": 32,
    "indexed": 1200,
    "skipped": 0,
    "errors": 0,
    "started_at": "2026-06-17T18:29:49"
  }
}

Returns (idle): {"status": "success", "reindex": {"active": false}}


list_categories

List all document categories with their document counts.

Returns:

{
  "status": "success",
  "categories": {
    "security": 52,
    "development": 8,
    "ctf": 12,
    "general": 3
  },
  "total_documents": 75
}

list_documents

List all indexed documents, optionally filtered by category.

Parameter Type Description
category string Optional category filter

Returns: JSON array of documents with id, source, category, format, chunks, and keywords.


get_index_stats

Get statistics about the knowledge base index.

Returns:

{
  "status": "success",
  "stats": {
    "total_documents": 75,
    "total_chunks": 9256,
    "categories": {"security": 52, "development": 8},
    "supported_formats": [".md", ".txt", ".pdf", ".py", ".json", ".docx", ".xlsx", ".pptx", ".csv", ".ipynb"],
    "embedding_model": "BAAI/bge-small-en-v1.5",
    "embedding_dim": 384,
    "reranker_model": "Xenova/ms-marco-MiniLM-L-6-v2",
    "chunk_size": 1000,
    "chunk_overlap": 200,
    "query_cache": {
      "size": 12,
      "max_size": 100,
      "ttl_seconds": 300,
      "hits": 45,
      "misses": 23,
      "hit_rate": "66.2%"
    }
  }
}

Document Management

add_document

Add a new document to the knowledge base from raw content. Saves the file to the documents directory and indexes it immediately.

Parameter Type Default Description
content string required Full text content of the document
filepath string required Relative path within documents dir (e.g., security/new-technique.md)
category string "general" Document category

update_document

Update an existing document. Removes old chunks from the index and re-indexes with new content.

Parameter Type Description
filepath string Full path to the document file
content string New content for the document

remove_document

Remove a document from the knowledge base index. Optionally deletes the file from disk.

Parameter Type Default Description
filepath string required Path to the document file
delete_file bool false If true, also delete the file from disk

add_from_url

Fetch content from a URL, strip HTML (scripts, styles, nav, footer, header), convert to markdown, and add to the knowledge base.

Parameter Type Default Description
url string required URL to fetch content from
category string "general" Document category
title string null Custom title (auto-detected from <title> tag if not provided)

search_similar

Find documents similar to a given document using embedding similarity.

Parameter Type Default Description
filepath string required Path to the reference document
max_results int 5 Number of similar documents to return (1-20)

evaluate_retrieval

Evaluate retrieval quality with test queries. Useful for tuning hybrid_alpha, testing query expansion effectiveness, or validating after reindexing.

Parameter Type Description
test_cases string (JSON) Array of test cases: [{"query": "...", "expected_filepath": "..."}, ...]

Metrics:

  • MRR@5 (Mean Reciprocal Rank): Average of 1/rank for expected documents. 1.0 = always first result.
  • Recall@5: Fraction of expected documents found in top 5 results. 1.0 = all found.

Configuration

Knowledge RAG is fully configurable via a config.yaml file in the project root. If no config.yaml exists, sensible defaults are used — the system works out of the box with zero configuration.

Quick Start

# Option 1: Use a preset
cp presets/cybersecurity.yaml config.yaml    # Offensive/defensive security, CTFs
cp presets/developer.yaml config.yaml        # Software engineering, APIs, DevOps
cp presets/research.yaml config.yaml         # Academic research, papers, studies
cp presets/general.yaml config.yaml          # Blank slate, pure semantic search

# Option 2: Start from the documented template
cp config.example.yaml config.yaml
# Edit config.yaml to your needs

Restart Claude Code after changing config.yaml.

config.yaml Structure

# Paths — where your documents live
paths:
  documents_dir: "./documents"    # Scanned recursively
  data_dir: "./data"              # Index storage
  models_cache_dir: "./models_cache"  # Persistent embedding model cache

# Documents — what gets indexed and how
documents:
  supported_formats:              # File types to index
    - .md
    - .txt
    - .pdf
    - .docx
    - .ipynb
    # - .py                       # Uncomment to index code
  exclude_patterns:               # Glob patterns to skip
    - "node_modules"
    - ".venv"
    - "__pycache__"
  chunking:
    chunk_size: 1000              # Max chars per chunk
    chunk_overlap: 200            # Shared chars between chunks

# Models — AI models for search (all run locally, no API keys)
models:
  embedding:
    model: "BAAI/bge-small-en-v1.5"   # ONNX, ~33MB, auto-downloaded
    dimensions: 384
    gpu: false                         # Set true + pip install knowledge-rag[gpu]
  reranker:
    enabled: true                      # Falls back to RRF if model is unavailable
    model: "Xenova/ms-marco-MiniLM-L-6-v2"
    top_k_multiplier: 3               # Candidates fetched before reranking

# Search — result limits and collection name
search:
  default_results: 5
  max_results: 20
  collection_name: "knowledge_base"   # Change for separate knowledge bases

# Categories — auto-tag documents by folder path
# Set to {} to disable categorization entirely
category_mappings:
  "security/redteam": "redteam"
  "security/blueteam": "blueteam"
  "notes": "notes"

# Keyword routing — prioritize categories based on query keywords
# Set to {} for pure semantic search with no routing bias
keyword_routes:
  redteam: