bloorecall-mcp
Uses a local Ollama instance to generate dense embeddings for semantic search over indexed notes, enabling meaning-based retrieval alongside BM25 keyword scoring. Search falls back to keyword-only mode when Ollama is unavailable.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
BlooRecall
Local, private "where did I write that?" search for your notes — built for coding agents.
BlooRecall indexes your Markdown / text / reStructuredText and finds files by meaning and keyword, not just filename. It blends BM25 lexical scoring with dense embeddings from a local Ollama model, so a search for "how the gateway retries a dropped socket" finds the note that says "exponential backoff on reconnect" even with no shared words.
It ships a CLI for you and an MCP server for your coding agent, over one engine. Everything runs on your machine — no cloud, no API keys, nothing uploaded.
$ bloorecall search "how do we retry a dropped socket"
1. 1.08 ~/notes/gateway/reconnect-design.md
notes · 2026-08-12 · [OK] disk hash matches
Reconnect design > Backoff: on disconnect the client waits with exponential backoff...Why
Finds notes by idea, not filename. Great when you remember what you wrote, not where.
Local and private. Embeddings come from Ollama on
127.0.0.1; BlooRecall only reads your corpus (read-only) and only writes inside its own project dir.Agent-native. An MCP tool (
bloorecall_search) lets an AI assistant look things up itself instead of making you paste context. Pairs well with code-structure tools like sem: sem knows your code, BlooRecall knows your notes.Trustworthy results. Each hit is re-checked against the file on disk and flagged
[OK]or[DRIFT], so an agent never quotes a stale snippet as current.Heading-aware. Markdown chunks carry their heading trail (
Design > Phased build > P0), so a paragraph matches queries whose words only live in the section titles.Safe by default. Credential-shaped queries are refused;
secrets,credentials,.env,memoryandsessionspaths are skipped during indexing.
Related MCP server: local-recall-mcp
Install
Requires Python 3.12+, numpy, the mcp SDK, and a running local Ollama with an embedding model.
git clone <this repo> && cd bloorecall
pip install numpy mcp
ollama pull qwen3-embedding:0.6b # any Ollama embedding model works; set it in config
python3 -m unittest discover -s tests # no network neededPut bin/ on your PATH for a short command, or run ./bin/bloorecall in place.
Quick start
bloorecall init --root ~/notes --root ~/projects # write config.json for your folders
bloorecall refresh # build the index (incremental after the first run)
bloorecall search "restic backup runbook"
bloorecall status # index size, model, freshness
bloorecall doctor # config / ollama / index / MCP checksIf Ollama is down, search still works in keyword-only mode and says so.
Use it from a coding agent (MCP)
bloorecall serve (or bin/bloorecall-mcp) is an MCP stdio server exposing one tool,
bloorecall_search(query, top_k=10, project=None). It returns ranked paths with snippets and a
trust note — never whole files.
Claude Code / any client with claude mcp:
claude mcp add bloorecall -- /abs/path/to/bloorecall/bin/bloorecall-mcpAny client with an mcpServers config:
{
"mcpServers": {
"bloorecall": { "command": "/abs/path/to/bloorecall/bin/bloorecall-mcp", "args": [] }
}
}BLOORECALL_CONFIG=/path/to/config.json selects a non-default config.
How it works
Index (
bloorecall refresh): walk your corpus roots, apply safety gates, chunk each file (heading-aware for Markdown), embed changed chunks via Ollama, store vectors + a BM25 index in SQLite. Incremental: an unchanged file is skipped by size/mtime, an edited paragraph re-embeds one chunk. A no-change refresh is ~instant.Search: embed the query, rank every chunk by meaning (dense) and by keyword (BM25), and fuse the two orders with reciprocal-rank fusion, plus small recency and path-match bonuses (a named, editable policy; a path match is a query word naming the file or one of its folders under the corpus root). Fusing ranks rather than raw scores keeps either signal from drowning out the other. Keep the best chunk per file, re-rank the top results with a term-coverage bonus, then verify each hit against disk. If the embedder is down, or the index was built with a different model than the config names, search ranks by keyword alone and says why.
Benchmark
Against a one-pass grep baseline (what an agent gets from a single rg search) on a 251-file
synthetic corpus, BlooRecall v1.1 reaches Recall@5 0.97 vs grep's 0.73 (MRR@10 0.89 vs
0.58), and matches or beats grep in every query category.
v1.0 shipped a linear blend that, it turned out, let BM25 overrule the embeddings (0.87 / 0.77,
behind embeddings alone at 0.95 / 0.86). v1.1 fuses ranks instead: it sits level with
embeddings-only on meaning-style queries and keeps keyword search's edge on exact identifiers.
The fusion was picked on the dev split (python3 -m bench.sweep) and confirmed once on test;
the report has the diagnosis.
v1.2 fixes ranking bugs rather than retuning weights. The tokenizer kept a sentence's full stop
(backoff. never matched backoff; 7% of the corpus's tokens), and whole paths were one token,
so the path-match bonus almost never fired. Fusion also counted function words ("the", "a") as
keyword evidence, and gave a chunk with no keyword match nothing from BM25, ranking it below
every chunk that shared a word with the query. Short texts show it worst: on a
probe of 40 haiku with queries that share no content word with their
poem, v1.1's fusion scored MRR@10 0.29 where embeddings alone scored 0.85. v1.2 drops function
words from the BM25 query and ranks unmatched chunks just after the last match: 0.69.
Measured on the same harness with a stand-in embedder (all-MiniLM-L6-v2; the work was done
without Ollama), v1.2 against v1.1 on the test split: shipped-policy Recall@5 0.936 → 0.984
(3 queries gained, none lost), MRR@10 flat (−0.010, 95% CI [−0.057, +0.040]), keyword-only
nDCG@10 +0.041 (CI [+0.001, +0.084]); the grep baseline is byte-identical. The qwen3 numbers here
and in the report are v1.1's; python3 -m bench.run --system all --split test --fresh then
python3 -m bench.report brings them up to date.
Corpus coverage repair (2026-11-04)
Opt-in balanced root allocation prevents one first-level worktree from exhausting a broad project root. Existing configs keep newest selection. Configuration, anonymized evidence and limitations.
This local N=23 sample is not comparable to the historical synthetic benchmark above. It measures sample-specific designated-source coverage, not global accuracy or a new model/ranking result. No speedup is claimed.
Synthetic corpus and queries, one 0.6B embedding model, 63 test queries: treat the gaps between
systems as the signal, not the absolute numbers. Setup, per-category results, an independent
red-team review and one-command reproduction are in bench/REPORT.md.
Configuration
config.json (write a starter with bloorecall init). Key fields: corpus_roots (read-only
folders with a label and max_files), allowed_extensions, embedding (Ollama model +
endpoint), policies / active_policy (retrieval weights and fusion: rrf, or raw for the
v1.0 blend), exclusions, search_defaults. All paths are absolute; the index lives under the
project's var/.
Upgrading from v1.0: a policy without a fusion key keeps the v1.0 blend, so a policy id
always means one behavior. To get v1.1's ranking, copy the hybrid-rrf-v1 block from
config.example.json into your policies and set "active_policy": "hybrid-rrf-v1".
Upgrading to v1.2: nothing to run. Search re-tokenizes an older index in memory, and the
next bloorecall refresh rewrites it on disk without re-embedding (its summary counts
retokenized chunks). Behavior changes:
Ranking: the fixes in Benchmark apply to every policy, so
hybrid-rrf-v1andbaseline-v0rank a little differently than they did in v1.1.Results no longer carry
trust.embedding_fresh. It read the embedding cache, whose entries expire aftervector_cache_days, so every file left unedited for two weeks came backweak/[DRIFT]. Verdicts now depend on the file alone.refreshdrops the files of a root you remove from the config. A root that is only missing on disk (an unmounted drive) still keeps its files until it comes back.Keyword-only search (embedder down) returns only files that share a word with the query, instead of padding the list by recency.
Shallow
verifycompares each file's size and mtime with the index (it used to check only that files exist), and files that refresh refuses on content (secret-shaped, not UTF-8, blank) no longer count as unindexed drift.refreshre-chunks every file on its own whenchunk_chars,chunk_overlap_charsorparse_versionchange (they used to need--full), logs every file it skips with a reason inrejections.jsonlnext to the index, warns when a root holds more files than itsmax_files, refuses to run twice at once on one index, and keeps the index files owner-only (mode 600).Config:
chunk_charsandchunk_overlap_charsare required (refresh failed without them anyway), the overlap must be shorter than the chunk, andcorpus_rootsmay not be empty.A query holding a pasted key or token is refused like a credential-phrased one, and
search --jsonprints a refusal as JSON.
Keeping it fresh
bloorecall refresh is incremental and idempotent: an edit re-embeds only the chunks whose text
changed, however long the file sat untouched. scripts/bloorecall-refresh.sh is a cron-ready
wrapper (its header has a sample crontab line). bloorecall verify runs a read-only drift audit
against size and mtime, --deep re-hashes every file (exit 1 on drift);
bloorecall doctor --verify-drift adds the deep audit to the health check.
Status
v1.2. Tests run locally, no network or Ollama needed. Born as a throwaway tool for personal knowledge-base research that turned out to be genuinely useful, so it's shared in case it helps you too.
License
MIT — see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Recall authorized Vault notes with keyword and hybrid search, original excerpts, and source paths.
Token-efficient search for coding agents over public and private documentation.
Shared memory for coding agents. Stop re-explaining your codebase every session.
Search your Obsidian vault to quickly find notes by title or keyword, summarize related content, a…
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables Claude Code to search and retrieve from a local knowledge base of markdown notes using hybrid semantic+keyword search, keeping data entirely offline.7 npmMIT
- AlicenseNot gradedqualityCmaintenanceProvides fully local long-term memory for AI agents by enabling semantic search over notes and session logs using Ollama embeddings, with no external APIs or databases.MIT
- AlicenseAqualityDmaintenanceEnables coding agents to search local notes via BM25 keyword ranking, returning relevant passages with file paths and line numbers, fully offline with zero dependencies.3MIT
- AlicenseAqualityBmaintenanceEnables coding agents to query local notes, decisions, docs, and code with hybrid retrieval (BM25 + embeddings + reranking) and get path:line citations. It provides tools like rag_query for full-corpus search and search_knowledge for project-scoped knowledge recall.21MIT