recall
MCP server exposing memory search, index, and stats tools for agents, with honesty guards to prevent re-litigation of settled decisions.
README
<p align="center"> <img src="docs/banner.png" alt="RE-call β Retrieval-Augmented Self-Recall" width="900"> </p>
<p align="center"> RAG for an AI agent's own memory β that <i>knows when it doesn't know</i>. </p>
<p align="center"> <a href="https://github.com/GiulioDER/RE-call/actions/workflows/ci.yml"><img src="https://github.com/GiulioDER/RE-call/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a> <img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python 3.11+"> <img src="https://img.shields.io/badge/PostgreSQL-pgvector-336791" alt="PostgreSQL + pgvector"> </p>
<p align="center"> <a href="docs/CASE_STUDY.md"><b>π Real-world usage β</b></a> Β Β·Β <a href="docs/USING_WITH_CLAUDE.md">Use with Claude</a> Β Β·Β <a href="docs/WRITEUP.md">Engineering writeup</a> Β Β·Β <a href="docs/RAG_TRAINING_STUDY.md">Fine-tuning study</a> Β Β·Β <a href="#-quickstart-2-minutes-no-api-key">Quickstart</a> </p>
A long-running agent piles up memory β decisions, closed experiments, incident notes. Two failure modes follow: it re-litigates settled decisions, and it hallucinates over gaps where the memory simply has no answer.
RE-call is a RAG engine for that memory, built to be honest about what it doesn't know: it retrieves before the agent acts, and flags when the memory probably has no answer instead of returning confident noise.
β¨ What it does
- π³οΈ Gap-aware β when the best match is weak, it returns a
gap_warning("probable corpus gap β treat as noise") instead of hallucinating an answer. - β±οΈ Freshness-aware β every result reports how stale the index is, so a rotting memory warns instead of silently serving old facts.
- π Anti-re-litigation β meant to be queried before re-proposing an idea, so closed decisions and falsified hypotheses resurface first.
- π§± Production-shaped β PostgreSQL + pgvector, hybrid dense + full-text retrieval fused with RRF, cross-encoder reranking, and an MCP server. Integration-tested on a real database.
π§ How it works
flowchart LR
Q([query]) --> E[embed]
E --> D[dense Β· pgvector cosine]
Q --> S[sparse Β· Postgres full-text]
D --> F[Reciprocal Rank Fusion]
S --> F
F --> R[cross-encoder rerank]
R --> G{honesty guards}
G --> O([hits + gap_warning + freshness])
Dense semantic search and sparse keyword search each retrieve candidates; Reciprocal Rank Fusion merges them, a cross-encoder reranks, and the honesty guards annotate the result before it ever reaches the agent.
β‘ See it work
$ python -m recall.cli demo
indexed 3 chunks from 3 files
[ok] query='what did we decide about caching?'
0.736 corpus/decisions.md '# Decisions ## 2026-05-02 β Caching layer We decided to add a read-thβ¦'
[ok] query='do we inject retrieved context into the prompt?'
0.809 corpus/hypotheses.md '# Hypotheses ## H-014 β Prompt-injected context improves answers (CLOβ¦'
[GAP] query='how do we handle penguins on mars?'
0.468 corpus/decisions.md '# Decisions ## 2026-05-02 β Caching layer We decided to add a read-thβ¦'
The two answerable queries return strong hits (0.74, 0.81). The deliberately-unanswerable one's
best match is only 0.468 β below the 0.50 gap threshold β so it's flagged [GAP] instead of
handing back that irrelevant chunk as if it were an answer. That single flag is the whole thesis.
π Results that matter
A reproducible ablation harness scores every embedder Γ fusion config on a labelled query set β
precision@k, recall@k, MRR, nDCG, and a guard-specific false-confident rate.
<p align="center"> <img src="results/guard_effect.png" width="48%" alt="Guard effect: false-confident rate on unanswerable queries"> Β <img src="results/ndcg_by_config.png" width="48%" alt="Retrieval quality (nDCG@10) by config"> </p>
Three honest findings β including what didn't work:
- π― The gap threshold doesn't transfer across embedders. The default
0.50gives a 0.80 false-confident rate on FastEmbed (its cosines cluster high); per-embedder calibration to~0.70makes the guard perfect. β Calibrate against a small labelled set; don't hard-code. - π Reranking rescues a weak embedder. Hybrid + cross-encoder lifts MRR 0.68 β 1.00 on the offline embedder β but a strong embedder already saturates this corpus, so the gain is real yet situational.
- π§ͺ Fine-tuning the embedder pays off only for a vocabulary gap. A controlled study: on a rich corpus the base already saturates (Ξ +0.00); on an opaque-jargon corpus it can't decode, fine-tuning lifts held-out MRR 0.31 β 0.55 (+79%) and generalizes to unseen paraphrases. β Measure the baseβcorpus gap before fine-tuning. Read the study β
Full methodology + per-embedder tables β results/FINDINGS.md.
β 46 integration tests run against a real pgvector container (no mock DB), verified in CI, with a dependency audit.
π Where this comes from
RE-call isn't a toy β it's extracted from the memory system I run for a production trading-research agent whose own memory outgrew its context window: β660 typed markdown memos (~5 MB), re-indexed daily. Every guard here is a scar from a real failure β re-litigating an already-falsified experiment, trusting weak hits on a question the memory couldn't answer, serving a stale fact.
β Read the redacted case study β the real structure, the guards in action, and exactly what's public vs private.
π Quickstart (β2 minutes, no API key)
git clone https://github.com/GiulioDER/RE-call && cd RE-call
docker compose up -d --wait # Postgres + pgvector (waits until healthy)
python -m venv .venv && . .venv/bin/activate # Windows: .\.venv\Scripts\activate
pip install -e ".[fastembed,dev]"
python -m recall.cli demo
Default embedder is local FastEmbed (no key); --embedder hashing is a fully-offline fallback.
π§ Use it
python -m recall.cli index ./path/to/markdown # index your own docs
python -m recall.cli search "your question" # -> hits + gap/freshness flags
Point RECALL_DSN at any Postgres to use your own database.
Code, not just prose. The engine is content-agnostic β point it at source and it chunks on
def / class boundaries, so natural-language questions land the exact function:
$ python -m recall.cli index ./src --glob "**/*.py" # your codebase
$ python -m recall.cli code # demo: search RE-call's OWN source
indexed 42 code chunks from 16 files
[ok] query='where is reciprocal rank fusion implemented?'
0.805 recall/retriever.py 'def _rrf(rankings: list[list[str]], k: int = 60) -> dict[str, float]:β¦'
[ok] query='how are embeddings stored in postgres?'
0.789 recall/store.py 'class PgVectorStore: """The single, production-grade vector store:β¦'
[ok] query='how does cross-encoder reranking reorder hits?'
0.878 recall/rerank.py 'class CrossEncoderReranker: """Reorder hits by cross-encoder relevβ¦'
π Use it with Claude (MCP)
Expose memory to Claude Code or Claude Desktop as three tools β recall_search,
recall_index, recall_stats β so the agent queries its memory before it acts:
pip install -e ".[fastembed,mcp]"
python -m recall_mcp.server # stdio server
The self-recall pattern: Claude calls recall_search before proposing an idea; if a closed
decision surfaces (and it isn't a gap_warning), it backs off instead of re-litigating.
β Full guide: config for Claude Code + Desktop, the three tools, and a real redacted loop
Β Β·Β example agent: examples/self_recall_agent.py.
π§ͺ Reproduce the evaluation
pip install -e ".[fastembed,rerank,eval]"
make eval # -> results/RESULTS.md + the charts above
The Voyage cloud row appears when VOYAGE_API_KEY is set (shell env, or a gitignored .env).
π§± Tests
docker compose up -d --wait
pytest -v # integration tests hit the real pgvector container β no mock DB
License
MIT.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.