Skip to content

Releases: lyonzin/knowledge-rag

v4.7.1 — Documentation Hygiene (supersedes v4.7.0)

Choose a tag to compare

@lyonzin lyonzin released this 01 Aug 18:26
c0f3aac

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-rag now picks v4.7.1)
  • NPM: v4.7.0 will be deprecated (npm install knowledge-rag picks v4.7.1; latest re-tagged)
  • Docker: ghcr.io/lyonzin/knowledge-rag:v4.7.0 remains (image tags are immutable), but latest will 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:latest

Full Fix Description

See v4.7.0 CHANGELOG — behavior is identical.

Follow-up to #140 / #143.

v4.7.0 — BM25 Fragment Query Support (YANKED — use v4.7.1)

Choose a tag to compare

@lyonzin lyonzin released this 01 Aug 10:30

⚠️ 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.0 remains (tags immutable); latest points to v4.7.1

Please upgrade:

pip install --upgrade knowledge-rag

Highlights (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 doc

IDF 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.

Closes #140 — full technical writeup in PR #141.

v4.6.0 — MCP spec 2026-07-28 via Anthropic Tier 1 SDK (mcp 2.x)

Choose a tag to compare

@lyonzin lyonzin released this 30 Jul 20:40
b34f210

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.0mcp>=2.0.0,<3.0.0
  • from mcp.server.fastmcp import FastMCPfrom mcp.server import MCPServer
  • FastMCP("knowledge-rag", host=, port=)MCPServer("knowledge-rag", version="4.6.0")
  • host / port now belong to mcp.run() on non-stdio transports (v2 spec direction)
  • Server now advertises version="4.6.0" in serverInfo per 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.py green)
  • 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)

Choose a tag to compare

@lyonzin lyonzin released this 29 Jul 00:29
76500a6

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.md expanded 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 tightened monthlyweekly (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 integration xfail, strict=False, pinned for v4.6.0 wire-up).
  • README ## Unreleased — new ### v4.5.1 entry 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

  1. XXXX placeholder in the OpenSSF badge URL — the project needs to be registered at bestpractices.coreinfrastructure.org so the badge resolves to a real numeric ID.
  2. from mcp_server.security import ... works today, but automatic wiring (validation in add_document, injection defense in parse_file, bearer auth in main()) 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, import and call the primitives yourself.
  3. 14 integration tests in tests/security/ are marked xfail (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.04.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

Choose a tag to compare

@lyonzin lyonzin released this 07 Jul 01:11
f433745

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_knowledge now searches the entire index when the caller omits category_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 — the routed_by field is still populated for telemetry, but the candidate set is never restricted. Explicit category_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 source or filename metadata, 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 TestPathAwareRanking covers 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

Choose a tag to compare

@lyonzin lyonzin released this 06 Jul 00:43
28688e7

Highlights

Cross-platform LLM-client installer (from #108)

  • install.py orchestrator with thin install.sh (Linux/macOS) and install.ps1 (Windows) wrappers — one codebase, one behavior across every OS.
  • Auto-registers knowledge-rag in 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.bak backup; atomic os.replace writes; --dry-run previews 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:
    1. BM25 candidates are metadata-filtered before RRF fusion (with top_k widened to max_results * 20 to compensate for post-filter drop).
    2. The fallback metadata fetch during fusion re-checks category before adding a chunk to combined_scores.
  • The _route_by_keywords()-inferred routing now also filters BM25 (consistency with the semantic branch). If you rely on custom config.yaml keyword routes, this may change the mix of BM25 vs semantic candidates you see.
  • Restart your MCP server after upgrading to invalidate warm query_cache entries that may still contain stale (leaked) results.

Also in this release

  • INSTALLER FIXES: install.ps1 now targets ~/.claude.json (not the stale ~/.claude/mcp.json); gains PyPI mode + mcp_server.server init parity with install.sh; MCP server spec uses standard command + cwd (no more cmd /c cd /d ... && python ... wrapper).
  • INSTALLER FIXES: install.sh guards against sh install.sh; both scripts correctly advertise 13 MCP tools (was outdated at 12); Windows Python bootstrap prefers winget 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). Baseline 227 → 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

Choose a tag to compare

@lyonzin lyonzin released this 22 Jun 17:44
696a12e

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 in ingestion.py:_detect_category, but the validator only accepted what was in config.keyword_routes + config.category_mappings.values(). Users who customized config.yaml and dropped the default "general": "general" mapping hit Invalid category even though the index contained general documents. 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 empty document / metadata into the reranker. Pipeline now continues 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_version bumped 3.11 → 3.12 to accept PEP 695 type syntax 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

Choose a tag to compare

@lyonzin lyonzin released this 17 Jun 21:49

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_status MCP tool for reindex progress polling
  • NEW: reindex_documents runs 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

Choose a tag to compare

@lyonzin lyonzin released this 17 Jun 18:35

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_docid dict 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_mode parameter (default: true)
  • NEW: min_score parameter (default: 0.0)
  • NEW: filtered_by_score + content_length response fields
  • DEPS: rank-bm25 replaced by numpy (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.0

v4.0.0 — Enterprise Concurrent Access

Choose a tag to compare

@lyonzin lyonzin released this 09 Jun 17:14
4168c71

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 sse CLI
  • 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 /metrics endpoint on separate port (disabled by default)
  • All 12 tools instrumented: @rate_limited + @instrument decorators (zero-cost when disabled)
  • --transport CLI override: For Docker/systemd deployments
  • pip 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: 8179

MCP 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

Thanks to @noahcoad for #86.

Full Changelog: v3.9.0...v4.0.0