knowledge-rag

Claude Code向けのローカルRAGシステム。ハイブリッド検索(セマンティック + BM25)、クロスエンコーダー再ランキング、マークダウン対応チャンキング、9つのファイル形式、ファイル監視、12のMCPツールを搭載。外部サーバー不要。pip install knowledge-rag

ドキュメント

knowledge-rag banner: local hybrid RAG for MCP. Documents flow through ingestion (parse, chunk, embed, index), hybrid search (FTS5, BM25, semantic), cross-encoder reranking, and on to the LLM.

knowledge-rag: local RAG server for Claude Code, Cursor and other MCP clients

knowledge-rag is a local retrieval-augmented generation (RAG) server for the Model Context Protocol (MCP). It indexes the documents and code on your machine and gives Claude Code, Cursor, VS Code and other MCP clients 13 tools to search them, using hybrid search (BM25 keywords plus vector similarity) with cross-encoder reranking on by default. Everything runs in one Python process with embedded ChromaDB and FastEmbed ONNX models: no Docker, no database server, no API key. Indexing and search work offline once the models are cached.

PyPI NPM PyPI Downloads Python License Platform GPU CodeQL Quality Gate Glama Score OpenSSF Best Practices

  • 11.1 ms median uncached hybrid query on CPU, 6.1 ms on CUDA: 36 queries over 149 chunks on one Windows host, reranker off (audit)
  • 0.03 ms median for a repeated query served from the cache, same workload (audit)
  • 75,016 chunks from 5,889 documents: full rebuild completed in a production reproduction (v4.8.3)
  • 1,500+ tests collected on each of 9 OS × Python CI cells (platform-specific ones skip), 84% line coverage against an 80% floor (CI run, 2026-11-06)

Install and register with Claude Code (needs Python 3.11+ and Node.js 16+):

npx -y knowledge-rag --install-only
claude mcp add knowledge-rag -s user --env KNOWLEDGE_RAG_DIR=~/knowledge-rag -- npx -y knowledge-rag

Put files in ~/knowledge-rag/documents/ and ask your agent to search them. The folder is created on first start, new and changed files are indexed automatically, and the models (about 150 MB) download the first time they are needed.

Install · Results · How it works · Tools · Skills · Comparison · FAQ · Docs

Star history

Line chart of knowledge-rag GitHub stars over time

Updated daily by the star-history workflow.

Measured results

Measurements from the project's own audits. They come from one host and small corpora, so treat them as orders of magnitude for your planning, not as guarantees.

MeasurementCPUNVIDIA CUDA
Hybrid query, uncached, median11.13 ms6.10 ms
Hybrid query, uncached, p9513.91 ms7.44 ms
Repeated query (cache hit), median0.033 ms0.028 ms
Initial indexing, including first model load13.655 s0.832 s
Sampled peak process memory (RSS)1,416 MB990 MB

Workload: scripts/audit_retrieval.py indexing 7 public repository documents (149 chunks) into a temporary ChromaDB and FTS5 index; 36 uncached queries; cross-encoder disabled to isolate retrieval; Windows 11, RTX 3080 Ti, Python 3.12.7, FastEmbed 0.8.1, ChromaDB 1.5.9, model BAAI/bge-small-en-v1.5, Hugging Face offline mode. Source: audit 2026-10-03.

Retrieval checkResultWorkload and source
Expected source in the top 512 of 12 questions; MRR@5 0.875 (English), 0.792 (Portuguese)6 English and 6 Portuguese questions over the same 149 chunks, default English model; a small smoke test, not a general quality claim (audit)
Cross-encoder rerank of 8 candidates141.9 ms first call including model load, 19.7 ms next call (CPU)One English and one Portuguese query, network blocked (audit)
Large corpusFull rebuild completed and filled 75,016 FTS5 rows on the first attempt5,889 documents / 75,016 chunks, v4.8.3 reproduction (CHANGELOG)
More measurements: memory, model comparison, historical baseline
  • BM25 memory: retained Python memory fell from 120.7 MB to 26.9 MB (−77.75%) for 10,000 documents / 1.99 million tokens after the token pool was added in v4.10.0 (audit).
  • Metrics memory: 100,001 metric observations retained 328 bytes instead of 3,201,024 (audit).
  • Multilingual model: on a 60-question diagnostic set over a private ~70,000-chunk corpus, intfloat/multilingual-e5-large found the expected source for 36 questions against 37 for the default BAAI/bge-small-en-v1.5; re-embedding 70,083 chunks on CUDA took 1,186.58 s (audit 2026-10-04).
  • Historical baseline: v4.7.1 microbenchmarks and a 100-document rebuild on CPU (perf-baseline-v4.7.1). The ratios apply to those workloads only.
  • Live dashboard: the benchmark dashboard tracks CI microbenchmarks. They use mocked storage and models, so they answer a different question than the real-model numbers above.

How it works

  1. Ingest. Files under documents/ are parsed by format: code files get their function, class and import names as metadata, notebooks keep only Markdown and code cells, PDFs contribute their text. Markdown is split at ##/### sections; every other format is cut into 1,000-character chunks with 200 characters of overlap. Chunks are de-duplicated by SHA-256, embedded with FastEmbed (default BAAI/bge-small-en-v1.5, 384 dimensions) and stored in an embedded ChromaDB.
  2. Watch. While the server runs, a watcher re-indexes changed files after 10 seconds without new changes. Full rebuilds fill a staging collection and swap it in after validation, so searches keep using the current index.
  3. Search. search_knowledge checks a query cache (100 entries, 5-minute TTL), runs BM25 over an inverted index and vector search, fuses both rankings with weighted reciprocal rank fusion (hybrid_alpha, default 0.3, leaning towards keywords) and reranks the top candidates with the Xenova/ms-marco-MiniLM-L-6-v2 cross-encoder. With the opt-in FTS5 fast path, identifier-like queries such as CVE IDs, ticket keys and hashes go to SQLite FTS5 first.
  4. Answer. Results go back to the MCP client with the file path, chunk index, snippet and scores. The client's model writes the answer; knowledge-rag never calls an LLM.
  5. Serve. Over stdio for one client, or as one Streamable HTTP or SSE server for many. HTTP mode answers health probes first, then checks the bearer token. Every tool call passes through the optional rate limiter and metrics. config.yaml and five presets configure all of it.
