Skip to main content
Glama
bloorecall-mcp
by Blooshoo

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, memory and sessions paths 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 needed

Put 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 checks

If 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-mcp

Any 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

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

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

Historical synthetic Recall@5 by system

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.

Separate private local paired coverage sample

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-v1 and baseline-v0 rank a little differently than they did in v1.1.

  • Results no longer carry trust.embedding_fresh. It read the embedding cache, whose entries expire after vector_cache_days, so every file left unedited for two weeks came back weak/[DRIFT]. Verdicts now depend on the file alone.

  • refresh drops 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 verify compares 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.

  • refresh re-chunks every file on its own when chunk_chars, chunk_overlap_chars or parse_version change (they used to need --full), logs every file it skips with a reason in rejections.jsonl next to the index, warns when a root holds more files than its max_files, refuses to run twice at once on one index, and keeps the index files owner-only (mode 600).

  • Config: chunk_chars and chunk_overlap_chars are required (refresh failed without them anyway), the overlap must be shorter than the chunk, and corpus_roots may not be empty.

  • A query holding a pasted key or token is refused like a credential-phrased one, and search --json prints 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…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Provides 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
    • A
      license
      A
      quality
      D
      maintenance
      Enables coding agents to search local notes via BM25 keyword ranking, returning relevant passages with file paths and line numbers, fully offline with zero dependencies.
      3
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables 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.
      2
      1
      MIT