The memory server that forgets responsibly — and can tell you what it used to believe.
Every output in this demo is real engine output — it's generated by
scripts/make_demo_gif.py running the actual
server code against a throwaway database.
Claude forgets everything when you start a new chat. Most memory servers fix that by hoarding: everything saved forever, every recall dumped into context, no notion of whether a memory is still true. Chronos takes the opposite stance — memory that behaves like memory:
- Time-travel:
query_atreconstructs what Claude knew at any past timestamp, including content that has since been edited. "What did we know about the auth system on March 1st?" is a one-tool answer. - Forgetting curves: every memory carries a confidence score and an FSRS
retention model (the algorithm behind Anki). Unreviewed memories decay; confirmed
ones stabilize.
consolidate_memoriesruns a four-phase "dream" pass that merges duplicates, decays stale beliefs, and prunes what the system has lost faith in. - Honest deletion:
forgethides (auditable, reversible),purgedestroys (content removed from every table, content-free audit stub left behind).
Everything is one local SQLite file. No accounts, no API keys, no cloud, no embedding-model downloads. Search is SQLite's built-in FTS5/BM25 with porter stemming, kept in sync with content by database triggers — the index can't drift, even on a crash.
1. Install
git clone https://github.com/Kodaxadev/ChronosMCP
cd ChronosMCP
pip install -e .2. Add to Claude Desktop
Open claude_desktop_config.json and add:
{
"mcpServers": {
"chronos": {
"command": "python",
"args": ["/absolute/path/to/ChronosMCP/chronos_mcp.py"],
"env": {
"CHRONOS_DB_PATH": "/absolute/path/to/chronos.db"
}
}
}
}Claude Code (CLI):
claude mcp add chronos -- python /absolute/path/to/ChronosMCP/chronos_mcp.pyRestart Claude Desktop and you're done.
Remember that we always squash commits before merging to main.
What do you know about our Git workflow?
What did you know about the auth system as of March 1st?
How confident are you in that memory about the rate limit?
Run a consolidation pass and show me what's gone stale.
Permanently delete the memory with my old API key in it.
| Tool | What it does |
|---|---|
remember |
Saves a note with optional project, tags, and source provenance. |
recall |
BM25 + stemming search, re-ranked by FSRS retention × confidence. Optional token_budget for compressed results. |
get_memory |
Fetches one memory in full by id — never truncated. |
update_memory |
Rewrites content; the old version is snapshotted for time-travel. |
related_memories |
"More like this" — finds memories sharing distinctive vocabulary. |
query_at |
Time-travel recall at any past timestamp, resolving edited content. |
forget / restore_memory |
Reversible soft-delete with a persisted reason. |
purge_memory |
Irreversible hard-delete of content everywhere it lives. |
| Tool | What it does |
|---|---|
set_confidence / boost_confidence / weaken_confidence |
Evidence-driven trust scoring (0.01–0.99) with a full audit trail. |
review_memory |
FSRS review — strengthens a memory's stability so it decays slower. |
get_memory_health |
Confidence, retention probability, review history for one memory. |
log_search_feedback / search_effectiveness |
Tracks which recalls actually got used; recommends tuning. |
consolidate_memories |
Four-phase dream pass: orient → gather duplicates → merge/decay/flag → prune. Dry-run by default. |
An event-sourced project graph (tasks, dependencies, causal analysis over
outcomes) ships behind CHRONOS_ENABLE_GRAPH=1. It is quarantined rather than
deleted: the structural-similarity signal proved weak in practice (see
docs/KNOWN_LIMITATIONS.md), and the core memory
server is better without the surface area. Enable it if dependency-ordered
task suggestions (suggest_next_tasks) are worth it to you.
| Variable | Default | Description |
|---|---|---|
CHRONOS_DB_PATH |
./chronos.db |
Path to the SQLite database file |
CHRONOS_DB_KEY |
unset | Passphrase for SQLCipher at-rest encryption (needs the [encryption] extra) |
CHRONOS_SEMANTIC |
unset | Set to 1 for hybrid semantic search (needs the [semantic] extra) |
CHRONOS_SEMANTIC_MODEL |
BAAI/bge-small-en-v1.5 |
Embedding model when semantic search is on |
CHRONOS_ENABLE_GRAPH |
unset | Set to 1 to enable the experimental graph layer |
By default search is lexical (BM25 + porter stemming) — zero extra dependencies, instant startup. If you want "car" to find a memory about an automobile:
pip install "chronosmcp[semantic]" # local ONNX model, no API keys, no torchthen set CHRONOS_SEMANTIC=1 in the server env. Retrieval becomes hybrid:
BM25 candidates and embedding nearest-neighbors (a ~70MB local model,
downloaded once) are merged with Reciprocal Rank Fusion, so exact-term matches
and meaning matches both surface. Results gain a semantic_similarity field.
First startup on an existing database embeds your whole corpus — minutes,
once; everything after is write-through and milliseconds.
Chronos is a local, single-user tool. Its threat model is honest rather than reassuring:
- Plaintext at rest by default; SQLCipher opt-in. Without a key, the
database is an unencrypted SQLite file — don't store credentials, and use
purge_memory(notforget) when something must actually disappear. Withpip install "chronosmcp[encryption]"andCHRONOS_DB_KEYset, the entire file (content, history, FTS index, WAL) is SQLCipher-encrypted. The PBKDF2 cost is paid once at startup, not per operation. Migrate an existing database withpython scripts/encrypt_db.py encrypt. Know the trade: the key lives in an environment variable (readable by local processes running as you), and losing it means losing the data — there is no recovery. - Memories are data, not instructions. Recall results carry a
sourcefield (user,claude,web,document). Content that originated outside the conversation should never be followed as a directive — a poisoned memory is a persistent prompt injection, and provenance is your tripwire. The tool descriptions instruct Claude accordingly. - Injection-safe queries. Free text never reaches FTS5 MATCH syntax or SQL — queries are reduced to quoted terms, and every statement is parameterized.
- No network by default. The base server makes zero outbound connections —
the only imports are
mcp,numpy, and the standard library. The one exception is opt-in:CHRONOS_SEMANTIC=1downloads its embedding model from Hugging Face once, after which inference is fully local.
remember writes one row. A SQLite trigger indexes it into an FTS5 table in the
same transaction — there is no in-memory index, no startup rebuild, and no window
where the database and the search index disagree. recall ranks with BM25
(porter-stemmed), then re-ranks by an FSRS boost: memories that are confident
and recently confirmed surface first, while stale unverified ones sink.
update_memory snapshots the old content into a version table before
overwriting. query_at replays that history: it selects the memories that
existed at the requested instant, resolves each one's content as of that moment,
and ranks the reconstructed snapshot with an ephemeral BM25 index — so past and
present searches score the same way.
The cognitive layer is the part that makes Chronos different over months of use. Memories you confirm get more stable (slower decay); memories that sit unreviewed lose confidence on every consolidation pass; memories that are both untrusted and faded become prune candidates. The system's beliefs converge toward what you actually use and verify — and every confidence change is written to an audit table, so you can always ask why it believes what it believes.
Out of the box, search is lexical (BM25 + stemming) — a deliberate
zero-dependency trade-off. The optional [semantic] extra upgrades recall to
hybrid lexical+vector retrieval with a local model (see Configuration);
remaining boundaries are documented in
docs/KNOWN_LIMITATIONS.md.
pip install -e ".[dev]"
pytest tests/ -v # 48 tests
ruff check .CI runs the suite on Linux and Windows across Python 3.10/3.12/3.14.
Architecture notes live in docs/ARCHITECTURE.md;
the v3.3 → v4.0 redesign rationale is in CHANGELOG.md.
The README demo regenerates with python scripts/make_demo_gif.py
(needs pip install pillow) so it can never drift from real behavior.
MIT
