Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **fix(chroma)** — GH #200: stop toggling SQLite `journal_mode=WAL` through a second `sqlite3` connection *after* `chromadb.PersistentClient` is already live. In HTTP/SSE mode the Chroma Rust binding holds a live sqlx pool, so the post-client switch left stale `-wal`/`-shm` handles and the first `Collection.add()` failed during compaction with `SQLITE_NOTADB` (error 26, "file is not a database"). WAL is now switched inside `_init_chroma_client` **before** the client opens the DB, is idempotent (a DB already in WAL is left untouched), pre-creates a fresh install's DB directly in WAL (so run #1 is already WAL, verified against chromadb 1.5.9), and is skipped on network filesystems (NFS/SMB/CIFS) where WAL is unsafe (chroma-core/chroma#7040 caveat), including UTF-8 mountpoints. Read-only startups were unaffected; only incremental writes hit the failure. Reported by @admincheg with an isolated A/B reproduction. 18 regression tests lock the ordering, idempotency, fresh-install pre-create and network-FS guard.
- **fix(config)** — the `init`-generated `config.yaml` now enables 29 formats (code + infrastructure) instead of only `.md`/`.txt`/`.pdf`/`.docx`. The bundled `mcp_server/data/config.example.yaml` template previously shipped an explicit list with the new infra/code formats commented out, so anyone who ran `init` silently opted out of Go/Rust/YAML/Proto/SQL/etc; it now mirrors the root `config.example.yaml` (the 4 office/notebook formats and MetaTrader stay opt-in, commented). Also corrects the stale "20 formats" count to 35 across `README.md` and `docs/ARCHITECTURE.md`, and `12 MCP tools` to 13 in the npm README.
- **fix(search)**: `QueryCache` never hit for the default dispatch path. In `KnowledgeOrchestrator.query()`, the per-result formatting loop reassigns the local name `search_method` (to label each result `hybrid`/`semantic`/`keyword`) after the cache lookup already ran, so the cache write stored under that clobbered value instead of the caller's actual dispatch parameter (`"auto"` by default). Every repeat of the same query missed the cache. The per-result label now uses its own variable; the parameter is untouched for the rest of the function.
- **fix(config)**: GH #208: `KNOWLEDGE_RAG_DIR` set to a relative path (e.g. `./my-data`) or tilde path (`~/rag`) now resolves correctly at startup. `BASE_DIR` applies `.expanduser().resolve()` so all derived paths (`config.yaml`, `data_dir`, `chroma_db`, `documents_dir`, `models_cache_dir`) are always absolute regardless of how the env var was written. Absolute paths are idempotent (no behavior change for existing users). Cross-platform safe — tested on Linux, Windows and macOS.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

### v4.8.5 (2026-08-13) — Enterprise observability: `/health` probes + JSON structured logging (opt-in)

Expand Down
2 changes: 1 addition & 1 deletion mcp_server/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def _is_project_root(path):
_venv_dir = _venv_project_dir()

if os.environ.get("KNOWLEDGE_RAG_DIR"):
BASE_DIR = Path(os.environ["KNOWLEDGE_RAG_DIR"])
BASE_DIR = Path(os.environ["KNOWLEDGE_RAG_DIR"]).expanduser().resolve()
elif _venv_dir is not None and (_venv_dir / "config.yaml").exists():
# Prefer venv parent if it has an actual config.yaml (editable installs, PyPI installs)
BASE_DIR = _venv_dir
Expand Down
42 changes: 42 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"""Tests for configuration integrity."""

import os
from pathlib import Path

from mcp_server.config import _merge_query_expansion_sources, config


Expand Down Expand Up @@ -152,3 +155,42 @@ def test_query_expansion_groups_extend_legacy_entries():
assert merged["tb"] == ["triple barrier", "trip_barr", "legacy_alias"]
assert "tb" in merged["triple barrier"]
assert "trip_barr" in merged["triple barrier"]


class TestKnowledgeRagDirResolution:
"""KNOWLEDGE_RAG_DIR must resolve relative and tilde paths to absolute."""

def test_relative_path_becomes_absolute(self, monkeypatch, tmp_path):
import importlib

import mcp_server.config as config_module

rel = "." + os.sep + "my-rag-data"
monkeypatch.setenv("KNOWLEDGE_RAG_DIR", rel)
monkeypatch.chdir(tmp_path)
importlib.reload(config_module)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

assert config_module.BASE_DIR.is_absolute()
assert config_module.BASE_DIR == (tmp_path / "my-rag-data").resolve()

def test_absolute_path_stays_absolute(self, monkeypatch, tmp_path):
import importlib

import mcp_server.config as config_module

abs_path = str(tmp_path / "rag-store")
monkeypatch.setenv("KNOWLEDGE_RAG_DIR", abs_path)
importlib.reload(config_module)

assert config_module.BASE_DIR == Path(abs_path).resolve()

def test_tilde_path_expands(self, monkeypatch, tmp_path):
import importlib

import mcp_server.config as config_module

monkeypatch.setenv("KNOWLEDGE_RAG_DIR", "~/.knowledge-rag")
importlib.reload(config_module)

Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
assert config_module.BASE_DIR.is_absolute()
assert "~" not in str(config_module.BASE_DIR)
Loading