flowchart TB
    subgraph CLIENTS["MCP clients"]
        direction LR
        C1[Claude Code]
        C2[Claude Desktop]
        C3[Cursor]
        C4[Windsurf]
        C5[VS Code · other stdio or HTTP clients]
    end

    subgraph TRANSPORT["Transport"]
        T1[stdio<br/>1 process per client]
        T2[Streamable HTTP / SSE<br/>1 server for N clients]
    end

    subgraph MIDDLEWARE["ASGI middleware (HTTP transports)"]
        M1[HealthMiddleware<br/>/health · /healthz]
        M2[BearerAuthMiddleware<br/>constant-time compare]
    end

    subgraph MCP["13 MCP tools (contract checked in CI)"]
        W[Tool wrappers<br/>optional rate limit · metrics]
        MT1[search_knowledge]
        MT2[get_document · search_similar]
        MT3[add_document · add_from_url · update · remove]
        MT4[reindex_documents · get_reindex_status]
        MT5[list_documents · list_categories · get_index_stats · evaluate_retrieval]
    end

    subgraph SEARCH["Retrieval pipeline"]
        QC[Query cache<br/>LRU 100 · 5-min TTL]
        R[Query router<br/>regex, FTS5 fast path only]
        F[FTS5 fast path<br/>opt-in lexical retrieval]
        H[Hybrid search]
        BM[BM25<br/>inverted index]
        SE[Vector search<br/>FastEmbed ONNX, lazy-loaded]
        RRF[Weighted reciprocal rank fusion]
        CE[Cross-encoder rerank<br/>MiniLM-L-6-v2]
    end

    subgraph STORAGE["Local storage (data_dir)"]
        CH[ChromaDB<br/>vectors + metadata<br/>WAL with HTTP transports]
        FT[SQLite FTS5<br/>lexical index<br/>WAL + busy timeout]
        MD[index_metadata.json<br/>durable state]
    end

    subgraph INGEST["Document ingestion"]
        FS[documents/ folder]
        WD[Watcher<br/>10 s debounce]
        PA[Format parsers<br/>MD · PDF · DOCX · code · IaC · IPYNB]
        CK[Chunker<br/>Markdown sections · 1,000-char windows]
        EM[FastEmbed ONNX<br/>default bge-small-en-v1.5, 384-d]
        DD[SHA-256 dedup]
        SW[Staging rebuild<br/>rollback on handled publication failure]
    end

    subgraph OBS["Observability (opt-in)"]
        PM[Prometheus /metrics<br/>separate port, HTTP transports]
        LG[Structured JSON logs]
        HC[Health payload<br/>version · uptime · cache stats]
    end

    subgraph CFG["Configuration"]
        YM[config.yaml<br/>+ 5 presets]
    end

    CLIENTS -->|MCP over stdio| T1
    CLIENTS -.->|MCP over HTTP| T2
    T1 --> W
    T2 --> M1 --> M2 --> W
    W --> MT1 & MT2 & MT3 & MT4 & MT5

    MT1 --> QC
    QC -->|cache miss| R
    R -->|identifier-like| F
    R -->|everything else| H
    H --> BM
    H --> SE
    F --> FT
    BM --> CH
    SE --> CH
    BM --> RRF
    SE --> RRF
    RRF --> CE
    CE --> QC

    MT2 --> CH
    MT3 --> INGEST
    MT4 --> SW
    MT5 --> CH

    FS --> WD --> PA
    PA --> CK --> DD --> EM --> CH
    SW -.->|validated swap| CH
    SW -.-> FT
    CH -.-> MD

    W -.->|instrumented| PM
    W -.->|logs| LG
    M1 --> HC

    YM -.-> SEARCH
    YM -.-> STORAGE
    YM -.-> OBS
    YM -.-> MIDDLEWARE

    classDef client fill:#3776AB,stroke:#1e5a8a,color:#fff
    classDef transport fill:#00A67E,stroke:#006e54,color:#fff
    classDef middleware fill:#6b46c1,stroke:#4c1d95,color:#fff
    classDef storage fill:#4b5563,stroke:#1f2937,color:#fff
    classDef obs fill:#dc2626,stroke:#7f1d1d,color:#fff
    classDef ingest fill:#b45309,stroke:#78350f,color:#fff

    class C1,C2,C3,C4,C5 client
    class T1,T2 transport
    class M1,M2 middleware
    class CH,FT,MD storage
    class PM,LG,HC obs
    class FS,WD,PA,CK,EM,DD,SW ingest

Component ownership, memory and concurrency boundaries and the hybrid_alpha weights: docs/ARCHITECTURE.md.

Install

Every path below runs the same server. Each claude mcp add sets KNOWLEDGE_RAG_DIR, the folder that holds config.yaml, documents/, data/ and models_cache/. Without it, the server picks a folder from its install location and the working directory the client starts it in, which can be a project you did not intend (config.py).

Claude Code with the npx launcher

npx -y knowledge-rag --install-only
claude mcp add knowledge-rag -s user --env KNOWLEDGE_RAG_DIR=~/knowledge-rag -- npx -y knowledge-rag

The npm package is a launcher. It finds Python 3.11+ (python3/python, or python/py -3 on Windows), creates a virtual environment in ~/.knowledge-rag/venv, installs the matching knowledge-rag version from PyPI and starts the server over stdio. --install-only does the installation once ahead of time, so the client's first start does not wait for pip. A new npm version reinstalls the package on the next start. If a client on native Windows fails to start npx, register cmd /c npx -y knowledge-rag instead.

pip in a virtual environment

python3 -m venv ~/knowledge-rag/venv
~/knowledge-rag/venv/bin/python -m pip install knowledge-rag
claude mcp add knowledge-rag -s user --env KNOWLEDGE_RAG_DIR=~/knowledge-rag \
  -- ~/knowledge-rag/venv/bin/python -m mcp_server.server

On Windows (PowerShell), use python and $HOME\knowledge-rag\venv\Scripts\python.exe in place of python3 and ~/knowledge-rag/venv/bin/python.

Optional: knowledge-rag init, run inside ~/knowledge-rag with the virtual environment active, copies config.example.yaml and the five presets and creates documents/. It does not create config.yaml or register the server: copy the template or a preset to config.yaml yourself. Without a config.yaml, the defaults apply, including all 33 default file types.

Docker

The image is published for linux/amd64 and linux/arm64. For stdio, the client starts one container per session:

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

The image runs python -m mcp_server.server, which reads /app/config.yaml and defaults to stdio on 127.0.0.1. To serve HTTP from a container, mount a config that listens on all container interfaces and pass --transport:

mkdir -p ~/knowledge-rag
cat > ~/knowledge-rag/config.yaml <<'EOF'
server:
  host: "0.0.0.0"
  auth:
    bearer_token: "replace-with-a-long-random-token"
EOF
docker run -d --name knowledge-rag -p 127.0.0.1:8179:8179 \
  -v ~/knowledge-rag/config.yaml:/app/config.yaml:ro \
  -v ~/knowledge-rag/documents:/app/documents \
  -v ~/knowledge-rag/data:/app/data \
  -v knowledge-rag-models:/app/models_cache \
  ghcr.io/lyonzin/knowledge-rag:latest --transport streamable-http
curl http://127.0.0.1:8179/health
claude mcp add --transport http knowledge-rag http://127.0.0.1:8179/mcp \
  # if server.auth.bearer_token is set, add the token as a Bearer auth header

The image builds its model copies into FastEmbed's default cache, but the server reads /app/models_cache, so the models download again the first time they are needed. The knowledge-rag-models volume keeps them across containers.

Shared HTTP server for several clients

stdio fits one client that owns one process. For several clients on one index (Claude Code, Cursor, LM Studio, more terminals), run one Streamable HTTP server and point every client at it (#217, #223):

# ~/knowledge-rag/config.yaml
server:
  transport: "streamable-http"      # "sse" for clients that only speak SSE
  host: "127.0.0.1"
  port: 8179
  auth:
    bearer_token: "replace-with-a-long-random-token"
  rate_limit:
    enabled: true
    requests_per_minute: 60
    burst: 10
  metrics:
    enabled: true
    port: 9179                      # binds 0.0.0.0 with no auth: firewall it
  logging:
    format: "json"
KNOWLEDGE_RAG_DIR=~/knowledge-rag knowledge-rag     # run from the venv where you installed it
curl http://127.0.0.1:8179/health                  # {"status":"ok","version":"5.0.0",...}
claude mcp add --transport http knowledge-rag http://127.0.0.1:8179/mcp \
  # if server.auth.bearer_token is set, add the token as a Bearer auth header
TransportClients per server processWho runs the processUse it for
stdio (default)1The MCP client, per sessionOne client; models load in each process on first use
streamable-httpManyYou, one background serverSeveral clients sharing one index and one model load (preferred)
sseManyYou, one background serverHTTP clients that only support SSE

HTTP transports switch on the single-instance lock (KNOWLEDGE_RAG_SINGLE_INSTANCE=1), so a second server cannot open the same data directory. The default host is 127.0.0.1. If you bind to 0.0.0.0 or serve a network, set server.auth.bearer_token and put TLS in front with a reverse proxy such as Caddy or Nginx; without a token, the server prints a warning that the endpoint is unauthenticated.

Claude custom connectors (remote MCP)
  • Network requirement: custom connector requests come from Anthropic's cloud on web, desktop and mobile, so they cannot reach 127.0.0.1. Expose the server through a secure public HTTPS endpoint (for example Cloudflare Tunnel, ngrok or a reverse proxy) at https://your-public-domain/mcp. Configure the proxy to send an upstream Host that the HTTP app accepts for server.host (with the default loopback configuration, 127.0.0.1:8179); forwarding the public hostname unchanged can produce 421 Invalid Host header. Keep bearer authentication and HTTPS on. A local claude_desktop_config.json uses command/args for local stdio processes and does not support remote HTTP endpoints.
  • Setup and auth: in Claude (Pro/Max: Customize > Connectors > + Add > Add custom connector; Team/Enterprise owners: Organization settings > Connectors), enter the public endpoint. Under Request headers, set Authorization to Bearer <your-token>, matching server.auth.bearer_token in config.yaml. See Anthropic's custom connectors guide.

Other MCP clients

Any client that starts a stdio MCP server from a command, or connects to a Streamable HTTP endpoint, can use knowledge-rag. The installation guide has the exact JSON for each of these:

ClientWhere the server entry goes
Claude Desktopclaude_desktop_config.json, under mcpServers (Settings → Developer → Edit Config)
Cursor~/.cursor/mcp.json or .cursor/mcp.json, under mcpServers
Windsurf~/.codeium/windsurf/mcp_config.json, under mcpServers
Antigravity~/.gemini/antigravity/mcp_config.json, under mcpServers
VS Code (Copilot).vscode/mcp.json, under servers, with "type": "stdio"
opencodeopencode.json, under mcp, with command as an array
{
  "mcpServers": {
    "knowledge-rag": {
      "command": "npx",
      "args": ["-y", "knowledge-rag"],
      "env": { "KNOWLEDGE_RAG_DIR": "~/knowledge-rag" }
    }
  }
}

[!WARNING] One writer per data directory. Over stdio, each MCP client starts and owns its own knowledge-rag process. Do not run a second writer against the same data_dir while that client is open: on Windows, two writers corrupted the ChromaDB HNSW segment (#216). Add "KNOWLEDGE_RAG_SINGLE_INSTANCE": "1" to the client's env block as a safety net, or share one HTTP server. Running CLI commands safely: docs/single-instance.md.

[!WARNING] Keep data_dir out of cloud-sync folders. OneDrive (including OneDrive - <tenant>), iCloud Drive, Dropbox, Google Drive, Box, pCloud, MEGAsync, Nextcloud/ownCloud and Sync hold handles on the live index while they upload it. That races with ChromaDB's HNSW memory maps and SQLite WAL writes and can crash the process (0xC0000005 on Windows, SIGBUS elsewhere); startup auto-repair then moves the index to data/backups/auto-repair-* and you reindex from scratch. Since v4.11.0, startup stops with exit code 78 (EX_CONFIG) when data_dir resolves under one of these providers. Use a local path such as %LOCALAPPDATA%\knowledge-rag\data on Windows or ~/.local/share/knowledge-rag/data on Linux and macOS, or set paths.allow_cloud_sync: true to accept the risk.

All install methods, GPU setup and troubleshooting for each client: docs/INSTALLATION.md.

The 13 MCP tools

ToolWhat it does
search_knowledgeHybrid BM25 + vector search with cross-encoder reranking; filters by category, folders (include_folders, exclude_folders) and min_score
get_documentReturns the full content of one indexed file
search_similarFinds documents similar to an indexed file
evaluate_retrievalReports MRR@5 and Recall@5 for your own query → expected file pairs
list_documentsLists indexed documents, 100 per page by default (up to 200 with limit), paged with offset
list_categoriesLists categories, derived from folder paths, with document counts
get_index_statsDocument and chunk counts, embedding model, BM25 status, cache hit rate, watcher status, HNSW self-recall probe
add_documentSaves text as a file under documents/ and indexes it
update_documentReplaces a document's content and re-indexes it
remove_documentRemoves a document's chunks from the index, and optionally the file
add_from_urlFetches an http(s) page, strips the HTML, saves it as Markdown with a provenance marker and indexes it
reindex_documentsIncremental by default; force re-embeds every file, full_rebuild builds and swaps a staging collection; runs in the background
get_reindex_statusReports the progress of a background reindex

Each result carries source (the file path), chunk_index and, in snippet mode, match_offset (a character offset into the content), so citations point to a file and chunk, not a line number. score is normalized per query, so the top result is always 1.0 and min_score filters relative to the other results; compare reranker_score, the raw cross-encoder score, across queries. Parameters, return shapes and examples: docs/API.md.

Agent skills

The 13 tools tell an agent what it can do; they do not tell it when to search. The skills/ folder adds ten MIT-licensed skills for Claude Code that make the agent check your index first, cite what it used and keep the index current. Other agents can use the same instructions as rules or system prompts (skills/README.md).

SkillKindWhat it changes
rag-check-firstFoundationSearches the corpus before answering technical questions
rag-cite-sourcesFoundationCites the source file and chunk for every claim taken from the corpus
rag-onboard-contextFoundationProbes what is indexed at the start of a session
rag-deep-diveWorkflowSearch, read the best document in full, then find similar ones
rag-web-fallbackWorkflowGoes to the web only when the local corpus comes back empty or insufficient
rag-troubleshootWorkflowLooks up prior fixes and runbooks before debugging an error
rag-code-reviewWorkflowChecks ADRs, standards and similar files before commenting on a change
rag-index-decisionsMaintenanceIndexes decisions and fixes back into the corpus
rag-evaluate-qualityMaintenanceMeasures MRR@5 and Recall@5 after ingestion or configuration changes
rag-security-firstDomainConsults MITRE ATT&CK, CVE and runbook notes first on security tasks

Each skill installs as ~/.claude/skills/<skill-name>/SKILL.md (or .claude/skills/<skill-name>/SKILL.md for one project):

# Node.js 22.20+: choose the skills and agents interactively; -g installs for all projects
npx skills add lyonzin/knowledge-rag -g

# Without Node (Linux, macOS, WSL, Git Bash): all ten skills into ~/.claude/skills
curl -fsSL https://raw.githubusercontent.com/lyonzin/knowledge-rag/master/skills/install.sh | bash

install.sh also takes --project, --target <path>, --only rag-check-first,rag-cite-sources, --dry-run, --list and --help. Restart Claude Code after installing. Skill chains and per-client setup: skills/CATALOG.md, skills/README.md.

How knowledge-rag compares to other RAG projects

As of 2026-11-06. 🗸 default: built in and on by default. 🗸 opt-in: built in, off until you enable it. 🗸: available, default state not compared. component: a library piece you assemble yourself. ✗: not built in, meaning absent from the code, needing an external provider (local embeddings column), or not found in code or docs; ✗ manual: you start it yourself. Words in parentheses name the mechanism.

Project (stars, license)MCP serverMCP clientHybridRerankAuto re-indexLocal emb.
knowledge-rag (301, MIT)🗸 13 tools✗🗸 default (+FTS5 opt-in)🗸 default🗸 default🗸
Dify (160.5k, Dify license)🗸🗸🗸🗸 opt-in✗ manual sync✗
Open WebUI (157.9k, Open WebUI license)✗🗸🗸 opt-in🗸 opt-in✗ manual (add-on)🗸
Langflow (156.4k, MIT)🗸🗸componentcomponentcomponent (Schedule Trigger)component
LangChain (149.1k, MIT)✗🗸componentcomponentcomponent (indexing API)component
RAGFlow (93.7k, Apache-2.0)🗸🗸🗸 default🗸 opt-in🗸 connectors✗
AnythingLLM (68.0k, MIT)✗🗸✗🗸 opt-in🗸 opt-in (beta, per file, hourly)🗸
Serviette, ex pathwaycom/llm-app (58.7k, MIT)✗✗🗸 opt-in🗸 opt-in🗸 default🗸
PrivateGPT (57.6k, Apache-2.0)✗🗸🗸 opt-in (Qdrant)✗🗸 opt-in (CLI --watch)✗
LlamaIndex (52.8k, MIT)✗🗸componentcomponentcomponent (refresh_ref_docs)✗
LightRAG (40.6k, MIT)✗✗✗🗸 opt-in (API)✗ manual scan✗
Quivr 2 (39.6k, MIT)🗸✗🗸🗸 opt-in (paid service)🗸 scheduled feeds🗸
Khoj (38.0k, AGPL-3.0)✗🗸✗🗸 opt-in🗸 via clients, every 10 min🗸
GraphRAG (36.6k, MIT)✗✗✗✗✗ manual (graphrag update)✗
Onyx (33.3k, MIT CE + EE)🗸🗸🗸 default✗ removed in 2026🗸 connectors🗸
FastGPT (30,000, FastGPT license)🗸🗸🗸🗸 with a rerank model🗸 opt-in (daily sync)✗
ProjectTypeNeeds for first answerFormats
knowledge-ragMCP serverpip + MCP client33 (+2 opt-in), code/IaC; no OCR
DifyplatformDocker (Postgres, Redis, Weaviate…) + modelPDF, Office, CSV, MD, HTML, EML, EPUB
Open WebUIchat apppip/Docker + Ollama/APIPDF, Office, EPUB, CSV, HTML, MD
Langflowbuilderpip/Docker + provider keyTXT, MD, CSV, JSON, HTML, PDF, DOCX…
LangChainlibrarycode + providerloaders
RAGFlowengine + UIDocker (Elasticsearch, MySQL, MinIO, Kvrocks, NATS), 16 GB + modelOffice, PDF, scans, web
AnythingLLMdesktop/Docker appinstaller (built-in LLM)PDF, TXT, DOCX…
ServietteRAG serverpip + Pathway licence keytext, Office, PDF, scans
PrivateGPTAPI serverLLM + embedding serversPDF, DOCX, PPT/X, EPUB, IPYNB, images, audio
LlamaIndexlibrarycode + OPENAI_API_KEY~20 incl. images, MP3/4
LightRAGgraph lib + serverLLM + embedding providersTXT, MD, PDF, DOCX
Quivr 2HTTP engineDocker (Postgres, object store)PDF plugin, RSS, mail
KhojappDocker (pgvector, SearxNG…) + LLMPDF, MD, Org, Word, Notion
GraphRAGlibrary/CLIOpenAI/Azure keyTXT, CSV, JSON
Onyxenterprise appDocker (~12 containers: OpenSearch, Postgres, Redis…) + LLMPDF, DOCX, PPTX, XLSX, EML, EPUB, HTML, TXT, MD, JSON, YAML, XML, CSV, SQL, PNG, JPG, WebP
FastGPTplatformDocker (MongoDB, PgVector, AI Proxy) + LLM + embeddingTXT, MD, HTML, PDF, Office, CSV

Notes: Serviette was renamed from pathwaycom/llm-app and inherited its stars. The LlamaIndex README says its focus moved to LlamaParse. Quivr was repurposed as a content-stream search engine. The GraphRAG README says it is in maintenance mode. PrivateGPT's changelog lists a SentenceTransformer reranker (#1810) that the current code no longer contains.

Where knowledge-rag differs. It is the only row with hybrid search and cross-encoder reranking both on by default, and the only MCP server in the table that answers without Docker, a database server or a model key. Auto re-index is on by default only in knowledge-rag and Serviette, and Serviette ships no MCP server; everywhere else it takes a connector, a client, a setting, assembly or a manual run. It is code-first: 23 of its 33 default file types are code or infrastructure files. The license is plain MIT.

Where others are stronger. Multi-user scale: Postgres, OpenSearch and Elasticsearch stacks against one writer on an embedded ChromaDB. Web UIs and visual builders: knowledge-rag has no UI and does not generate answers; the MCP client's LLM does. Connectors, OCR, images and audio, and graph RAG. Evidence and reach: knowledge-rag's published quality check is a 12-question smoke test (Recall@5 1.0) and it has 301 stars. This table compares features, not retrieval quality.

Checklist for your own evaluation

Feature checklists are not benchmarks. Before you choose, measure what your deployment needs on comparable hardware:

DecisionWhat to verify
Local or remote processingWhere models run, which client receives content, which features need network access
Retrieval qualityExpected-source queries on your corpus, including its languages and identifiers
PerformanceCold start, indexing, uncached queries, memory peaks and concurrent use
OperationsAuthentication, metrics exposure, backups, recovery and who owns the single writer
ExtensibilitySupported parsers, model profiles and the public MCP contract

Results this project measured against an older version of itself, such as the audit, are not speed comparisons with other RAG projects.

Sources for every row

Built for production

Each capability says whether it is on by default and links to the test, document or release note that backs it. Opt-in features do nothing until you set their key in config.yaml or the environment variable shown.

Security

CapabilityDefaultEnable / tuneProof
Listens on loopback onlyOnserver.hostcode
Bearer token on HTTP transportsOpt-inserver.auth.bearer_tokentest
Client paths kept inside documents_dirOn—test
Fence on fetched web contentOn—test
Rate limit, one window per processOpt-inserver.rate_limitcode
Refuses a cloud-synced data_dirOnpaths.allow_cloud_syncv4.11.0
Bandit, Semgrep, pip-audit, Gitleaks, dependency review, SocketEvery PR—workflow
CodeQLMaster and weekly—workflow
PyPI Trusted Publishing, npm provenanceEvery release—workflow
OpenSSF Best PracticesPassing—#13864

Observability

CapabilityDefaultEnable / tuneProof
/health and /healthz, no token neededOn (HTTP)—test
Prometheus metrics on a separate portOpt-in (HTTP)server.metricstest
Structured JSON logsOpt-inserver.logging.formattest
Public benchmark dashboardEvery push to master—dashboard

Scale and performance

CapabilityDefaultEnable / tuneProof
One HTTP server for many clientsOpt-inserver.transportdoc
ChromaDB WAL journalAuto (HTTP)—test
BM25 over an inverted indexOn—test
Cross-encoder rerank, fused-order fallbackOnmodels.rerankertest
FTS5 fast path for identifiersOpt-insearch.lexical_fast_pathdoc
NVIDIA CUDA with CPU fallbackAutomodels.embedding.gputest
Windows AMD DirectML, experimentalOpt-ingpu: "directml"test
Embedding batch size per providerAutodocuments.embed_batch_sizetest
Query cache, cleared on writesOn—test
Background reindex with progressOn—doc
Staging rebuild while the index servesOn—test

Reliability

CapabilityDefaultEnable / tuneProof
Single-instance lockAuto (HTTP)KNOWLEDGE_RAG_SINGLE_INSTANCEtest
Startup probe, auto-repair, 3 backups keptOn—test
Index kept if documents_dir is empty or missingOn—test
File watcher, 10 s quiet periodOnKNOWLEDGE_RAG_WATCHER_DISABLEDtest
Rollback on handled publication failuresOn—test
1,500+ tests on 9 OS × Python cells, 80% floorEvery PR—run
MCP tool contract and API surface vs masterEvery PR—test
Legacy configs and call shapesEvery PR—test
Performance gate, 10%Every PR—workflow
Chaos tests, 3 failure scenariosNightly—tests
50,000-call soak, stub embeddingsNightly—test
Mutation testing of lock and preflightNightly—workflow
Full suite three times for flaky testsNightly—workflow

Compliance: the project holds no LGPD, GDPR or HIPAA certification. Local indexing can help with data-locality requirements, but the MCP client sends the results it retrieves to its own model provider as part of the conversation.

Operational notes: what each control does and does not cover
  • Bearer token: compared in constant time (hmac.compare_digest). A missing or wrong token gets 401 with WWW-Authenticate: Bearer realm="knowledge-rag". Health probes answer without it. stdio has no token: the client owns the process. There is no built-in TLS, so put a reverse proxy (Caddy, Nginx) in front of anything that leaves loopback.
  • Rate limit: one sliding window for the whole process, shared by every client: requests_per_minute plus burst (defaults 60 and 10) calls per 60 seconds. It is not per client.
  • Metrics: only with HTTP transports, on server.metrics.port (default 9179). The endpoint binds 0.0.0.0 with no authentication, so firewall it. It exposes per-tool calls, errors and durations plus FTS5 fast-path counters.
  • Paths: the six tools that accept a path resolve it inside documents_dir and reject traversal, escaping symlinks, NUL bytes and NTFS alternate data streams (CWE-22, CWE-59).
  • Fetched content: add_from_url neutralizes known model control tokens and stores a provenance marker with the page. That limits prompt injection; it does not prevent it, so treat retrieved text as untrusted (SECURITY.md). The tool fetches whatever http(s) URL it receives, private network addresses included, and follows redirects; on a shared server, decide who may call it.
  • Network use: the first model download contacts Hugging Face; set HF_HUB_OFFLINE=1 once the models are cached.
  • Health payload: GET /health returns status, version, uptime_seconds and query-cache statistics on the MCP port.
  • Single instance: HTTP transports turn the lock on automatically; for stdio clients, add KNOWLEDGE_RAG_SINGLE_INSTANCE=1 to the client's env block. A second process on the same data_dir then exits with code 75.
  • Index safety: a full rebuild fills a staging collection and swaps it in after validation; handled failures roll back. Startup retries a failing probe 3 times before moving the index to data/backups/auto-repair-*, keeping the 3 newest. Recovery steps: docs/reindex-operations.md.
  • Dependencies: requirements.txt and pyproject.toml declare version ranges, not pins. For a reproducible deployment, resolve once and record the tested environment.
  • CI gates: every pull request runs 34 GitHub Actions checks. The performance gate fails a benchmark only when every branch run is more than 10% slower than every master run. Nightly jobs open issues instead of blocking merges; mutation testing is exploratory and never fails the run.
  • Disclosure: private security advisories, acknowledged within 48 hours, initial assessment within 5 business days, 90-day coordinated disclosure (SECURITY.md).

Configuration in 30 seconds

knowledge-rag init                    # copies config.example.yaml, the presets and an empty documents/
cp presets/general.yaml config.yaml   # or cybersecurity, developer, research, multilingual
# drop your files into documents/, restart, ask the agent

Settings live in config.yaml inside KNOWLEDGE_RAG_DIR. Every key is optional; these are the defaults:

paths:
  documents_dir: "./documents"
  data_dir: "./data"                 # local disk only, see the cloud-sync warning
  models_cache_dir: "./models_cache"
documents:
  chunking:
    chunk_size: 1000
    chunk_overlap: 200
models:
  embedding:
    profile: "custom"                # "compact" | "quality" | "multilingual" | "custom"
    model: "BAAI/bge-small-en-v1.5"  # used when profile is "custom"
    dimensions: 384
    gpu: "auto"                      # "auto" | "true" | "false" | "directml"
  reranker:
    enabled: true
    model: "Xenova/ms-marco-MiniLM-L-6-v2"
search:
  default_results: 5
  max_results: 100
  lexical_fast_path:
    enabled: false                   # SQLite FTS5 for identifier-like queries
server:
  transport: "stdio"                 # "stdio" | "streamable-http" | "sse"
  host: "127.0.0.1"
  port: 8179
ProfileEmbedding modelDimensions
compactBAAI/bge-small-en-v1.5384
qualityBAAI/bge-large-en-v1.51024
multilingualintfloat/multilingual-e5-large, with query:/passage: prefixes1024
custom (default)models.embedding.modelmodels.embedding.dimensions

Changing the embedding model or its prefixes needs a full reindex. GPU: auto tries NVIDIA CUDA on the first embedding and falls back to CPU; the [gpu] extra targets CUDA 12. directml is the experimental Windows AMD mode and needs models.embedding.device_id (GPU setup).

Presets are complete config.yaml files (knowledge-rag init copies them):

PresetForCategoriesRouting keywordsQuery expansionsFile types enabled
cybersecurityRed and blue team, CTF, threat hunting, incident response81286914
developerDesign docs, ADRs, runbooks, APIs, code91376416
researchPapers, lab notebooks, datasets980408
generalAnything else: no categories, routing or expansions0004
multilingualNon-English corpora, multilingual profile0004

The presets list their own documents.supported_formats, so they index fewer file types than the defaults: general and multilingual index only .md, .txt, .pdf and .docx. Every field, every default and tuning recipes: docs/CONFIGURATION.md.

35 file formats

35 parsers; 33 file types enabled by default

Without a config.yaml, 33 file types are indexed. The MetaTrader extensions .mq4 and .mqh are opt-in. The shipped config.example.yaml enables 29 (it leaves out .xlsx, .pptx, .ipynb and .csv), and each preset enables its own list. Chunking is fixed-size (1,000 characters, 200 overlap) for every format except Markdown.

FormatMatchHow it is parsedDefault
Markdown.mdSplits at ##/### sections, code blocks protectedYes
Plain text.txtFixed-size chunksYes
PDF.pdfPyMuPDF text extraction, or an explicit page-marked UTF-8 extraction (below); no OCRYes
Word.docxpython-docx, headings kept as MarkdownYes
Excel.xlsxopenpyxl, sheet by sheetYes
PowerPoint.pptxpython-pptx, slide by slideYes
Jupyter Notebook.ipynbMarkdown and code cells only; outputs and base64 skippedYes
JSON.jsonStructure-aware, flattened key-value textYes
CSV.csvHeader and rows as textYes
XML.xmlRoot element and namespace metadataYes
Python.pyFunction and class names as metadataYes
C.cFunctions, structs and includes as metadataYes
C/C++ header.hFunction declarations and structs as metadataYes
C++.cppClasses, structs and includes as metadataYes
JavaScript.jsFunctions, classes and imports (ESM and CJS) as metadataYes
React JSX.jsxSame as JavaScriptYes
TypeScript.tsFunctions, classes, interfaces, enums and imports as metadataYes
React TSX.tsxSame as TypeScriptYes
Go.goFunctions, structs and imports as metadataYes
Rust.rsFunctions, structs, enums, traits and use imports as metadataYes
Kotlin.ktFunctions (including class members) and classes as metadataYes
YAML.yamlKubernetes kind, apiVersion and name as metadataYes
YAML.ymlSame as .yamlYes
HuJSON.hujsonJSON with comments and trailing commas (for example Tailscale ACLs)Yes
CUE.cuePackage and imports as metadataYes
Protocol Buffers.protoServices, messages and RPCs as metadataYes
Rego.regoOPA policies: package and imports as metadataYes
SQL.sqlTable names and statement types as metadataYes
Shell.shFunction names as metadataYes
jq.jqIndexed as a shell-style scriptYes
DockerfileDockerfilePlain text, matched by exact file nameYes
MakefileMakefilePlain text, matched by exact file nameYes
TiltfileTiltfileStarlark: def functions and load() calls as metadataYes
MQL4 source.mq4Code parserOpt-in
MQL4 header.mqhCode parserOpt-in

A file with a .pdf extension can also hold an explicit text extraction: the first line must be [Page N] with a positive page number, followed by non-empty UTF-8 text. An optional UTF-8 BOM and tab/CR/LF whitespace are accepted. Recognition is limited to 4 MiB, rejects other C0/C1 control characters and DEL, and never treats a file with PDF magic bytes in its first 1 KiB as text. Accepted extractions keep their source path and are marked content_format=extracted_text and page_count_source=markers; the marker count does not prove the original PDF's page count. Binary PDFs keep the native parser and its error messages, and invalid or oversized text lookalikes remain errors.

To enable an opt-in format, list it in documents.supported_formats. The list replaces the default, so include every type you want:

documents:
  supported_formats: [".md", ".pdf", ".mq4", ".mqh"]

Per-format notes: docs/CONFIGURATION.md.

FAQ

Does knowledge-rag send my documents to the cloud?

Indexing, embeddings and search run on your machine. Three things use the network: the first model download from Hugging Face, add_from_url when you call it, and your MCP client, which sends the results it retrieves to its model provider as part of the conversation.

Can it run fully offline or air-gapped?

Yes, once the models are in models_cache/. Start it once with network access, or copy a populated models_cache/ from another machine, then set HF_HUB_OFFLINE=1. The audit measurements above ran in that mode.

How is it different from LangChain, LlamaIndex or a vector database?

Those are building blocks you assemble in code. knowledge-rag is a finished MCP server: hybrid search, reranking and folder watching behind 13 tools, configured with a YAML file. See the comparison.

Do I need a GPU?

No. CPU is the default and the fallback. NVIDIA CUDA speeds up embedding (13.7 s against 0.8 s for the first indexing in the audit workload); on Windows, AMD GPUs can opt into experimental DirectML. See docs/gpu-setup.md.

Do I have to reindex after editing files?

Not while the server runs: the watcher picks up changes after 10 seconds without new ones. Changes made while it was stopped are indexed by the next change or by calling reindex_documents() with no arguments.

Can several clients or agents share one index?

Yes. Run one Streamable HTTP server and point every client at it. Do not run two writers against the same data_dir.

Does it handle non-English documents?

The default model is English-trained, yet it found the expected source for all 6 Portuguese test questions (MRR@5 0.792) in the audit. The multilingual profile uses intfloat/multilingual-e5-large; on the 60-question diagnostic it found 36 expected sources against 37 for the default, so measure on your own corpus before switching.

How do I measure search quality on my documents?

Call evaluate_retrieval with your own pairs of query and expected file; it returns MRR@5, Recall@5 and per-query hits. Use reranker_score, not score, to compare results across queries, because score is rescaled so that each query's top result is 1.0.

Does it work with Cline, Gemini CLI, Zed or other clients?

Any client that can start a stdio MCP server from a command, or connect to a Streamable HTTP endpoint, can use it. The repository has written setup for Claude Code, Claude Desktop, Cursor, Windsurf, Antigravity, VS Code and opencode.

Documentation

DocumentContents
Installation guideInstall methods (npx, installer, pip, source, Docker, SSE/HTTP), client configurations, GPU acceleration
API referenceAll 13 MCP tools: parameters, return shapes, examples
Configuration referenceEvery config.yaml field, presets, hybrid_alpha tuning
ArchitectureComponents, query and ingestion flow, memory and concurrency boundaries, hybrid weights
Search scope and retrieval qualityFolder filters, evaluation method, quality limits
TroubleshootingCommon problems and their fixes
FTS5 fast pathWhen and how to enable lexical retrieval
Reindex operationsStaging rebuilds, resume, checkpoints, recovery
Operating model and single instanceWho runs the server, running CLI commands safely
GPU setupCUDA runtime compatibility, CPU fallback, AMD and macOS limits
Migration to v4.8.0Embedding profiles, multilingual, zero-downtime rebuild
Memory and GPU auditReproducible workloads, measured results, validation limits
Release reviewIndependent review findings, retrieval diagnostics, model comparison
Security policyThreat model, disclosure channel and timelines
ContributingDevelopment, testing, pull request process
ChangelogRelease notes since v1.0.0

Recent releases

  • v5.0.0 (2026-11-05): breaking list_documents now returns 100 documents per page by default (up to 200 with limit); call it again with offset until you reach total. Also: an empty or missing documents_dir no longer clears the index, search snippets start at the match, add_from_url stops overwriting other pages, and failing tests now block merges.
  • v4.11.1 (2026-10-25): fresh installs start again, Windows state writes retry through brief file locks, and the Docker image adds linux/arm64.
  • v4.11.0 (2026-10-04): startup refuses a data_dir inside cloud-sync folders.
  • v4.10.0 (2026-10-04): indexing reliability, bounded memory, include_folders/exclude_folders search scopes and opt-in Windows DirectML.
  • v4.9.0 to v4.9.2 (2026-09): 15 more file types, watcher and index resilience, and a fix for the reindex loop on Windows stdio clients.

Full history, including the v4.8 releases: CHANGELOG.md.

Community and support

Security reports are acknowledged within 48 hours (SECURITY.md).

License

MIT. You may use, modify and redistribute knowledge-rag, including in commercial products, under its terms. There is no paid edition.

Acknowledgments

Built on these open-source projects:

  • Model Context Protocol: the protocol and its Python SDK
  • ChromaDB: embedded vector database
  • FastEmbed: ONNX embeddings and reranking without PyTorch
  • Hugging Face: model hosting, including the Xenova/ms-marco-MiniLM-L-6-v2 cross-encoder
  • BAAI: the bge-small-en-v1.5 embedding model

Community contributors: @Hohlas, @eeshsaxena, Sergey Khokhlov, and everyone who filed issues or pull requests (all contributors).


Built by Ailton Rocha (Lyon.) · Star the repository if it saves you time · Report an issue · Contribute

knowledge-rag: a local RAG server with hybrid search and reranking for Claude Code, Cursor and other MCP clients.