Releases: lyonzin/knowledge-rag
Release list
v4.7.1 — Documentation Hygiene (supersedes v4.7.0)
Documentation-Only Patch
v4.7.0 used real deployment code names in tests/test_bm25_tokenizer_fragment.py and the CHANGELOG entry describing the fix. Those strings identified a specific production corpus and should not have shipped in a public repository.
This patch replaces them with:
- Synthetic placeholders:
RULE-A00X,PROJECT-Custom001-xxxx - Well-known public identifiers:
CVE-2024-1234,MS17-010,ADR-0003,T1078.002
Zero Behavior Change
mcp_server/server.py is byte-identical to v4.7.0. The tokenizer fix, the digit-required heuristic, and all 28 fragment tests all work exactly the same way.
v4.7.0 Status: YANKED
- PyPI: v4.7.0 is being yanked (
pip install knowledge-ragnow picks v4.7.1) - NPM: v4.7.0 will be deprecated (
npm install knowledge-ragpicks v4.7.1;latestre-tagged) - Docker:
ghcr.io/lyonzin/knowledge-rag:v4.7.0remains (image tags are immutable), butlatestwill re-point to v4.7.1
Upgrade
If you're on v4.7.0, upgrade for the sanitized fixtures/docs — no code or config change is needed on your side.
pip install --upgrade knowledge-rag
# or
npm install knowledge-rag@latest
# or
docker pull ghcr.io/lyonzin/knowledge-rag:latestFull Fix Description
See v4.7.0 CHANGELOG — behavior is identical.
v4.7.0 — BM25 Fragment Query Support (YANKED — use v4.7.1)
⚠️ YANKED — use v4.7.1
This release contained real deployment code names in tests + CHANGELOG examples. Superseded by v4.7.1 which sanitizes those references. The fix behavior is identical between v4.7.0 and v4.7.1.
- PyPI: v4.7.0 is yanked
- NPM: v4.7.0 is deprecated
- Docker:
ghcr.io/lyonzin/knowledge-rag:v4.7.0remains (tags immutable);latestpoints to v4.7.1
Please upgrade:
pip install --upgrade knowledge-ragHighlights (unchanged from v4.7.1)
BM25 tokenizer now supports fragment queries. Before v4.7.0, RULE-A002 was indexed as a single token rule-a002, so queries like A002, RULE, B005 silently returned NO_RESULTS. This hit every hyphenated code taxonomy in typical infosec / doc corpora (RULE-*, CVE-*, ADR-*, MS17-*, PROJECT-Custom001-xxxx).
Now emits both the composite AND its sub-parts of length ≥ 2:
bm25.search("A002") # ← was []; now returns RULE-A002 doc
bm25.search("RULE") # ← was []; now returns all RULE-* family
bm25.search("B005") # ← was []; now returns RULE-B005 docIDF preserves ranking — composite matches still rank above fragment matches (composite is rarer).
Perf-Safety Heuristic
Sub-token expansion triggers only when the composite contains at least one digit. Alphanumeric codes (RULE-A002, CVE-2024-1234, MS17-010) expand — that's the real use case. Natural-language hyphenated phrases (pass-the-hash, state-of-the-art) stay as single tokens. Without this heuristic, concurrent BM25 query throughput regressed +50-60% on corpora containing common infosec vocabulary.
⚠️ Upgrade Note
Run reindex_documents(force=True) once after upgrade to rebuild the BM25 inverted index with the new sub-token emission. ChromaDB vectors are untouched. Expect ~30-50% increase in BM25 index memory — negligible for typical corpora.
v4.6.0 — MCP spec 2026-07-28 via Anthropic Tier 1 SDK (mcp 2.x)
Highlights
Adopts the MCP spec 2026-07-28 — stateless request/response core, per-request _meta.io.modelcontextprotocol/protocolVersion + clientCapabilities, serverInfo on responses — by moving directly to the Anthropic Tier 1 Python SDK (mcp>=2.0.0,<3.0.0).
Delivers the follow-up promised in the v4.5.0-post CHANGELOG note about migrating off mcp.server.fastmcp.
What changed
mcp>=1.6.0,<2.0.0→mcp>=2.0.0,<3.0.0from mcp.server.fastmcp import FastMCP→from mcp.server import MCPServerFastMCP("knowledge-rag", host=, port=)→MCPServer("knowledge-rag", version="4.6.0")host/portnow belong tomcp.run()on non-stdio transports (v2 spec direction)- Server now advertises
version="4.6.0"inserverInfoper the 2026-07-28 spec
@mcp.tool() / @mcp.resource() / @mcp.prompt() decorator ergonomics are unchanged — the migration is a class rename + import path change.
Backwards compatibility
- All 13 MCP tool signatures unchanged (
check_api_surface --check: OK) - Every public function under
mcp_server.*frozen (test_backwards_compat.pygreen) - stdio users see zero behaviour change
- Suite: 417 passed / 0 failed across the 9-cell OS × Python matrix
Why not the third-party fastmcp package
An initial attempt via fastmcp (see closed PR #134) tripped Pillar 5 (perf regression gate): test_bench_orchestrator_idle_rss +21.9% RSS and test_bench_query_cache_5000_entries +17.1% RSS. Cutting the extras to fastmcp-slim[mcp] broke @mcp.tool() at runtime. Anthropic's mcp 2.x is the direct upstream reference — no third-party intermediary, no dependency bloat.
Install / upgrade
pip install --upgrade knowledge-rag # PyPI
npx -y knowledge-rag@4.6.0 # NPM
docker pull ghcr.io/lyonzin/knowledge-rag:4.6.0
Full changelog
See README.md#changelog — v4.6.0 entry.
v4.5.1 — Fase 1 Security Hardening (standalone library)
v4.5.1 — Fase 1 Security Hardening (standalone library, zero behavior change)
Ships the tools; wires them into MCP handlers in v4.6.0. Users motivated to adopt hardening today can import and apply immediately. Existing setups: nothing changes.
🟢 New surface — usable today
A standalone mcp_server.security library that closes three attack classes against untrusted document ingestion. Import explicitly to opt in:
| Defense | Import | Use |
|---|---|---|
| Path traversal + symlink escape (CWE-22 / CWE-59) | from mcp_server.security import validate_path_within, is_path_within, PathEscapeError |
Validate any filepath a client (LLM or human) supplied before touching disk. Rejects .., absolute-outside-base, symlinks whose target leaves the corpus root. |
| Prompt injection (OWASP LLM01:2025) | from mcp_server.security import sanitize_external_content, neutralize_injection_sentinels, wrap_external_content, detect_external_marker |
Wrap content fetched from URLs / dropped files with tagged sentinels — the downstream LLM can distinguish operator-authored context from external content. |
| Unauthenticated HTTP transport (CWE-287) | from mcp_server.security import BearerAuthMiddleware, bearer_token_matches, extract_bearer_token |
Attach BearerAuthMiddleware to a Starlette / FastAPI app. Constant-time comparison. stdio bypasses auth by construction (no network surface). |
| Provenance / integrity | from mcp_server.security import content_sha256 |
Evidence-chain utility for auditing what was indexed vs what was returned. |
🟡 Internal changes (visible only if you look)
- README carries the OpenSSF Best Practices badge (placeholder project ID until the project is registered at bestpractices.coreinfrastructure.org).
SECURITY.mdexpanded with a full defense matrix + threat model..github/SECURITY.md— new. Enables GitHub's built-in "Report a vulnerability" button..github/openssf-best-practices.md— new. Evidence pack for OpenSSF badge registration..github/dependabot.yml— pip schedule tightenedmonthly→weekly(OpenSSF-recommended floor for language ecosystems). Expect ~4× more dependabot PRs.docs/adr/0001-fase1-security-hardening.md— new ADR capturing the design + integration roadmap for v4.6.0.tests/security/— 71 new tests (57 unit green + 14 integrationxfail,strict=False, pinned for v4.6.0 wire-up).- README
## Unreleased— new### v4.5.1entry with the full narrative.
🔴 What does NOT change (why this is a PATCH, not MINOR)
| Behavior | State |
|---|---|
Existing add_document / add_from_url / parse_file / get_document calls |
Byte-identical — no caller in the master imports security yet |
| 13 MCP tool signatures | Preserved 100% (tests/test_backwards_compat.py green) |
| Config schema | Zero change |
| BM25, semantic search, cross-encoder rerank, query cache, GPU support | Zero change |
Dependencies (mcp>=1.6.0,<2.0.0, chromadb, fastembed) |
Zero change |
| Performance | Zero regression |
⚠️ Watch-outs
XXXXplaceholder in the OpenSSF badge URL — the project needs to be registered at bestpractices.coreinfrastructure.org so the badge resolves to a real numeric ID.from mcp_server.security import ...works today, but automatic wiring (validation inadd_document, injection defense inparse_file, bearer auth inmain()) lands in v4.6.0. If you rely on the defenses being applied automatically, wait for v4.6.0. If you're motivated to adopt hardening now,importand call the primitives yourself.- 14 integration tests in
tests/security/are markedxfail(strict=False) — they will turn green automatically when v4.6.0 wires the primitives into the MCP tools.
Migration guide
None required. This is an additive PATCH. Existing installs continue to work with byte-identical behavior.
Full threat model
See docs/adr/0001-fase1-security-hardening.md for the complete ADR — CWE mapping, per-defense contract, integration roadmap.
Version bump: 4.5.0 → 4.5.1 (PATCH — additive library only, zero behavior change on the default install).
Test results on CI: 34 SUCCESS / 0 real failure across 9 OS×Python cells (Linux + macOS + Windows × Python 3.11 + 3.12 + 3.13). Backwards-compat, Semgrep, mypy strict, format smoke matrix, memory baseline, property-based fuzz — all green.
v4.5.0 — Hybrid Search Ranking & Routing Fix
Two search-quality changes ship together in v4.5.0. Both are additive from a public-API standpoint (no signature changes, routed_by field unchanged).
Fixed
search_knowledgenow searches the entire index when the caller omitscategory_filter. The internal keyword router previously acted as a hard where-filter on both semantic and BM25 branches, so a query whose terms happened to map to a sparsely-populated category could return two documents while thousands of relevant chunks in other categories were silently dropped. The router is now informational only — therouted_byfield is still populated for telemetry, but the candidate set is never restricted. Explicitcategory_filter=...continues to filter BM25 consistently with #109. (#112)
Added
- Path-metadata ranking boost in hybrid search. When query terms match a chunk's indexed
sourceorfilenamemetadata, hybrid search now applies a small bounded boost (capped at ~20% of typical RRF magnitudes) before final sorting/reranking. This helps navigational queries surface the canonical file for a topic instead of adjacent files that only cross-reference it. Public API unchanged. (#110, thanks @Hohlas)
Tests
- New
TestKeywordRoutingBehavior(3 tests) pins the routing fix. - New
TestPathAwareRankingcovers the path-metadata boost. - Baseline: 266 → 271.
Upgrade note
Warm query_cache entries from before v4.5.0 should be invalidated by restarting the server so cached responses no longer reflect the pre-fix restrictive behavior.
v4.4.0 — Cross-Platform Installer & Hybrid Search Category Filter
Highlights
Cross-platform LLM-client installer (from #108)
install.pyorchestrator with thininstall.sh(Linux/macOS) andinstall.ps1(Windows) wrappers — one codebase, one behavior across every OS.- Auto-registers
knowledge-ragin 8 LLM clients: Claude Code, Claude Desktop, Cursor, Windsurf, VS Code (Copilot Chat), Cline, Gemini CLI, Zed — each with its canonical config path and correct JSON schema (mcpServers/servers/context_servers). - Idempotent JSON merge with automatic
.knowledge-rag.bakbackup; atomicos.replacewrites;--dry-runpreviews without touching disk. - Flags:
--for <clients>,--exclude <clients>,--dry-run,--list-clients,--pypi-version <ver>,--skip-init,--skip-model.
Hybrid search — category filter now applies to BM25 (from #109, thanks @Hohlas)
- Before this release,
search_knowledge(..., category="X")only filtered the semantic (Chroma) branch. The BM25 branch was queried globally, so keyword hits from other categories could leak into filtered results via RRF fusion. - Both leak paths are now closed:
- BM25 candidates are metadata-filtered before RRF fusion (with
top_kwidened tomax_results * 20to compensate for post-filter drop). - The fallback metadata fetch during fusion re-checks
categorybefore adding a chunk tocombined_scores.
- BM25 candidates are metadata-filtered before RRF fusion (with
- The
_route_by_keywords()-inferred routing now also filters BM25 (consistency with the semantic branch). If you rely on customconfig.yamlkeyword routes, this may change the mix of BM25 vs semantic candidates you see. - Restart your MCP server after upgrading to invalidate warm
query_cacheentries that may still contain stale (leaked) results.
Also in this release
- INSTALLER FIXES:
install.ps1now targets~/.claude.json(not the stale~/.claude/mcp.json); gains PyPI mode +mcp_server.server initparity withinstall.sh; MCP server spec uses standardcommand+cwd(no morecmd /c cd /d ... && python ...wrapper). - INSTALLER FIXES:
install.shguards againstsh install.sh; both scripts correctly advertise 13 MCP tools (was outdated at 12); Windows Python bootstrap preferswinget install Python.Python.3.12 --scope user. - TESTS: +23 (
tests/test_installer_no_data_loss.py× 22 +TestHybridCategoryFilter::test_bm25_results_respect_category_filter× 1). Baseline227 → 267.
Upgrade
PyPI
pip install --upgrade knowledge-rag
NPM
npx -y knowledge-rag@4.4.0
Docker
docker pull ghcr.io/lyonzin/knowledge-rag:4.4.0
Full CHANGELOG in README.md § v4.4.0.
v4.3.1 — Hybrid Search Fixes
PATCH release: hybrid search bug fixes from external contributor @Hohlas, anti-regression tests pinning the contracts, and a CI unblocker.
Fixed
search_knowledge(category="general")no longer rejected on custom configs. The parser hardcodes"general"as the fallback iningestion.py:_detect_category, but the validator only accepted what was inconfig.keyword_routes+config.category_mappings.values(). Users who customizedconfig.yamland dropped the default"general": "general"mapping hitInvalid categoryeven though the index containedgeneraldocuments. Validator now always tolerates"general". (#98)- Stale BM25 chunk IDs no longer leak empty results. When BM25 returned a chunk_id that Chroma could no longer resolve (right after
remove_document, or in the window between async reindex and BM25 rebuild), the previous fallback injected a record with emptydocument/metadatainto the reranker. Pipeline nowcontinues past stale IDs cleanly. (#98)
Internal
- 4 anti-regression tests pin both contracts (
tests/test_pr98_regression.py). Test count baseline: 227 → 231. (#99) [tool.mypy] python_versionbumped 3.11 → 3.12 to accept PEP 695typesyntax in the numpy stub. Static-analysis only; runtime support still>=3.11. (#100)
Compatibility
- 13 MCP tools frozen — all parameter signatures identical to v4.3.0
- No breaking changes (verified by
check_api_surface.py) - Atomic version sync across
pyproject.toml,mcp_server/__init__.py,npm/package.json
Install
```bash
pip install knowledge-rag==4.3.1
or
npx -y knowledge-rag@4.3.1
or
docker pull ghcr.io/lyonzin/knowledge-rag:4.3.1
```
Thanks to @Hohlas for the contribution.
v4.3.0 — Async Reindex, GPU CUDA 12, 13th MCP Tool
Highlights
Async Background Reindex
reindex_documents() now runs in a background daemon thread and returns immediately — no more MCP timeouts on large document sets (5K+ files). Concurrent calls return already_running with current progress.
New MCP Tool: get_reindex_status
Lightweight progress polling without computing full index stats. Returns active/idle status, percent, processed/total, errors, and last result. 13 MCP tools total.
GPU CUDA 12 Support
Full NVIDIA GPU acceleration with automatic DLL discovery. The [gpu] pip extra installs onnxruntime-gpu + 7 CUDA 12 runtime packages. Server runs 4-step GPU verification on startup (providers → DLLs → nvidia-smi → session test) and falls back to CPU gracefully.
What's Changed
- NEW:
get_reindex_statusMCP tool for reindex progress polling - NEW:
reindex_documentsruns async in background thread - NEW: GPU CUDA 12 support with 8 nvidia pip packages and auto DLL path discovery
- NEW: 4-step GPU verification (providers, DLLs, nvidia-smi, ONNX session test)
- DEPS:
[gpu]extra expanded from 3 to 8 packages - FIX: GPU status no longer falsely reports ACTIVE when CUDA DLLs are missing
- DOCS: GPU section rewritten, tool reference updated for 13 tools
- TEST: Backwards-compat baseline updated for 13 MCP tools
Full Changelog: v4.2.0...v4.3.0
Full Changelog: v4.2.0...v4.3.0
v4.2.0 — Search Performance & Output Quality
Search Performance & Output Quality
128× Faster BM25 Search
Custom inverted-index BM25 replaces rank-bm25 full-corpus scan. Only documents containing query terms are scored via posting lists. numpy.argpartition provides O(n) top-k selection instead of O(n log n) sort.
- Batched adjacent chunk fetch — single ChromaDB
collection.get()call replaces N round-trips per result - O(1) reverse lookup via
_source_to_dociddict eliminates linear scans across search, update, and remove operations
Smarter Output
Two new parameters on search_knowledge:
| Parameter | Default | Description |
|---|---|---|
snippet_mode |
true |
Truncates content to ~500 chars at natural break points. Reduces token consumption by ~72%. Adds content_length field with original size |
min_score |
0.0 |
Filters results below normalized relevance threshold (0.0-1.0). Response includes filtered_by_score count |
Both parameters are fully backwards-compatible — existing callers see improved output by default.
Changes
- PERF: Inverted-index BM25 with numpy top-k (128× speedup on 50K+ chunk corpora)
- PERF: Batch adjacent chunk fetch (single ChromaDB call)
- PERF: O(1) source→doc_id reverse lookup
- NEW:
snippet_modeparameter (default:true) - NEW:
min_scoreparameter (default:0.0) - NEW:
filtered_by_score+content_lengthresponse fields - DEPS:
rank-bm25replaced bynumpy(direct dependency) - TEST: 6 new tests + updated backwards-compat baseline
- DOCS: Updated architecture flowcharts, API reference, changelog
CI Status
✅ Quality Gate — 16/16 checks passed (7 pillars)
✅ CI — 9/9 matrix cells passed (Linux + Windows + macOS × Python 3.11/3.12/3.13)
✅ Security — CodeQL passed
✅ 226 tests passed, 0 failed
Install / Upgrade
pip install --upgrade knowledge-rag
# or
npx -y knowledge-rag@4.2.0v4.0.0 — Enterprise Concurrent Access
Enterprise Concurrent Access — SSE/HTTP Transport
The server now supports SSE and streamable-http transport modes. A single server process serves all clients with shared resources — 1 embedding model, 1 ChromaDB, 1 query cache.
New Features
- SSE/HTTP transport:
server.transport: "sse"in config.yaml or--transport sseCLI - Thread-safe shared state: QueryCache locking, BM25 build lock, orchestrator double-checked locking
- ChromaDB WAL mode: Enabled automatically in SSE/HTTP mode for concurrent read performance
- Rate limiting: Optional sliding-window counter (disabled by default)
- Prometheus metrics: Optional
/metricsendpoint on separate port (disabled by default) - All 12 tools instrumented:
@rate_limited+@instrumentdecorators (zero-cost when disabled) --transportCLI override: For Docker/systemd deploymentspip install knowledge-rag[server]: Optional dependency for SSE/HTTP (uvicorn)
Migration
Default transport remains stdio — existing users need zero changes. To enable SSE:
# config.yaml
server:
transport: "sse"
host: "127.0.0.1"
port: 8179MCP client config:
{"mcpServers": {"knowledge-rag": {"type": "sse", "url": "http://127.0.0.1:8179/sse"}}}Also includes v3.9.1 fixes
- Expand
~in config paths (#86) - Accumulate-mode file watcher debounce
- Batched ChromaDB writes (500 chunks/call)
- Reindex concurrency lock
Acknowledgements
Full Changelog: v3.9.0...v4.0.0