Long-term memory MCP server for LLMs — hybrid search in a single SQLite file
Project description
rekal
Long-term memory for LLMs, as an MCP server.
Every conversation starts from scratch — your LLM forgets what you told it yesterday. rekal is a small MCP server that stores memories in a single SQLite file and retrieves them with hybrid search. Install it, point your MCP client at it, and your LLM starts remembering things between sessions.
pip install rekal
Why I built this
I got tired of repeating myself. "I prefer Ruff." "We deploy with tags." "The auth service lives in services/auth." Every new conversation, same explanations.
Existing memory tools either do keyword search (which misses anything phrased differently) or vector search (which misses exact terms). I wanted both, plus a bias toward recent memories so stale stuff sinks naturally. And I wanted it in a single file I could back up by copying.
How search works
rekal runs three searches and blends the results:
score = 0.4 · BM25(keyword match)
+ 0.4 · cosine(semantic similarity)
+ 0.2 · exp(-t/half_life)
So a memory about "deploying the auth service to staging" shows up whether you search for "deploy auth" or ask about "shipping the login system to pre-prod". Recent memories rank higher, but old ones still surface if they're relevant.
Embeddings run locally with fastembed — no API keys, no network calls.
Quick start
Add to your MCP client config (Claude Desktop, Cursor, Claude Code, etc.):
{
"mcpServers": {
"rekal": {
"command": "rekal"
}
}
}
rekal creates ~/.rekal/memory.db on first run. That file is your entire memory — portable, backupable, yours.
Requires Python 3.14+.
What it looks like in practice
Your LLM picks up on things worth remembering and stores them:
User: "I prefer Ruff over Black for formatting"
LLM: → memory_store("User prefers Ruff over Black", type="preference")
Weeks later, in a completely different conversation:
User: "Set up linting for my new project"
LLM: → memory_search("formatting linting preferences")
← "User prefers Ruff over Black" (score: 0.92)
When knowledge changes, it doesn't just pile up — old versions stay linked:
LLM: → memory_supersede(old_id="mem_abc", new_content="API moved from v2 to v3")
And when things contradict each other, you can ask:
LLM: → memory_conflicts(project="backend")
← "use PostgreSQL for everything" contradicts "migrate analytics to ClickHouse"
Tools
rekal exposes 16 tools over MCP.
Core
| Tool | Description |
|---|---|
memory_store |
Store a memory with type, project, tags, and conversation scope |
memory_search |
Hybrid search — BM25 + vector + recency in one query |
memory_update |
Update content, tags, or type (re-embeds automatically) |
memory_delete |
Delete a memory by ID |
Smart write
| Tool | Description |
|---|---|
memory_supersede |
Replace a memory while preserving the old one as history |
memory_link |
Link memories: supersedes, contradicts, related_to |
memory_build_context |
Relevant memories + conflicts + timeline for a query, in one call |
Introspection
| Tool | Description |
|---|---|
memory_similar |
Find memories similar to a given one |
memory_topics |
Topic summary grouped by type |
memory_timeline |
Chronological view with optional date range filters |
memory_related |
All links to and from a memory |
memory_health |
Database stats — counts by type, project, date range |
memory_conflicts |
Find memories that contradict each other |
Conversations
| Tool | Description |
|---|---|
conversation_start |
Start a conversation, optionally linked to a previous one |
conversation_tree |
Get the full conversation DAG |
conversation_threads |
List recent conversations with memory counts |
conversation_stale |
Find inactive conversations |
Memory types
Memories are tagged with a type so search can be scoped:
| Type | For | Example |
|---|---|---|
fact |
Things that are true | "The API rate limit is 1000 req/min" |
preference |
How the user likes things | "Prefers dataclasses over hand-written __init__" |
procedure |
Steps to do something | "Deploy: git tag vX.Y.Z && git push --tags" |
context |
What's going on right now | "Currently rewriting the payment service" |
episode |
Things that happened | "Debugged the OOM — root cause was unbounded cache" |
Architecture
Everything lives in one SQLite file:
rekal
│
SQLite ──┬── FTS5 index ──── keyword relevance (BM25)
├── sqlite-vec ──── semantic similarity (384d vectors)
├── recency ─────── exponential decay (30-day half-life)
└── memory links ── supersedes / contradicts / related_to
Conversations form a DAG — follow-ups, branches, merges — so you can navigate interaction history the way you'd navigate a Git log.
Claude Code skills
rekal ships two optional Claude Code skills that improve how your LLM uses its memory:
| Skill | Trigger | What it does |
|---|---|---|
rekal-save |
Auto on session end, or /rekal-save |
Extracts durable knowledge from the conversation, deduplicates against existing memories, stores or supersedes |
rekal-hygiene |
/rekal-hygiene (manual only) |
Finds conflicts, duplicates, stale data, and quality issues. Proposes fixes for your approval — never auto-deletes |
Install the skills plugin
# In Claude Code:
/plugin marketplace add janbjorge/rekal
/plugin install rekal-skills@rekal
The skills require rekal to be configured as an MCP server (see Quick start above).
CLI
rekal serve # Run as MCP server (default)
rekal health # Database health report
rekal export # Export all memories as JSON
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rekal-1.1.0.tar.gz.
File metadata
- Download URL: rekal-1.1.0.tar.gz
- Upload date:
- Size: 81.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3b629e0c92be77a04ab20b53d25e488ce0f7b2794770917cd7ee480730feeee
|
|
| MD5 |
643f87841d504abfb413f80027c22d41
|
|
| BLAKE2b-256 |
9a761baa12f999ad958721e1f3fe382f5ae6b522e581b2e3070ce29f67e6d7de
|
Provenance
The following attestation bundles were made for rekal-1.1.0.tar.gz:
Publisher:
release.yml on janbjorge/rekal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rekal-1.1.0.tar.gz -
Subject digest:
b3b629e0c92be77a04ab20b53d25e488ce0f7b2794770917cd7ee480730feeee - Sigstore transparency entry: 1281149689
- Sigstore integration time:
-
Permalink:
janbjorge/rekal@853a99bf2f0b3a751e484def738c05a01936eb6e -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/janbjorge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@853a99bf2f0b3a751e484def738c05a01936eb6e -
Trigger Event:
push
-
Statement type:
File details
Details for the file rekal-1.1.0-py3-none-any.whl.
File metadata
- Download URL: rekal-1.1.0-py3-none-any.whl
- Upload date:
- Size: 19.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b50855bb3c351231ee68bc1ecd0638bbe1a3451303de6b41b64ec3ca742c45b1
|
|
| MD5 |
2e2a921a7a60c834e0dc69b654b8748f
|
|
| BLAKE2b-256 |
62114e26821be95bf70f6c5e016a6e9d95b60858e95656a93658de18c568fda7
|
Provenance
The following attestation bundles were made for rekal-1.1.0-py3-none-any.whl:
Publisher:
release.yml on janbjorge/rekal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rekal-1.1.0-py3-none-any.whl -
Subject digest:
b50855bb3c351231ee68bc1ecd0638bbe1a3451303de6b41b64ec3ca742c45b1 - Sigstore transparency entry: 1281149698
- Sigstore integration time:
-
Permalink:
janbjorge/rekal@853a99bf2f0b3a751e484def738c05a01936eb6e -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/janbjorge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@853a99bf2f0b3a751e484def738c05a01936eb6e -
Trigger Event:
push
-
Statement type: