engram
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@engramremember that this repo uses pnpm, never npm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Your coding agents forget everything the moment a session ends. You re-explain the package manager, the deploy command, the naming convention, the reason you rejected that library — every single time.
engram is a long-term memory store that lives in one plain JSONL file on your machine. Any agent that speaks MCP can read and write it. No server, no database, no embeddings, no API keys, no telemetry.
$ engram add "Releases run pnpm build, then rsync to prod-01" --kind decision --tags deploy
+ saved mvevxbqm9dce
$ engram context "how do I reach prod-01"
<memory>
- [decision] Releases run pnpm build, then rsync to prod-01
</memory>Contents: Why · Features · Install · Quick start · Connect your agent · CLI · Ranking · Data model · MCP · Recipes · FAQ
Why
Agents are stateless. Every new session starts from zero, so you spend the first ten turns re-establishing context you already gave.
Cloud memory is a lock-in. Your preferences and project decisions are not a subscription product.
Greppable beats magical. You can
cat,git diff, back up and hand-edit the whole store. Nothing is hidden in a vector index.
How it compares
engram | Cloud memory APIs | Vector databases | |
Storage | one JSONL file you own | hosted service | database + embedding model |
Dependencies | none (Node built-ins) | SDK, account, network | server, model, index |
Retrieval | BM25, deterministic | opaque | semantic, approximate |
Works offline | yes | no | usually not |
Hand-editable | yes | no | no |
Best at | local agent context | managed cross-device memory | large semantic corpora |
Related MCP server: zipmem-mcp
Features
One file, zero dependencies —
~/.engram/memory.jsonl, plain JSONL, human-readable and diff-friendly.BM25 retrieval with CJK support — Chinese/Japanese/Korean text is indexed as character unigrams and bigrams, so
缩进 偏好matches偏好 tab 缩进without a segmenter.Explainable ranking — relevance × importance × recency × scope match.
--explainprints every multiplier.Memory lifecycle — update in place,
--replacesto supersede a stale fact (history stays queryable),pruneto clean up.Safe with many agents — advisory lock + read-modify-write + atomic rename, so parallel writers never lose memories.
Built for agents — 8 MCP tools, 2 resource templates, and an
injectcommand for prompt hooks.Scriptable —
--jsoneverywhere, stable exit codes, stdin/stdout friendly.
Install
npm install -g engram # provides the `engram` binary
# or run without installing
npx -y engram --helpRequires Node.js ≥ 18.
From source:
git clone https://github.com/PHclaw/engram.git
cd engram
npm install
npm run build
node dist/index.js helpQuick start
# Save what matters
engram add "This repo uses pnpm, never npm" --kind decision --tags tooling
engram add "Indent with 2 spaces, never tabs" --kind preference --tags style --importance 4
engram add "缩进用 2 个空格,不要 tab" --kind preference --tags style,zh --importance 4
# Find it again
engram search "pnpm"
engram search "缩进" --limit 3 --explain
# Get a block you can paste into a prompt
engram context "how should I indent" --max-chars 800
# Review and maintain
engram list --all-scopes
engram stats
engram doctorConnect your agent
engram mcp speaks MCP over stdio (newline-delimited JSON-RPC). Point any client at it.
Claude Code
claude mcp add engram -- npx -y engram mcpCursor — .cursor/mcp.json
{
"mcpServers": {
"engram": { "command": "npx", "args": ["-y", "engram", "mcp"] }
}
}Claude Desktop — claude_desktop_config.json
{
"mcpServers": {
"engram": { "command": "npx", "args": ["-y", "engram", "mcp"] }
}
}Codex CLI — ~/.codex/config.toml
[mcp_servers.engram]
command = "npx"
args = ["-y", "engram", "mcp"]From a local checkout (no npm install needed):
{
"mcpServers": {
"engram": { "command": "node", "args": ["/absolute/path/to/engram/dist/index.js", "mcp"] }
}
}The server sends instructions on initialize, telling the model to search before answering questions about prior context and to save durable facts as they come up.
Prompt hook (no MCP client support needed)
engram inject reads a prompt from argv or stdin, prints a <memory> block, and stays completely silent when nothing matches — safe to wire into a hook that appends to the prompt.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "engram inject" }
]
}
]
}
}It also unwraps JSON hook payloads, pulling the first of prompt, user_prompt, userPrompt, text, message, query.
CLI
Command | Description |
| Save a memory. Identical text is merged (tags union, max importance). |
| BM25 search, ranked by relevance + importance + recency + scope. |
| Print a |
| Hook helper: reads argv or stdin, silent when empty. |
| List active memories, newest first. |
| Show one memory with timestamps and supersede links. |
| Patch text, kind, tags, scope, importance or |
| Delete (asks first; |
| Totals, active vs superseded, counts per kind. |
| Every scope with a memory count. |
| Validate the store: duplicate ids/text, bad kinds, dangling supersede links. |
| Find near-duplicates; |
| Delete superseded memories; |
| Dump every memory as JSONL. |
| Load JSONL or a JSON array ( |
| Run the MCP server on stdio. |
| Usage / version. |
Flags
Flag | Description |
|
|
| Comma-separated tags. |
| Filter reads by one tag. |
| Scope such as |
| Ignore scope filtering on reads. |
| Include replaced memories in reads. |
| Ranking weight (default |
| Page through results. |
| Mark an older memory as superseded by the new one. |
| Where the memory came from (file, URL, conversation). |
| Store file for this call (overrides |
| Budget for |
|
|
|
|
| Print the score breakdown per search hit. |
| Machine-readable output. |
| Suppress human chatter. |
| Skip confirmation prompts. |
Environment
Variable | Meaning |
| Store path (default |
| Disable ANSI colors. |
How ranking works
Two stages, both deterministic.
1. BM25 over memory text + tags (k1 = 1.2, b = 0.75), pooled by the current scope/kind filters:
idf(t) = ln(1 + (N − n(t) + 0.5) / (n(t) + 0.5))CJK runs are tokenized as unigrams + bigrams; latin text as lowercased words. Query tokens are deduplicated. That is enough for 部署 to hit 部署流程 and 生产环境 to hit 部署到生产, with no segmenter and no model.
2. Multipliers:
score = bm25
× (1 + 0.12 · (importance − 3)) # 0.76 … 1.24
× (1 + 0.35 · e^(−ageDays / 45)) # 1.00 … 1.35
× scopeBoost # 1.08 when the memory lives in the queried scope--explain prints all four factors, so a surprising result is inspectable instead of mysterious.
Data model
A memory is one JSON object per line:
{"id":"mvevxbqm9dce","text":"This repo uses pnpm, never npm","kind":"decision","tags":["tooling"],"scope":"project:engram","agent":"cli","importance":4,"createdAt":1760000000000,"updatedAt":1760000000000}Field | Notes |
|
|
| The memory, as a standalone statement. |
| One of |
| Free-form, indexed by BM25. |
|
|
| 1–5, clamped. |
| Present when a newer memory replaced this one. |
| Epoch milliseconds. |
Lifecycle: facts change, so don't delete history — supersede it.
engram add "Deploys use rsync to prod-01" --replaces mvevxbqm9dce # old stays queryable with --include-superseded
engram prune --days 30 # drop history you no longer needWrites are serialized through <store>.lock (stale locks reaped after 10s) and committed with a unique temp file + atomic rename, retried on Windows EPERM. Several agents can write to the same store concurrently without losing updates.
MCP surface
Tools
Tool | Purpose |
| Save a fact, preference, decision, task or snippet. |
| Ranked search; returns ids, text, tags and scores. |
| Top matches wrapped in a |
| Recent memories, newest first. |
| Full JSON for one id. |
| Patch fields on an existing memory. |
| Permanently remove one memory. |
| Totals, per-kind counts, scopes, store path. |
Resources
URI | Contents |
| Store summary. |
| Every memory in a scope. |
| One memory as JSON. |
Protocol version 2024-11-05; newline-delimited JSON-RPC on stdin/stdout, stdout reserved for the protocol.
Recipes
# Back up, move to another machine, or seed a second agent
engram export > backup.jsonl
ENGRAM_STORE=~/.engram/work.jsonl engram import backup.jsonl
# Share a decision with every project
engram add "Always run tests before committing" --scope global --kind preference
# Audit before a release
engram doctor
engram dedupe --threshold 0.75
engram stats --json | jq '.byKind'
# Feed a long-running agent
engram context "current task: refactor auth" --limit 10 --max-chars 2000Design principles
Zero dependencies. Only Node built-ins. No install-time supply chain.
Local first. No network calls, no accounts, no telemetry.
Explainable retrieval. BM25 + arithmetic you can read; no embedding drift.
Boring storage. JSONL that survives
cat,grep,gitandrsync.Non-goals: being a vector database, syncing between machines for you, or storing secrets. Keep credentials out of memories.
FAQ
Why not SQLite or embeddings? Because the whole store should fit in your head: one file, no binary format, no model download, no re-index. BM25 plus CJK bigrams covers the "find that thing I told you" case, and a 5k-memory store is still fast.
Where are memories stored?
~/.engram/memory.jsonl unless ENGRAM_STORE or --store says otherwise. Project memories default to project:<cwd basename>; global memories are always visible.
Will two agents clobber each other's writes? No. Writers take an advisory lock, reload, mutate, then commit via atomic rename. Readers never see a half-written file.
Windows?
Tested on Windows 11 with Node 20+. Rename retries cover EPERM/EBUSY from AV scanners and indexers. Use NO_COLOR=1 for clean logs.
Can I hand-edit the file?
Yes — it's JSONL. Run engram doctor afterwards; it reports duplicate ids, invalid kinds and dangling supersede links.
Development
npm install
npm run build # tsc → dist/
npm test # build + 39 end-to-end checks (CLI + MCP + concurrency)
npm run cleansrc/store.ts JSONL store, scopes, dedupe-by-hash, locking, atomic writes
src/search.ts BM25, CJK tokenizer, ranking multipliers, context block
src/dedupe.ts Jaccard near-duplicate detection
src/cli.ts subcommands and terminal output
src/mcp.ts MCP stdio server (tools + resources)
src/args.ts zero-dependency argv parser
src/ui.ts colors, padding, relative time
scripts/smoke.js end-to-end test runner
assets/ banner.svg, demo.svg (animated source) and demo.gifContributions are welcome: open an issue with the use case first, keep the zero-dependency rule, and add a smoke check for anything user-visible.
License
MIT © engram contributors
This server cannot be deployed
Maintenance
Related MCP Connectors
- KogniteOAuthdev.kognite
Hosted agent memory: store, search, and recall facts across sessions from any MCP client.
Persistent memory for AI agents across Claude, ChatGPT and any MCP client.
Cross-tool persistent memory and context for AI assistants over MCP.
Persistent memory, knowledge graph, task management and more for AI agents via MCP.
Related MCP Servers
- AlicenseCqualityAmaintenanceProvides persistent, local-first AI memory across sessions via MCP tools for storing, searching, and retrieving context from past interactions.26962 PyPI1MIT
- AlicenseAqualityDmaintenancelocal-first long-term memory for MCP agents (no DB, no API keys)33 npmMIT
- AlicenseAqualityBmaintenanceProvides persistent, searchable memory for AI agents across any MCP-compatible client, storing project context, user preferences, and session learnings locally in SQLite with tools to save, retrieve, search, and manage them.126 npmMIT
- AlicenseAqualityCmaintenanceProvides persistent local memory for any MCP-compatible AI agent, enabling agents to store, search, recall, forget, export, and manage preferences, rules, and facts with zero dependencies, no cloud, and no API keys.8MIT