intro
Changelog
CHANGELOG.md
Changelog
All notable changes to gnosis-mcp are documented here.
Format follows Keep a Changelog. Versioning follows Semantic Versioning (pre-1.0).
[Unreleased]
Added
Changed
Fixed
Security
[0.13.3] - 2026-04-19
Fixed
- Publish pipeline unblock — every release since v0.11.3 had its
testjob fail onruff format --check(I was runningruff checklocally but notruff format). Becausetestgates every downstream job inpublish.yml, PyPI, MCP Registry, tag creation, GitHub Release, Codeberg mirror, and the Arch-sums auto-PR were all skipped for ten consecutive releases. Net effect: the main branch had code up to v0.13.2 while PyPI was stuck at 0.11.3 and gnosismcp.com rendered v0.11.2. Appliedruff formatto the 9 files it flagged (no logic change, whitespace + line-break normalisation only) and cut this dead-simple patch so the pipeline gets another push to react to. No functional code change versus v0.13.2.
Security
[0.13.2] - 2026-04-19
Changed
- README reshape — Performance section now sits right after Features (was near the bottom of a 700-line file) so the numbers that matter to a new reader hit within the first three screenfuls. Added a
## Performance → Tokens savedparagraph with a livegnosis-mcp savingssample output — evidence for the token-savings claim the landing page makes, now matching in the primary doc. - Configuration table compressed to 5 rows — the full ~40-variable list moved behind a link to
docs/config.mdand the site equivalent atgnosismcp.com/doc/docs/config. Casual readers see the decisions they actually need to make on day one; power users follow one link for the rest. - Transport section cut from 32 lines to 13 — stdio vs HTTP tradeoff stays explicit, the full rationale + systemd/Docker notes moved to
gnosismcp.com/doc/docs/deployment. - REST API section cut to the endpoint table — env vars and curl examples moved to
docs/rest-api.md/gnosismcp.com/doc/docs/rest-api. The endpoint table stays because it's the thing someone scanning for "can I hit this from a web app" needs at a glance.
Fixed
- No code changes; README-only. PyPI re-renders on any README change so the patch bump is required per project policy.
Security
[0.13.1] - 2026-04-19
Fixed
gnosis-mcp savingsno longer raisesOperationalError: no such column: tokens_returnedon databases created before v0.12 (sqlite_backend.py). The v0.12.0 schema migration was tied toinit_schema()— called duringgnosis-mcp init-db, not during normalstartup()— so any existing user who skippedinit-dbafter upgrading hit a hard crash when running thesavingsCLI. Migration now runs fromstartup()via_ensure_post_v0_12_columns()(idempotent ALTER TABLE per missing column), andsavings_report()itself feature-detects the columns and returns all-zeros rather than raising when an unusual schema is encountered. Regression test builds a v0.11.x-shaped table directly, brings up the backend, and asserts the report method doesn't crash.tests/bench/sweep_new_knobs.py— dev-time 4-config sweep script added for anyone investigating the effect ofCOLLAPSE_BY_DOC×MMR_LAMBDAon their own corpus. Runs the same 30 goldens through the full server-side pipeline (backend.search → MMR → collapse-by-doc → truncate) so post-processor effects actually show up. First published numbers on the laptop dev-docs corpus: collapse-by-doc +2.2 nDCG points, MMR λ=0.6 −24 nDCG points + 48× latency (because candidate content re-embedding). MMR is opt-in and the knob works; the bench just doesn't test the use case MMR is designed for (diverse multi-intent queries rather than single-target lookups).
Security
[0.13.0] - 2026-04-19
Added
GNOSIS_MCP_MMR_LAMBDA— opt-in Maximal Marginal Relevance (Carbonell & Goldstein 1998) re-ranking forsearch_docs. Default1.0= pure relevance (current behaviour, exact identity). Any value in(0.0, 1.0)enables greedy diversity-aware reordering: at each step the candidate maximisingλ · cos(q, d) − (1 − λ) · max_{d' ∈ S} cos(d, d')wins, whereSis the already-picked set.λ=0.6is the community default for dev-docs where moderate diversity helps without abandoning relevance. Runs on the top-K candidates after rerank (if enabled) and beforecollapse_by_doc. Embeds candidate content on-the-fly via the existing local embedder — no new dependency, no backend API change. Fail-soft: any exception in the embedding path logs a warning and returns the original order, so misconfiguration degrades instead of breakingsearch_docs.fetch_limitauto-bumps tolimit × 5when MMR is active, mirroring the collapse-by-doc headroom pattern — MMR only diversifies what it's given, so a bigger candidate pool produces a better top-K. No overlap cost when both features are on; the fetch bumps compose viamax.
Security
[0.12.0] - 2026-04-19
Added
gnosis-mcp savingsCLI andsavings_report()backend method. Aggregates the newtokens_returned+tokens_baselinecolumns onsearch_access_loginto a per-tool breakdown of estimated tokens saved. Token count uses a 4-chars-per-token heuristic — close enough to GPT/Claude tokenisers for relative trend tracking without needing a tiktoken dependency. Per-call savings accumulate over time;--days Nwindows the aggregation,--jsonemits the raw shape for piping into observability dashboards. Whenaccess_logis disabled (GNOSIS_MCP_ACCESS_LOG=false) the CLI reports zero with a hint explaining why.search_access_log.tokens_returned+tokens_baselinecolumns (sqlite_schema.py). Populated bysearch_docsandget_docon every call.tokens_returnedis what the caller received;tokens_baselineis what the caller would have spent reading the full document naively.ALTER TABLE ADD COLUMNmigration inSqliteBackend.init_schema()retrofits existing databases idempotently;PostgresBackend.log_access()feature-detects the columns viainformation_schemaand falls back to the old three-column INSERT so pre-0.12 PG schemas keep writing access rows while the user rolls out the new DDL.- README tagline + before/after contrast updated to surface the concrete numbers (5–10× token savings, 92 % Hit@5) up top. Quick Start section now shows both
pip installanduv tool install— the two install paths most users actually take in 2026. - Landing-page (gnosismcp.com) metrics paragraph gained one sentence quantifying the token-cost delta (~300–800 returned vs 3,000–15,000 for a full Read, ~5–10× savings). Structural layout untouched.
Security
[0.11.7] - 2026-04-19
Added
GNOSIS_MCP_COLLAPSE_BY_DOC(server.py). Opt-in top-K deduplication — keeps at most one chunk per distinctfile_path, preserving incoming rank order (so the highest-scoring chunk per doc wins). Rescues the failure mode where a long document with many similar chunks (e.g. a single llms-full.txt that chunks into 1000+ fragments) drowns out the rest of the results. Fetch limit scales tolimit × 5when enabled so there's enough headroom after collapse to still returnlimitdistinct docs. Default off to preserve existing result shapes;GNOSIS_MCP_COLLAPSE_BY_DOC=trueturns it on.GNOSIS_MCP_FTS5_TITLE_WEIGHT+GNOSIS_MCP_FTS5_CONTENT_WEIGHT(sqlite_backend.py). SQLite FTS5 column weights previously hardcoded asbm25(..., 10.0, 1.0)are now configurable via env vars. Defaults preserve the 10:1 title-vs-content ratio so existing rankings don't shift; downstream users with unusual docs (massive titles, wiki-style cross-references, etc.) can rebalance without forking.
Changed
- Dead
__all__entries dropped (schema.py,parsers/git_history.py).SCHEMA_SQL,FileHistory, andGitIngestResultwere re-exported via__all__but never imported by any caller in the repo.SCHEMA_SQLis an implementation detail ofget_init_sql(); the two dataclasses are internal data carriers. Public-API surface stays intact (GitCommitandGitIngestConfigremain exported).
Fixed
- BFS crawl discovery silently swallowed fetch errors (
crawl.py:477). Theexcept Exception: continueblock ate transient HTTP errors during link discovery, leaving users to wonder why their--max-urls 200crawl only pulled 30 pages. Now logs at debug level soLOG_LEVEL=DEBUGreveals the dropped URLs without adding noise at the default level.
Security
[0.11.6] - 2026-04-19
Added
Changed
Fixed
- Local embed provider no longer inherits the OpenAI default model (
config.py).GNOSIS_MCP_EMBED_PROVIDER=localwith noEMBED_MODELused to leaveembed_model="text-embedding-3-small"(the OpenAI default), then pass that intoLocalEmbedder, which tried toGET https://huggingface.co/text-embedding-3-small/…and 401'd. This was the exact failure every first-time user hit after wiring gnosis-mcp into Claude Code.__post_init__now swaps inMongoDB/mdbr-leaf-irwhen provider is local and the user didn't pick a model. ExplicitEMBED_MODELvalues are preserved; non-local providers keep their own defaults. - Auto-embed failure in
search_docs+/api/searchno longer crashes the request (server.py+rest.py). A HuggingFace 401 / network blip / wrong model id used to propagate out of the tool as an unhandled exception, so the whole search failed instead of returning FTS5 results. Both callsites now catchException, log a warning, and fall back to keyword-only — hybrid degrades instead of breaking. - MCP semantic-search env documentation (
llms-install.md). The "Optional: Add Semantic Search" section used to cover CLI usage (gnosis-mcp search --embed) but didn't mention that MCP clients callingsearch_docsneedGNOSIS_MCP_EMBED_PROVIDER=localin the server's env block. Added the missing JSON snippet and a note aboutGNOSIS_MCP_EMBED_MODELoverride + graceful-fallback behaviour.
Security
[0.11.5] - 2026-04-19
Added
Changed
Fixed
- Binary files with a text extension no longer ingest as garbled markdown (
ingest.py). A PNG mistakenly saved asimage.txtused to pass the UTF-8-replace pathway, storing# Image\n\n�PNG\n^Z…as searchable content and polluting both FTS5 and dense indexes with junk tokens. New_looks_binary()helper samples the first 2KB and skips anything containing NUL bytes — the strongest unambiguous not-text signal. - Empty Jupyter notebooks no longer index their raw JSON wrapper (
ingest.py)._convert_ipynbfell back toreturn textwhencells == [], meaning{"cells": [], "metadata": {}, "nbformat": 4, ...}got stored as the document's content. Now returns""so the new post-convert size guard skips it cleanly. - Post-convert size guard applied to every format, not just PDF (
ingest.py). The pre-convert 50-char threshold caught tiny raw files but let converters with vacuous output (empty ipynb, CSVs with only a header row) through to write a single empty chunk. Added the sameif len(md_text.strip()) < 50: skipclause for plain-text / JSON / TOML / CSV / ipynb, mirroring PDF's existing path. tags: [alpha, beta]frontmatter now parses as["alpha", "beta"]instead of["[alpha", "beta]"](ingest.py).parse_frontmattertreats every value as a string and the downstream code did a naivesplit(","), so YAML inline-list syntax leaked the literal[and]characters into the stored tags. New_parse_tags_value()strips enclosing brackets before splitting and also strips surrounding quotes on each entry; tests cover inline-list, comma-separated, quoted, and blank-dropping cases.
Security
[0.11.4] - 2026-04-19
Added
Changed
Fixed
documentation_chunks_vecvectors no longer leak on re-ingest (sqlite_schema.py+sqlite_backend.py). The FTS shadow table had INSERT/DELETE/UPDATE triggers keeping it in lockstep; the vec0 virtual table had none. Everyupsert_docpath (plusdelete_docand the git-history re-ingest) does aDELETE FROM documentation_chunks WHERE file_path = ?followed by fresh INSERTs — old vec0 rows were left behind as orphans pointing to gone chunk IDs. Over months of dogfooding, a dev laptop DB had 4528 vectors vs 4267 chunks (261 orphan leak). Newchunks_ad_vectrigger mirrors the FTS pattern:AFTER DELETE ON documentation_chunks → DELETE FROM documentation_chunks_vec WHERE chunk_id = OLD.id.get_vec0_schema()now returns a list; regression test covers the full upsert-delete-upsert cycle and asserts post-condition count parity. Existing DBs carry their historical orphans until they're re-initialized — there's no automatic migration because the count bug is cosmetic (orphan rows never get returned by KNN since their chunk_ids don't join back to anything indocumentation_chunks).gnosis-mcp diff <path>no longer reports crawled URLs and git-history pseudo-paths as "deleted from disk" (ingest.py).diff_pathscanned the filesystem under<path>and pulled all stored file paths from the DB, then marked anything not found on disk as deleted — but crawled URLs (https://…) and git-history docs (git-history/…) live only in the DB by design. A laptop with ~700 crawled URLs randiff README.mdand saw every URL listed as[-] deleted.prunehad already addressed this via--include-crawled;diffnow filters the same pseudo-paths unconditionally. Regression test seeds a URL and a git-history row alongside a real file and asserts the deleted bucket stays empty./healthdistinguishes docs from chunks (rest.py+sqlite_backend.py+pg_backend.py). Previously the endpoint labeled the post-chunking row count under the"docs"key — on a 700-doc corpus with typical chunking this read as ~4000 docs, throwing off load balancers' expected-range checks and confusing anyone reconciling/healthagainstgnosis-mcp stats. Both backends'check_health()now also returndocs_count(distinctfile_pathcount); the REST response carries both"docs"(distinct) and"chunks"(rows).
Security
[0.11.3] - 2026-04-19
Removed
llms.txt.tmpl+llms-full.txt.tmpl+scripts/render-llms.pyand the correspondingrender-llms.py --checkCI step. Over-engineered for 4 trivial tokens;bump-version.shnow does in-placesedonllms.txt+llms-full.txt. Measured numbers (test count, MCP latency ms) stay maintainer-edited when benchmarks are re-run. One file per concept instead of two. Rationale: the template/rendered pair was added in v0.11.0 assuming more tokens would accumulate; they didn't, so it's just extra surface..mcpregistry_github_token+.mcpregistry_registry_token— stale on-disk tokens from before v0.10.13's security hardening, which moved registry auth to GitHub Secrets. Files were gitignored but still on disk, confusing for anyone finding them..gitignoreentry dropped too (no longer needed).
Changed
.gitignorenarrowed from.claude/to.claude/agent-memory/. Only per-session subagent notes are now ignored; a future.claude/settings.jsonor similar team-tooling config would be trackable without fighting the ignore. No behaviour change today (no such files exist), but removes a papercut for later.llms-install.mdrestructured to cover three install paths explicitly — Path A (Claude Code plugin marketplace, one command), Path B (manual copy-paste for cherry-picking agents/skills), Path C (MCP-server-only for any editor). Previously 202 lines of MCP-only editor snippets with zero plugin coverage; now 292 lines with the plugin path front-loaded as the recommended option. Single canonical install guide —agents/README.mdandskills/README.mdno longer duplicate setup boilerplate, they just link here.README.mdClaude Code Plugin table refreshed — previously listed only 3 slash commands (/search,/status,/manage) out of the 8 we actually ship. Now covers all 8 skills plus the 5 subagents the plugin installs.
Added
/gnosis:evalskill (skills/eval/SKILL.md) — single-shot retrieval quality check that wrapsgnosis-mcp eval, interprets the numbers in plain English (Hit@5 / MRR / nDCG@10 / Precision@5), and compares to a saved baseline stored at~/.local/share/gnosis-mcp/eval-baseline.json. Three modes: default (run + compare + recommend),quick(numbers only),save(lock current as new baseline),diff(compare without advising). Complements/gnosis:tune(which sweeps configurations); eval is the faster health-check.- Codeberg mirror automation in
.github/workflows/publish.yml— a newmirror-codebergjob pushes main + all tags to Codeberg on every tag release. Guarded so the workflow stays green whenCODEBERG_TOKENisn't set; enable by adding the secret plus optionalCODEBERG_REPO/CODEBERG_USERrepo variables. - PKGBUILD ↔ .SRCINFO source-URL drift check in
scripts/check-versions.sh. Catches the class of bug that slipped between v0.11.1 and v0.11.2, where the sha256-only sed fallback left.SRCINFOpointing at a content-hash PyPI path while PKGBUILD had been bumped to the predictable/source/g/form. Normalizes$pkgverin the comparison so baseline state passes. .claude-plugin/plugin.jsonversion-parity coverage. The file was 5 minor versions stale (0.6.0while pyproject was0.11.2) becausebump-version.shnever touched it. Now bumped in lockstep and gated bycheck-versions.sh.
Changed
- Workflow permissions on the repo now allow GitHub Actions to create and approve pull requests. This lets the existing
pypi-resolve-and-arch-prjob finally complete its last step — opening the PR with the computed Arch sha256 — so AUR packaging stays aligned without a manual branch merge.
Fixed
- Contact email unified to
[email protected]across every metadata file. Previously:pkg/arch/PKGBUILD+PKGBUILD-githad an obsolete[email protected], andmarketplace.jsonhad a[email protected]that didn't match the SECURITY / CODE_OF_CONDUCT / CONTRIBUTING / pyproject canonicals. Added anemailfield to.claude-plugin/plugin.jsonas well. One email everywhere now. sqlite://URL prefix now stripped before opening the DB file (sqlite_backend.py). PreviouslyGNOSIS_MCP_DATABASE_URL=sqlite:///path/docs.db(the format shown indocs/config.md,llms-install.md, and used by everytests/bench/*.pyhelper) was passed toaiosqlite.connect()verbatim — Linux treated the colon as a valid filename character and created asqlite:turd-dir relative to the process cwd, with the actual DB buried inside. The server'sbench_real_corpus.pyruns had been quietly leaving~/prod/gnosis-mcp/sqlite:/tmp/gnosis-real-*.dbbehind for months. New helper_sqlite_path_from_url()normalizessqlite:///abs,sqlite:////abs,sqlite://:memory:, and bare paths alike; regression test asserts nosqlite:directory appears in cwd.- Crawl now accepts
text/plainandtext/markdownresponses (crawl.py). Previouslyfetch_pagesilently returnedNonefor any non-text/htmlcontent-type, which bubbled up as a misleading[=] ... 304 Not Modifiedlog line and zero chunks ingested. This excluded the entirellms-full.txtpattern used by MCP, Anthropic docs, Vercel, and other LLM-ready single-file doc bundles. Plain-text bodies now flow through as pre-extracted markdown (trafilatura skipped). TheNone-means-304 ambiguity in the caller is also addressed — when a URL returnsNonewithout having a cache entry, the log now readsunsupported responseinstead of lying about a conditional request that never happened.
Security
[0.11.2] - 2026-04-19
Fixed
- Ruff lint + format failures on
mainthat gated the v0.11.1 publish workflow.tests/bench/bench_beir.pyandtests/bench/bench_sweep.pyhad unusedimport os(F401) and a strayf"..."with no placeholders (F541). Eight files were also overdue forruff format. All auto-fixable. Net effect of v0.11.1: same as v0.11.0 — Docker shipped, PyPI didn't. v0.11.2 is the first version to exercise PyPI + MCP Registry end-to-end. - CI trigger for
mainbranch (.github/workflows/ci.yml). Previous rulebranches-ignore: [main]meant main pushes never ran lint/pytest; v0.11.0 and v0.11.1 both shipped broken because of regressions that CI would have caught on a PR. Switched tobranches: ["**"]so CI runs on every push, closing the loophole.
[0.11.1] - 2026-04-19
Fixed
.github/workflows/publish.ymlYAML parse error in thepypi-resolve-and-arch-prjob introduced in v0.11.0. Agh pr create --body "..."argument spanned multiple lines and contained both a blank line and backtick-escaped inline code — that combination broke YAML's multiline-quoted-string scanner, so the workflow failed at parse time (0 s duration, no job logs) on every push since the job was added. Switched to--body-filewith a heredoc. Net effect of v0.11.0: the Docker image published successfully but PyPI, MCP Registry, and the Arch-sums PR automation never ran. v0.11.1 is the first release that exercises those paths end-to-end.tests/test_local_embed.pycollection error introduced in v0.11.0 by the ONNX filename fallback patch. The test module imported_MODEL_FILESwhich had been renamed to_TOKENIZER_FILES+_ONNX_CANDIDATES; pytest couldn't import the module at all, so 15 tests silently went uncollected (dropping the reported count from 632 to 617 without raising a red flag). CI on the release branch was path-filtered topyproject.tomlonly, which let the regression slip past. Tests now updated to the new API shape; all 632 collect and the 627 non-PG tests pass.
[0.11.0] - 2026-04-18
Changed
- Default chunk size lowered 4000 → 2000 chars (
GNOSIS_MCP_CHUNK_SIZE). Measured on a real 558-doc developer-docs corpus with 25 hand-written golden queries: 2000-char chunks sit on the peak nDCG@10 plateau (0.8702), up from 0.8416 at the old 4000-char default (+3 nDCG points, Hit@5 0.88 → 0.92). Existing corpora keep working; re-ingest with--wipeto regenerate at the new size. Full sweep indocs/bench-experiments-2026-04-18.md. - Documentation hardened on reranker guidance. The bundled
[reranking]cross-encoder remains off by default. New measurements on the same corpus: MiniLM rerank drops nDCG@10 by 27 points and adds 400× latency; BGE-reranker-v2-m3 drops nDCG@10 by 31 points and adds 2400× latency. Search skill now warns explicitly before enabling. Prose-trained rerankers fight reference-style docs; measure on your corpus before turning it on.
Added
- Release pipeline (
scripts/bump-version.sh,scripts/release.sh,scripts/update-arch-sums.sh,scripts/render-llms.py,docs/releasing.md). Single-command version bump across 13 files — pyproject,__init__.py,server.json,marketplace.json,SECURITY.md,CHANGELOG.md,pkg/arch/PKGBUILD,pkg/arch/.SRCINFO,docs/rest-api.md,docs/show-hn.md,demo/hero.tape,skills/setup/SKILL.md,uv.lock. Extendedscripts/check-versions.shnow gates all seven version-bearing files in CI and on every parity check.scripts/release.sh verify <X.Y.Z>polls PyPI / GHCR / MCP Registry / GitHub / AUR after a tag push and reports which are live. - Post-release Arch-sums PR workflow. A new job in
.github/workflows/publish.ymlpolls PyPI until the new sdist resolves, computes the tarball sha256, and opens a PR with the PKGBUILD +.SRCINFOupdate. First-time AUR publish walkthrough indocs/releasing.md§8. - Template-rendered
llms.txt/llms-full.txt. Source lives inllms.txt.tmplandllms-full.txt.tmplwith{{VERSION}}/{{TEST_COUNT}}/{{MCP_MEAN_MS}}/{{MCP_P95_MS}}tokens; CI gates the rendered output for drift. - Agents + skills refresh.
agents/corpus-sync.md(bulk ingest / prune / wipe / crawl / git-history lifecycle with playbooks), updatedagents/doc-keeper.md(single-file CRUD lane), and rewritten skills:skills/search/,skills/ingest/,skills/tune/,skills/manage/. Users can drop these straight into~/.claude/agents/and~/.claude/skills/. - Benchmark experiments log (
docs/bench-experiments-2026-04-18.md): full chunk-size sweep (1000 → 4000 chars), reranker comparison table (keyword, MiniLM, BGE-v2-m3, mxbai-large), hybrid-vs-keyword on vocabulary-matched corpora, with raw numbers and methodology. tests/bench/bench_real_corpus.py --embed-model / --embed-dimflags — swap the embedder for A/B retrieval-quality shoot-outs against any model that ships an ONNX artefact on HuggingFace.GNOSIS_MCP_ALLOW_PRIVATE_CRAWL=trueenv override for the crawl SSRF guard — useful for local dev, CI, and Docker-internal testing. Mirrors the existingGNOSIS_MCP_WEBHOOK_ALLOW_PRIVATEpattern. Off by default.- ONNX filename fallback in
local_embed.py— triesonnx/model_quantized.onnxfirst, thenonnx/model.onnx. Lets the embedder load any HF repo that ships either variant. /evalshort-answer tooling exposed through thegnosis-mcp evalCLI and thetuneskill — measures Hit@K / MRR / Precision@K on a user's own corpus in under a second.- Docker image published to GHCR (
ghcr.io/nicholasglazer/gnosis-mcp), multi-arch (linux/amd64, linux/arm64), built by.github/workflows/docker.ymlon every tag push. - Landing site refresh at gnosismcp.com: honest feature copy, fact-checked benchmark card, graph visualization driven by live
documentation_linksdata,llms.txt+llms-full.txtserved at the root for AI-assistant ingestion. - Rewritten demo GIFs.
demo/demo-hero.gif(440 KB) anddemo/demo-crawl.gif(584 KB). Real commands only — no fake version echoes, nopip installduring recording, no backgrounded-server noise. Source (VHS tape + staging scripts) lives in.internal/demo-source/.
Fixed
llms.txtandllms-full.txtnow reflect the actual test count (632) and the current SDK version.- CHANGELOG ordering:
[Unreleased]stays at the top;All notable changes…preamble moved out of the release block so it doesn't get rolled into a version section on the next bump.
[0.10.13] - 2026-04-17
Security
- Timing-safe Bearer token comparison in REST API auth (
secrets.compare_digest). - Webhook SSRF guard:
GNOSIS_MCP_WEBHOOK_URLnow refuses private, loopback, link-local, multicast, and reserved addresses unlessGNOSIS_MCP_WEBHOOK_ALLOW_PRIVATE=true. - HuggingFace model download: enforced
https://huggingface.co/origin assertion, SHA-256 checksum verification scaffolding for the bundled default model. - robots.txt cross-host redirect now treated as disallow (prevents redirect-based spoofing).
- Content size caps:
upsert_docrejects content overGNOSIS_MCP_MAX_DOC_BYTES(default 50 MB);search_docsrejects queries overGNOSIS_MCP_MAX_QUERY_CHARS(default 10 000). - Dependency upper bounds pinned on
mcp,aiosqlite,asyncpg,onnxruntime,tokenizers,numpy,sqlite-vec,httpx,trafilatura,docutils,pypdf— major-version bumps can no longer slip in.
Added
- Typed relations (
relations:frontmatter block): documents can now declare semantic edge types beyond the flatrelates_to:list. Supported types:related,prerequisite,depends_on,summarizes,summarized_by,extends,extended_by,replaces,replaced_by,audited_by,audits,implements,implemented_by,tests,tested_by,example_of,references. Unknown types warn and are skipped. Stored in the existingrelation_typecolumn; queryable viaget_related(relation_type=...)andget_graph_stats(). No schema migration needed —relation_typecolumn already existed. - CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md for OSS community hygiene.
- PR CI workflow (
.github/workflows/ci.yml): ruff + pytest on Python 3.11 & 3.12, SQLite backend green-gated, PostgreSQL backend + pgvector service container (allow-failure during backend parity ramp-up). - Version-parity CI gate (
scripts/check-versions.sh): fails the release workflow ifpyproject.toml/__init__.py/server.json/marketplace.jsondrift. - Tag-vs-pyproject assertion in
publish.yml— av*tag push whose name disagrees withpyproject.tomlfails early. - End-to-end MCP protocol tests (
tests/test_mcp_e2e.py): spawngnosis-mcpsubprocess, drive it through stdio MCP, assert 9 tools + 3 resources + write/read roundtrip +gnosis-mcp checkintegration. - Three benchmark suites in
tests/bench/:bench_search.py(speed),bench_rag.py(retrieval quality — Precision@K, MRR, Hit Rate, keyword vs hybrid),bench_mcp_e2e.py(protocol round-trip latency). gnosis-mcp evalCLI subcommand — runs the retrieval-quality harness and prints Hit@K / MRR / Precision@K in ~1 s. Short answer to "show me the numbers".gnosis-mcp prune <path>— deletes DB chunks whose source file no longer exists on disk. Scoped to the given root so crawled URLs are untouched by default (--include-crawledto also prune them).--dry-runpreviews.gnosis-mcp ingest --prune— after ingest, prune stale docs in the same pass.gnosis-mcp ingest --wipe— nuke every document before re-ingesting (nuclear reset for re-organized knowledge folders).[reranking]optional extra with ONNX cross-encoder reranker (onnx-community/ms-marco-MiniLM-L6-v2-ONNX, 22 M params, Apache 2.0). Off by default; enable viaGNOSIS_MCP_RERANK_ENABLED=trueor thererank=truetool parameter.GNOSIS_MCP_RRF_Kenv var to tune hybrid-search Reciprocal Rank Fusion (default 60, the canonical value)./healthREST endpoint now exposessearch_stats(total / misses / hybrid / keyword counters).- Benchmarks doc (
docs/benchmarks.md) with methodology, scale curve to 10 000 docs, RAG-native metrics, PostgreSQL reproduction steps, and regression gates. - Dependency floors bumped:
mcp>=1.27,aiosqlite>=0.22,asyncpg>=0.30,onnxruntime>=1.22,tokenizers>=0.22,numpy>=2.0,sqlite-vec>=0.1.6,httpx>=0.28,trafilatura>=2.0,docutils>=0.22,pypdf>=5.0. Upper bounds retained. - Pytest markers (
sqlite_only,postgres_only,eval,bench,e2e) registered inpyproject.toml. GNOSIS_MCP_CRAWL_EXTRACT_TIMEOUT_S(default 30 s) caps per-page trafilatura extraction.- First-run guidance:
gnosis-mcp searchagainst an empty database now hints to rungnosis-mcp ingest <path>first. - REST request logging middleware: method, path, status, duration_ms at INFO (skips
/health). - Windows install paths documented in
llms-install.md.
Changed
- MCP protocol round-trip latency improved ~35 % (13.3 ms → 8.7 ms mean, 24.4 ms → 13.0 ms p95) via
mcpSDK upgrade to 1.27. - Ingest format dispatch refactored to a registry (
_CONVERTERSiningest.py) — adding a new format is now a single map entry. - Duplicate search-result dict construction in
server.pyextracted to_format_search_result()helper. - Tests gate the publish workflow (
publish.yml): ruff check + pytest must pass before PyPI upload. - MCP Registry publish step now has explicit error handling, timeout, and binary integrity check.
- vec0 initialization failure is now fail-fast when
GNOSIS_MCP_EMBED_PROVIDERis configured (silent degradation hid hybrid-search breakage). - Resource error responses include exception type and a
hintto rungnosis-mcp check. _search_customPostgreSQL fallback narrowed toasyncpg.UndefinedFunctionError,AmbiguousFunctionError,InvalidParameterValueError(was catching every exception).
Fixed
/healthnow bypasses Bearer auth even whenGNOSIS_MCP_API_KEYis set — monitoring probes and load balancers were previously broken by returning 401. Regression test added intests/test_rest_auth.py.- Documentation claim alignment: corrected test count, format count, and tool count across README,
llms.txt,llms-full.txt, anddocs/show-hn.md. - README links to
llms-install.mdfrom the Quick Start section. .coverage,htmlcov/,coverage.xmladded to.gitignore.- Removed orphaned
demo.gif(not referenced) and straysqlite:/directory (CLI-misparse artefact).
[0.10.12] - 2026-04-07
Added
- Enriched
get_relatedtool: Multi-hop traversal (depth=1-3), relation type filtering, and optional title/category enrichment viainclude_titles. get_graph_statstool: Knowledge graph topology — orphans (disconnected docs), hubs (most connected), relation type distribution, edge/node counts.- Content link extraction:
ingestnow parses[text](path.md)markdown links and[[wikilinks]]from body content, stored ascontent_linkrelation type. GET /api/graph/statsREST endpoint: Same functionality as the MCP tool.gnosis-mcp fix-link-typesCLI command: Migrate existing git-history links from genericrelates_toto propergit_co_changeandgit_reftypes.
Changed
- Git history ingest now uses
git_co_change(cross-file) andgit_ref(source file) relation types instead of genericrelates_to.
[0.10.11] - 2026-04-07
Added
get_contexttool: Usage-weighted context loading — surfaces most-accessed documents for efficient session startup. Supports topic search enrichment, category filtering, and repository statistics.- Access tracking:
search_access_logtable automatically records document access fromsearch_docs(top 3 results) andget_doc. Fire-and-forget pattern, opt-out viaGNOSIS_MCP_ACCESS_LOG=false. GET /api/contextREST endpoint: Same functionality as the MCP tool, available when REST API is enabled.gnosis-mcp cleanupCLI command: Purge old access log entries (--days N, default 90).
[0.10.10] - 2026-04-07
Added
- Example agents for Claude Code: doc-explorer, doc-keeper, doc-reviewer, context-loader (
agents/directory) search_git_historysection in search skill with--gitflag- Git history and web crawl sections in setup skill
- All optional install extras documented in setup skill (
[embeddings],[web],[formats])
Fixed
- Skills: updated tool count from 6 to 7 across all skills (search_git_history was missing)
- Search skill: corrected hybrid search note — SQLite also supports hybrid via sqlite-vec RRF
- Manage skill: added missing
audienceparameter to upsert_doc and update_metadata examples - CLAUDE.md: corrected tool count from 6 to 7 in architecture diagram
Changed
- Updated deps: mcp 1.27.0, onnxruntime 1.24.4, numpy 2.4.4, ruff 0.15.9
[0.10.9] - 2026-04-07
Fixed
has_column()on PostgreSQL: switch frominformation_schema.columnstopg_catalog.pg_attribute— fixesupsert_docNotNullViolationError on Supabase and other PostgreSQL deployments where role permissions filter information_schema visibility. This was the actual root cause of the content_hash bug that v0.10.8 attempted to fix.
[0.10.8] - 2026-04-01
Fixed
upsert_docMCP tool: compute content_hash (SHA-256) on insert, fixing NotNullViolationError on PostgreSQL deployments with NOT NULL constraint on content_hash column- SQLite
upsert_doc: same content_hash fix, withhas_column()detection for backwards compatibility
[0.10.7] - 2026-03-24
Added
- Comparison table in README: gnosis-mcp vs Context7 vs Grounded Docs vs mcp-local-rag — feature-by-feature positioning
- Submitted to tolkonepiu/best-of-mcp-servers directory (knowledge-and-memory category)
[0.10.6] - 2026-03-23
Fixed
- MCP Registry publish pipeline:
server.jsontransport field was an array (invalid) — changed to single object per schema. This fixes 17 consecutive CI failures since v0.7.3 and restores registry updates.
[0.10.5] - 2026-03-23
Added
- Always-on
/healthendpoint for HTTP transports (streamable-http, sse) — no longer requires--restflag. Returns{"status": "ok", "version": "...", "transport": "..."}. Full REST API (/api/*) still requires--rest.
[0.10.4] - 2026-03-22
Added
- Transport guide in README: explains stdio vs HTTP tradeoffs, why stateful servers benefit from HTTP sharing, and how to configure multi-session setups
[0.10.3] - 2026-02-23
Fixed
- Custom search function disambiguation: add explicit
NULL::vectorcast forp_embeddingparameter when no query embedding is provided, fixingAmbiguousFunctionErrorwith PostgreSQL databases that have multiple overloaded search function signatures
[0.10.2] - 2026-02-23
Fixed
- CORS preflight now works when API key auth is enabled (middleware ordering fix: CORS outermost, auth innermost)
- Added test coverage for
/api/docs/{path}/relatedendpoint - Removed no-op
TYPE_CHECKINGblock fromrest.py --restwith stdio transport now logs a warning instead of silently ignoring
[0.10.1] - 2026-02-23
Changed
- Documentation: added REST API usage to README, llms.txt, llms-full.txt, CLAUDE.md
[0.10.0] - 2026-02-23
Added
- REST API: Native HTTP endpoints alongside MCP —
GET /api/search,/api/docs/{path},/api/docs/{path}/related,/api/categories,/health - Enable via
--restflag onserveorGNOSIS_MCP_REST=trueenv var - Optional CORS support via
GNOSIS_MCP_CORS_ORIGINS(comma-separated origins or*) - Optional API key auth via
GNOSIS_MCP_API_KEY(Bearer token in Authorization header) create_rest_app()factory for standalone REST appcreate_combined_app()factory for MCP + REST on same port- Hybrid search auto-embeds queries when local provider is configured
[0.9.13] - 2026-02-23
Added
- Search benchmark script:
tests/bench/bench_search.py— automated QPS, latency percentiles, hit rate measurement - Performance section in README: ~9,800 QPS (100 docs), ~3,500 QPS (500 docs), p50 under 0.25ms
- Performance data added to
llms.txtandllms-full.txt - Install size noted: ~23MB with
[embeddings], ~5MB base - Test count: 550+ tests, 10 eval cases (90% hit rate, 0.85 MRR)
[0.9.12] - 2026-02-23
Added
- Cross-file commit graph links: When a commit touches files A, B, C, their git-history docs now link to each other via
relates_to _build_cross_file_links()pure function for computing shared-commit relationships- Links created automatically after
ingest-gitcompletes, enrichingget_relatedresults
[0.9.11] - 2026-02-23
Added
- Git history eval cases: 5 new eval cases for commit messages, author emails, and file changes
- 4 sample git-history documents added to eval fixture (auth, db, tests, pyproject)
- Eval harness now covers 10 cases total: 90% hit rate, 0.85 MRR on sample corpus
[0.9.10] - 2026-02-23
Added
search_git_historyMCP tool: Dedicated tool for searching git commit history with post-filters- Scoped to
git-historycategory automatically — no need to passcategoryparameter - Filters:
author(name/email substring),since,until,file_path(path substring) - Over-fetches 3x then post-filters for accurate author/file matching
[0.9.9] - 2026-02-23
Added
--authorfilter foringest-git: Filter commits by author name or email (e.g.--author Alice)--untilfilter foringest-git: End date filter complementing existing--since(e.g.--until 2026-02-20)
[0.9.8] - 2026-02-23
Added
- Author email in git history:
git lognow captures%ae(author email) alongside%an(author name) - Rendered markdown includes
Author: Name <email>for better searchability GitCommitdataclass gainsauthor_emailfield
[0.9.7] - 2026-02-23
Fixed
- Empty query handling:
search()now validates input — empty/whitespace-only queries return empty list with warning log - File path search fallback: When FTS5 returns 0 results and query contains
/or., falls back tofile_path LIKEsearch search_docsMCP tool returns descriptive error for empty queries instead of silent empty result
[0.9.6] - 2026-02-23
Added
--forceflag foringest-git: Re-ingest all files ignoring content hash, matchingingest --forcebehavior
[0.9.5] - 2026-02-23
Fixed
- RST
includedirective crash:_convert_rst()now disablesfile_insertion_enabledandraw_enabledin docutils settings - RST files with
.. include::or.. raw::directives no longer crash ingestion - Added
except Exceptionfallback that returns raw text with warning log on any docutils failure
[0.9.4] - 2026-02-23
Changed
- Title boosting in FTS5:
bm25()now weights title column 10x over content column - Searches matching a document's title rank significantly higher than content-only matches
- SQLite backend only (PostgreSQL uses ts_rank with different weight mechanism)
[0.9.3] - 2026-02-23
Added
- Contextual chunk headers: Embedding text now includes
"Document: {path} | Section: {title}"prefix - Embeddings capture hierarchical document context, improving retrieval accuracy for ambiguous queries
contextual_header()pure function exported fromembed.pyget_pending_embeddings()now returnstitleandfile_pathalongsideidandcontent- Re-embed existing docs to benefit:
gnosis-mcp embed --provider local
[0.9.2] - 2026-02-23
Added
- Query logging: Every
search_docscall logs query, mode (keyword/hybrid), result count, top result path, score, and category - Search stats counters: In-memory
_search_statsdict tracks total searches, misses (zero results), and search mode breakdown - Enables search quality monitoring: watch for rising miss rate or declining scores
[0.9.1] - 2026-02-23
Added
- Search quality eval harness:
tests/eval/with Precision@K, MRR, and Hit Rate metrics - JSON-driven test cases (
tests/eval/cases.json) — add query-answer pairs to measure retrieval quality - Baseline eval: 5 cases, 100% hit rate on sample docs
- Runs as part of
pytest tests/eval/ -v— no extra dependencies
[0.9.0] - 2026-02-23
Added
- Git history ingestion:
gnosis-mcp ingest-git <repo-path>converts commit history into searchable markdown documents - Commit messages, authors, dates, and file associations parsed from
git logvia subprocess (zero new deps) - One markdown document per file, each commit as an H2 section — flows through existing chunk/embed/search pipeline
- Stored as
git-history/<file-path>with categorygit-historyfor scoped searches - Auto-linking to source file paths via
relates_tograph - Content hashing for incremental re-ingest (skips files with unchanged history)
- CLI flags:
--since,--max-commits,--include,--exclude,--dry-run,--embed,--merges - New
src/gnosis_mcp/parsers/package for non-file ingest sources - 48 new tests (pure function + integration with temp git repos)
[0.8.4] - 2026-02-22
Changed
- README restructure: funnel layout (hook → proof → features → install)
- Added before/after framing section ("Without a docs server" / "With Gnosis MCP")
- Replaced prose "Why use this" with scannable feature bullets
- Wrapped CLI reference, ingestion details, architecture in collapsible sections
- Added PyPI monthly downloads badge
- Improved tagline: "Turn your docs into a searchable knowledge base for AI agents"
- Trimmed visible content from 334 to 290 lines while preserving all information
[0.8.3] - 2026-02-22
Fixed
- README readability: rewrote intro sections, collapsed editor integrations
- Factual errors: transport values, DATABASE_URL naming, hybrid search scope
- llms.txt DATABASE_URL consistency
[0.8.2] - 2026-02-22
Fixed
- SECURITY: SSRF protection — blocks private/internal IPs (127.x, 10.x, 192.168.x, ::1, metadata endpoints) and checks redirect targets
- SECURITY: XML size limit (10 MB) in sitemap parser to prevent billion-laughs-style attacks
- SECURITY: Response size guard (50 MB) in
fetch_pageto prevent memory exhaustion - SECURITY: Cache file written with 0o600 permissions (owner-only read/write)
- BUG:
asyncio.CancelledErrorno longer swallowed in_crawl_single— properly re-raised (Python 3.11+ treats it asExceptionsubclass) - BUG:
save_cachemoved tofinallyblock — cache data preserved even on errors or cancellation - Atomic cache writes using
tempfile.mkstemp+os.replace— no corruption on crash asyncio.gatherusesreturn_exceptions=True— single task failure no longer aborts all tasks- robots.txt parsed once per crawl session (
RobotFileParserreused), not re-parsed per URL - Nested sitemap index fetches now run in parallel via
asyncio.gather - BFS discovery respects
max_urlscap on queue size (prevents unbounded memory growth) - Crawl depth clamped to max 10 in
CrawlConfig.__post_init__ - Debug log on robots.txt fetch failure (was silent
pass)
Added
CrawlActionStrEnum for type-safe action values (crawled,unchanged,skipped,error,blocked,dry-run)_is_private_host()SSRF protection function_parse_robots()for one-time robots.txt parsingTYPE_CHECKINGannotations forhttpx.AsyncClient,DocBackend,GnosisMcpConfigCounterusage in CLIcmd_crawlfor cleaner action counting- 30+ new tests: SSRF, CancelledError, depth clamping, atomic writes, cache permissions, BFS cap, StrEnum, oversized responses
[0.8.1] - 2026-02-22
Fixed
extract_content()now runs trafilatura in a thread pool (run_in_executor) to avoid blocking the event loop during CPU-bound HTML extraction- BFS discovery uses
collections.dequeinstead oflist.pop(0)— O(1) popleft vs O(n) shift - Nested sitemap index detection simplified from fragile double-negative to
len(nested) == len(all) - Silent
except: passon link insertion replaced withlog.debug()for troubleshootability
Added
--max-urlsflag (default: 5000) caps discovered URLs to prevent runaway memory on large sitemaps
[0.8.0] - 2026-02-22
Added
- Web crawl for documentation sites:
gnosis-mcp crawl <url>ingests docs from the web - Sitemap.xml discovery (
--sitemap) and BFS link crawling (--depth N) - robots.txt compliance — respects
Disallowrules automatically - ETag/Last-Modified HTTP caching for incremental re-crawl (304 Not Modified)
- URL path filtering with
--includeand--excludeglob patterns - Dry run mode (
--dry-run) to discover URLs without fetching - Force re-crawl (
--force) ignoring cache and content hashes - Post-crawl embedding (
--embed) for hybrid semantic search - Rate-limited concurrent fetching (5 concurrent, 0.2s delay by default)
- New optional dependency extra:
pip install gnosis-mcp[web](httpx + trafilatura) - Crawl cache at
~/.local/share/gnosis-mcp/crawl-cache.json - Crawled pages stored with URL as
file_path, hostname ascategory
[0.7.13] - 2026-02-20
Fixed
- PostgreSQL multi-word search now uses OR (was AND) — parity with SQLite v0.7.9 fix
- Added
content_hashcolumn to PostgreSQL DDL for new installations
Added
- E2E comparison test script for SQLite vs PostgreSQL backend parity
- 21 new unit tests:
ingest_path,diff_path, links, highlights, config defaults, PG OR query - Test suite now at 300+ tests
[0.7.12] - 2026-02-20
Added
- Optional RST support:
pip install gnosis-mcp[rst](docutils) - Optional PDF support:
pip install gnosis-mcp[pdf](pypdf) - Combined
[formats]extra:pip install gnosis-mcp[formats] - Dynamic extension detection:
.rstand.pdfauto-enabled when deps installed
[0.7.11] - 2026-02-20
Added
- GitHub Releases: CI now creates GitHub releases with auto-generated notes
- Ingest progress:
[1/N]counter in log output during file ingestion
[0.7.10] - 2026-02-20
Added
- CSV export format:
gnosis-mcp export -f csv gnosis-mcp diffcommand: show new/modified/deleted files vs database state
[0.7.9] - 2026-02-20
Changed
- FTS5 multi-word search now uses OR instead of implicit AND for broader matching
- BM25 ranking still puts multi-match results first
[0.7.8] - 2026-02-20
Fixed
GNOSIS_MCP_CHUNK_SIZEenv var now passed tochunk_by_headings()(was parsed but ignored)
Added
--forceflag forgnosis-mcp ingestto re-ingest unchanged files
[0.7.7] - 2026-02-20
Changed
- Replaced
huggingface-hubdependency with stdliburllib.request(~60 lines) - Fixed CI release pipeline: combined auto-tag + publish into single
publish.yml
Removed
huggingface-hubfrom[embeddings]extra (5 → 4 optional deps)
[0.7.6] - 2026-02-20
Added
- Multi-format ingestion:
.txt,.ipynb,.toml,.csv,.json(stdlib only, zero extra deps) - Each format auto-converted to markdown for chunking
[0.7.5] - 2026-02-20
Added
- Streamable HTTP transport (
--transport streamable-http) GNOSIS_MCP_HOSTandGNOSIS_MCP_PORTenv vars--hostand--portCLI flags forservecommand
[0.7.4] - 2026-02-20
Changed
- Smart recursive chunking: splits by H2 → H3 → H4 → paragraphs
- Never splits inside fenced code blocks or tables
[0.7.3] - 2026-02-20
Added
- Frontmatter
relates_tolink extraction (comma-separated and YAML list) - Links stored in
documentation_linkstable, queryable viaget_related
[0.7.2] - 2026-02-20
Added
- Search result highlighting:
<mark>tags in FTS5 snippets (SQLite),ts_headline(PostgreSQL)
[0.7.1] - 2026-02-20
Added
- File watcher:
--watchflag forgnosis-mcp serveauto-re-ingests on file changes - Auto-embed on file change when local provider configured
[0.7.0] - 2026-02-19
Added
- Local ONNX embeddings via
[embeddings]extra (onnxruntime + tokenizers + numpy) - sqlite-vec hybrid search with Reciprocal Rank Fusion (RRF)
gnosis-mcp embedCLI command for batch embedding backfill--embedflag oningestandsearchcommands- Auto-embed queries when local provider configured (MCP server)
[0.6.3] - 2026-02-18
Added
- VS Code Copilot and JetBrains editor setup docs
[0.6.2] - 2026-02-18
Added
- MCP Registry badge and automated registry publish in CI
[0.6.1] - 2026-02-18
Added
- MCP Registry verification tag and
server.json
[0.6.0] - 2026-02-17
Added
- SQLite as zero-config default backend (no PostgreSQL required)
- FTS5 full-text search with porter stemmer
- XDG-compliant default path (
~/.local/share/gnosis-mcp/docs.db) gnosis-mcp checkcommand for health verification
[0.5.0] - 2026-02-16
Added
- Embedding support: openai, ollama, custom providers
- Hybrid search (keyword + cosine similarity) on PostgreSQL
- Demo GIF in README
[0.4.0] - 2026-02-15
Changed
- Rebranded from stele-mcp to gnosis-mcp
- Published to PyPI
- Added configurable tuning knobs via env vars
[0.3.0] - 2026-02-14
Added
- Structured logging
get_docmax_length parameter- Safer frontmatter parsing
[0.2.0] - 2026-02-13
Added
- Resources (
gnosis://docs,gnosis://categories) - Write tools (upsert, delete, update_metadata)
- Multi-table support (PostgreSQL)
- Webhook notifications
[0.1.0] - 2026-02-12
Added
- Initial release: PostgreSQL backend, search_docs tool, ingest command