MCP integration
Agent Memory
Give your agent a durable memory that survives context-window resets — facts, decisions and procedures, local-first.
@prom.codes/memory-mcp is the second prom.codes MCP server. Where the
context engine answers "what does this codebase look like?", the memory
server answers "what did we already learn, decide, and agree on?" —
and keeps that knowledge across sessions, context-window resets, and
agent restarts.
How it works
- Local-first SQLite. All records live in one database on your
machine (
~/.prometheus/memory.db), partitioned per project. Nothing is uploaded; the samePROMETHEUS_API_KEYthat unlocks the context engine unlocks the memory server. - Scope chain. Every record is filed under one of four scopes —
project → workspace → tenant → system. Recall walks that chain and, when two records share the same type and key, the narrowest scope wins: a project-level decision overrides a workspace default, which overrides a tenant or system default. That gives you team-wide defaults that any single project can locally override. Both MCP servers derive the project identity the same way (a hash of the workspace root), so they always agree on what "this project" is. - Git-versioned project memories. Project-scoped durable facts are
mirrored to
.prometheus/memories/*.mdunder your workspace root — plain markdown, human-editable, reviewable in pull requests, shared with the team via git. Deleting a record removes its markdown file too, so the mirror never drifts from the database. - Secrets rejected on every write. Before a value reaches the
database or a markdown file it is scanned against a deny-list of
secret shapes — API keys, JWTs, private-key blocks,
Authorizationheaders and connection strings with inline credentials. A match is refused outright, so memory never becomes a place secrets leak into git. - Token-capped recall block.
memory_readdoesn't just return raw rows — itweave()s the resolved records into a single prompt-ready markdown block with a token budget (≈1,500 tokens by default). Records are packed greedily until the cap is hit, so the agent gets the most relevant memory without blowing up the context window.
Memory types
| Type | What it holds | Example |
|---|---|---|
semantic | Durable facts that stay true across sessions | "The staging DB uses connection pooling via pgbouncer" |
procedural | Repeatable how-to knowledge | "Run integration tests with pnpm test:int from the repo root" |
episodic | What happened in a session — plan and outcome | "Refactored the billing module; plan + outcome of the session" |
working | Short-lived scratch notes for the current task | "Currently bisecting the flaky watcher test" |
Tools
The server registers the tools under their short names — read, write,
capture, search, list, delete, setup, status — and your MCP
client namespaces them with the server name you registered (Claude Code:
mcp__memory__read, mcp__memory__write, …). These docs write them as
memory_read etc. for readability; agents resolve either form.
| Tool | What it does |
|---|---|
memory_read | Recall along the scope chain; returns records plus a prompt-ready, token-capped markdown block |
memory_write | Upsert one record (identity: scope + type + key); mirrors project facts to markdown |
memory_capture | Session-end consolidation: plan/outcome → episodic, facts → semantic, procedures → procedural |
memory_search | Full-text search (FTS5) over keys and values, ranked, with highlighted snippets |
memory_list | Flat admin listing with scope/type/key filters |
memory_delete | Delete one record by identity; removes the mirrored markdown file too |
memory_setup | Idempotently install the memory-protocol rules into CLAUDE.md, Cursor, Augment and AGENTS.md configs |
memory_status | Health check: resolved workspace root, project id, DB path, record counts (total + by scope), embedding provider with a zero-cost key probe, and which levers are active |
Setup
Add the memory server next to the context engine — same API key:
claude mcp add memory --env PROMETHEUS_API_KEY=prom_live_… -- npx -y @prom.codes/memory-mcp@latest
The -- separator is required before the command. The workspace root and
database path are auto-detected — don't set them unless you need to point at a
different folder. Mint a real prom_live_… key at
/app/api-keys; that one key unlocks both
the context engine and memory. Full install options (scopes, .mcp.json,
Cursor/VS Code) are on the
Claude Code install page.
Making the agent actually use memory (awareness)
An MCP server only offers tools — the agent uses them only if it knows it should. Nobody is sitting there typing "now read memory," so memory has to make the agent proactive on its own. Three layers do that, strongest last:
- Server instructions — on connect, the server tells the agent the protocol (read at session start, write durable facts as they come up, capture at the end). Hosts that surface MCP instructions (Claude Code) inject this into the model's context immediately. Nudge — same session, host-dependent.
- Auto-installed rule (default on). On startup, the server writes a small,
marked memory-protocol rule block into the runtime config files that already
exist in your project —
CLAUDE.md,AGENTS.md,.cursor/rules/,.augment/rules/. Those files are loaded every session by every host, so the awareness becomes durable and tool-independent. It is idempotent (re-running updates the marked block in place, never touches your own content), never creates a new config file unprompted (only appends to ones you already have), and is skipped when no project is open (home/root). Opt out withPROMETHEUS_MEMORY_AUTO_SETUP=off. memory_setup(explicit). Run it to install the rule into all detected runtimes (incl. creatingAGENTS.mdif you have no config yet) — the manual equivalent of layer 2 with full coverage.
The rule block is the canonical one: recall at session start → store durable
preferences/decisions/corrections as they happen → search when recall is not
specific enough → capture at session end → never store secrets. Check what's
installed (and everything else) with memory_status (rules.installed).
Net effect: add the server to a project that already has a
CLAUDE.md(orAGENTS.md) and memory becomes self-using on the next session — no manual step. In a project with no config file yet, runmemory_setuponce.
Environment variables
| Variable | Required | Default | Purpose |
|---|---|---|---|
PROMETHEUS_API_KEY | yes | — | prom_live_… key minted at /app/api-keys; validated at startup |
PROMETHEUS_WORKSPACE_ROOT | no | auto | Anchors the project identity + .prometheus/memories/ mirror. Auto-detected (0.3.1+) from CLAUDE_PROJECT_DIR / MCP roots; set only to override |
PROMETHEUS_MEMORY_DB_PATH | no | ~/.prometheus/memory.db | Override the shared memory database location |
PROMETHEUS_MEMORY_EMBED | no | auto | Vector channel: auto (Voyage if VOYAGE_API_KEY, else proxy, else keyword) · voyage · prometheus · off |
PROMETHEUS_MEMORY_RERANK_PROVIDER | no | prometheus (when key set) | (0.3.2+; proxy default 0.10.0+) Second-stage cross-encoder for memory_search: prometheus (metered proxy, on by default with your PROMETHEUS_API_KEY — no extra key, provider hidden) · voyage (direct, your own VOYAGE_API_KEY) · bge (self-hosted /rerank via PROMETHEUS_MEMORY_RERANK_ENDPOINT) · none (opt out) |
PROMETHEUS_MEMORY_EXTRACT_PROVIDER | no | none | (0.4.0+) Extraction-at-write LLM for memory_capture: mistral (EU, MISTRAL_API_KEY) · openai (OPENAI_API_KEY) · generic (PROMETHEUS_MEMORY_EXTRACT_ENDPOINT) · none |
PROMETHEUS_MEMORY_REWRITE_PROVIDER | no | none | (0.5.0+) HyDE query-rewrite LLM for memory_search: mistral · openai · generic (PROMETHEUS_MEMORY_REWRITE_ENDPOINT) · none |
PROMETHEUS_MEMORY_TEMPORAL | no | on | (0.7.0+, on by default) Temporal-intent ranking — promotes the newest/oldest matching record for "latest"/"earliest" queries. Pure-local, free; set off to disable. |
PROMETHEUS_MEMORY_TEMPORAL_WEIGHT | no | 2.0 | Blend strength of the timestamp rank vs relevance order (only when temporal is on). |
PROMETHEUS_MEMORY_AUTO_SETUP | no | on | (0.9.0+) On startup, install the memory-protocol rule into runtime config files that already exist (idempotent, marked, never creates a new file; skipped for home/root). Set off to disable auto-writing. |
PROMETHEUS_MEMORY_DEDUP | no | off | (0.8.0+) on collapses near-duplicate records in read/search (restatements of the same fact under different keys / across scopes) before the limit. Pure-local. |
PROMETHEUS_MEMORY_DEDUP_THRESHOLD | no | 0.92 | Embedding-cosine cut above which two records are treated as duplicates (only the text-equality signal applies without embeddings). |
Re-ranking (optional, sharper recall)
memory_search is hybrid: FTS5 keyword ⊕ vector cosine, fused with RRF. On top
of that runs a cross-encoder second stage — and with just your PROMETHEUS_API_KEY
it's on by default through the metered proxy (PROMETHEUS_MEMORY_RERANK_PROVIDER=prometheus):
no extra key, and the upstream model stays hidden. It scores each candidate's
key + value jointly against your query and reorders the top pool before returning.
Best-effort: if the reranker is offline it silently falls back to the hybrid order,
so it never breaks recall. Set PROMETHEUS_MEMORY_RERANK_PROVIDER=none to opt out,
or =voyage to call Voyage rerank-2.5 directly with your own VOYAGE_API_KEY.
On the standard LoCoMo agent-memory benchmark, turning rerank on lifted judge accuracy +8.4 points (38.3% → 46.7%) and matched the full-context ceiling at ≈28× fewer tokens (paired run, n=60).
Extraction at write (optional, cleaner facts)
By default memory_capture stores the facts you pass verbatim. Set
PROMETHEUS_MEMORY_EXTRACT_PROVIDER (mistral for EU, openai, or a generic
OpenAI-compatible endpoint) to also mine durable atomic facts from the freeform
plan/outcome with one LLM call — turning prose like "we moved deploys to
Railway and switched the test command" into clean, retrievable facts. This is the
lever that separates a note-taking memory from a learning one (what Mem0/Zep do).
It is opt-in (default off → no LLM call, fully local). Mined facts are merged
with your explicit facts (your keys win on collision), each is secret-gated
independently, and the whole step is best-effort — an extractor error leaves
capture storing your facts only.
Query rewriting (optional, HyDE)
Set PROMETHEUS_MEMORY_REWRITE_PROVIDER (mistral for EU, openai, or a
generic endpoint) to rewrite a search into question + a short hypothetical memory entry before the FTS/vector channels — pulling the query toward the
wording of the record that answers it. Re-ranking still scores the original
question. Opt-in, best-effort (a failure falls back to the raw query).
It helps most where the model can plausibly generate the answer's vocabulary (temporal/domain questions) and little where it can't (unknowable specifics). On a LoCoMo A/B it was a modest +2.7 pt overall (temporal category 20% → 60%); for coding-project questions it should help more — measure on your workload.
Temporal queries (optional, recency-aware)
memory_search honours an explicit time intent in the query. "What's our
latest decision on auth", "how do we currently deploy", "the first
database we tried" — the answer is the most- (or least-) recently updated
record, but keyword/semantic ranking scores by wording, not time. When (and only
when) the query carries a clear recency/earliest cue, the engine applies a
decisive recency (or antiquity) blend over the records' updated_at as the
final step.
It is on by default because it is pure-local and free (no LLM, no
network) and has no effect on non-temporal queries — a query without a time
cue is returned in its normal relevance order (measured: enabling it leaves
ordinary recall byte-for-byte unchanged). Set PROMETHEUS_MEMORY_TEMPORAL=off
to disable, or tune the strength with PROMETHEUS_MEMORY_TEMPORAL_WEIGHT
(default 2.0).
On a dedicated timestamp probe (planting evolving versions of the same fact),
it lifts Recall@1 on time-intent queries from 0.43 → 0.88 (recency queries
0.15 → 0.90). LoCoMo can't measure this — its temporal answers live in the
record text, not the updated_at metadata this lever reads.
Deduplication (optional, denser recall)
Over a project's lifetime, memory accumulates restatements of the same fact
— "we deploy via Railway" under one key, "deployment target: Railway" under
another, or the same fact written at both project and workspace scope. Scope
resolution only collapses records that share type+key, so these each take a
top-k slot and dilute recall. Set PROMETHEUS_MEMORY_DEDUP=on to collapse them
in memory_read / memory_search before the limit, keeping the
highest-priority representative. Two signals: normalized-text equality (free,
exact) and embedding cosine ≥ PROMETHEUS_MEMORY_DEDUP_THRESHOLD (default
0.92, conservative — only when embeddings are on).
Pure-local and best-effort. On a restatement-polluted recall probe it lifted distinct facts in the top-12 from 5 to 12, and on a set of distinct facts it was byte-for-byte unchanged (it never merges things that aren't duplicates). It is off by default because, unlike temporal ranking, it drops records — opt in once your memory has grown enough to benefit.
What stays on your machine
The index does. Records live in SQLite and project memories live as markdown in
your repo; the only thing committed is what you choose to commit to git. In the
keyword-only default the server makes no network calls at all. If you enable
the vector channel (PROMETHEUS_MEMORY_EMBED) or re-ranking
(PROMETHEUS_MEMORY_RERANK_PROVIDER), only the short query/record text is sent
to the embedding/rerank provider you configured — never your whole database.