@sayyamkhan/engram-ai
v0.2.0
Published
Persistent memory layer for Claude AI. Drop-in context and history management for any Claude-powered application.
Maintainers
Readme
Engram
Persistent memory for Claude. Because every conversation shouldn't start from zero.
Engram is a drop-in TypeScript library that gives Claude persistent memory. Drop it into any Claude-powered application and your users will never lose context again — preferences, history, facts, and decisions all persist seamlessly across sessions.
The Problem
Every Claude conversation starts completely blank. All context, user preferences, learned behavior, and history is permanently lost between sessions. Developers building Claude-powered apps have no standard, drop-in solution for persistent memory.
Without Engram:
Session 1: "Hi, I'm Alice. I'm building a FastAPI app on AWS."
Session 2: "Hey, can you help me with my project?"
Claude: "Of course! What are you working on?" ← starts from zero, againWith Engram:
Session 1: "Hi, I'm Alice. I'm building a FastAPI app on AWS."
Session 2: "Hey, can you help me with my project?"
Claude: "Hi Alice! Happy to continue helping with your FastAPI app on AWS." ← remembers everythingFeatures
- Store — Persist every conversation turn to SQLite (or PostgreSQL) with userId, sessionId, role, timestamp, and token count
- Extract — Use Claude to extract structured user facts (name, preferences, goals, skills) after every turn
- Compress — Automatically summarise old turns when history exceeds your token threshold
- Inject — Prepend the right context (facts + summary + recent turns) before every Claude API call
- Search — Keyword search across all stored memories with relevance ranking
- MCP Server — Full Model Context Protocol server for Claude Desktop and MCP clients
- Zero config — Works out of the box with SQLite, no database setup required
- TypeScript-first — Strict types, full type exports, no
any
Installation
npm install engramPeer dependencies (already included):
# These are bundled as dependencies — no extra installs needed
# @anthropic-ai/sdk, better-sqlite3, @modelcontextprotocol/sdk, zodQuick Start
import Anthropic from '@anthropic-ai/sdk';
import { Engram } from 'engram';
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const engram = new Engram({
apiKey: process.env.ANTHROPIC_API_KEY!,
userId: 'user_123',
});
// Store turns after each exchange
await engram.store({ role: 'user', content: "I'm Alice. I love Python." });
await engram.store({ role: 'assistant', content: 'Nice to meet you, Alice!' });
await engram.extract(); // extract facts: name=Alice, language=Python
// In a new session — inject memory into your Claude call
const { messages, systemContext } = await engram.inject("What language do I prefer?");
const reply = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
system: systemContext, // injected facts + summary
messages, // history + new message
});That's it. Claude now remembers Alice is a Python developer across every future session.
How It Works
┌──────────────────────────────────────────────────────────────────┐
│ Engram Pipeline │
│ │
│ User message │
│ │ │
│ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ STORE │──▶│ EXTRACT │──▶│ COMPRESS │──▶│ INJECT │ │
│ │ │ │ │ │ │ │ │ │
│ │ Save to │ │ Claude │ │ Claude │ │ Facts + │ │
│ │ SQLite │ │ extracts│ │summarises│ │ Summary + │ │
│ │ with │ │ name, │ │ old turns│ │ Recent turns │ │
│ │ token │ │ prefs, │ │ when over│ │ → messages[] │ │
│ │ count │ │ goals │ │ threshold│ │ │ │
│ └─────────┘ └─────────┘ └──────────┘ └────────────────┘ │
│ │ │
│ ▼ │
│ Claude API call │
└──────────────────────────────────────────────────────────────────┘- Store — Every turn is saved with timestamp and token count
- Extract — Claude identifies user facts and stores them in a
factstable - Compress — When token count exceeds the threshold, old turns are summarised
- Inject — Before every new call, relevant facts + summary + recent turns are assembled
Full API Reference
new Engram(config)
const engram = new Engram({
apiKey: string, // Anthropic API key (required)
userId: string, // User identifier (required)
storage?: 'sqlite' | 'postgres', // Storage backend (default: 'sqlite')
dbPath?: string, // SQLite file path (default: './engram.db')
postgresUrl?: string, // PostgreSQL connection string
maxTokensBeforeCompress?: number, // Compression threshold (default: 4000)
maxInjectTokens?: number, // Injection budget (default: 2000)
extractFacts?: boolean, // Auto-extract facts (default: true)
model?: string, // Claude model (default: 'claude-sonnet-4-6')
sessionId?: string, // Session ID (auto-generated if omitted)
});engram.store(message)
Store a conversation turn. Call after every user message and assistant response.
const id = await engram.store({ role: 'user', content: 'Hello!' });
// role: 'user' | 'assistant' | 'system'
// Returns: number (stored turn ID)engram.inject(userMessage)
Build a memory-injected messages array for the next Claude API call.
const { messages, systemContext } = await engram.inject("What's my name?");
await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
system: systemContext, // Pass as the system prompt
messages, // Pass as the messages array
});engram.extract()
Use Claude to extract and persist structured user facts from recent turns.
await engram.extract();
// Stores facts like: { name: 'Alice', preferred_language: 'Python', ... }engram.compress()
Compress old turns into a summary using Claude. Returns true if compression occurred.
const compressed = await engram.compress();
// Automatically respects maxTokensBeforeCompress thresholdengram.search(query, topN?)
Search stored memories by keyword. Returns turns ordered by relevance.
const results = await engram.search('Python programming', 5);
// Returns: MessageTurn[]engram.getFacts()
Retrieve all extracted facts for this user.
const facts = await engram.getFacts();
// Returns: MemoryFact[] — [{ key: 'name', value: 'Alice', confidence: 1.0, ... }]engram.clear()
Delete all stored memory for this user. Irreversible.
await engram.clear();engram.stats()
Get aggregate memory statistics.
const stats = await engram.stats();
// Returns: {
// userId, totalTurns, activeTurns, compressedTurns,
// totalFacts, totalSummaries, estimatedTokens
// }engram.close()
Close the storage connection. Call when your app shuts down.
await engram.close();MCP Server
Engram ships with a full MCP server so you can give Claude Desktop persistent memory with zero code.
Add to claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"engram": {
"command": "npx",
"args": ["engram-mcp"],
"env": {
"ANTHROPIC_API_KEY": "your-api-key-here",
"ENGRAM_DB_PATH": "/Users/you/engram.db"
}
}
}
}Restart Claude Desktop — Engram's tools will be available immediately.
Available MCP Tools
| Tool | Description |
|---------------------|------------------------------------------------------|
| engram_store | Store a conversation message turn |
| engram_inject | Get memory-injected messages for the next API call |
| engram_search | Search memories by keyword |
| engram_get_facts | Get all extracted facts for a user |
| engram_compress | Trigger compression of old turns |
| engram_extract | Extract facts from recent conversation |
| engram_clear | Clear all memory for a user |
| engram_stats | Get memory statistics |
See examples/mcp-usage.md for full tool schemas and examples.
Storage Options
SQLite (default — zero config)
const engram = new Engram({
apiKey: '...',
userId: 'user_123',
storage: 'sqlite',
dbPath: './engram.db', // relative to cwd, auto-created
});SQLite is the default. The database file is created automatically on first use. Uses WAL mode for concurrent read performance.
PostgreSQL
const engram = new Engram({
apiKey: '...',
userId: 'user_123',
storage: 'postgres',
postgresUrl: 'postgresql://user:pass@localhost:5432/engram',
});Or use environment variables (PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD) and omit postgresUrl.
Tables are created automatically on first use.
Custom Storage Adapter
Implement StorageAdapter to add your own backend (Redis, DynamoDB, Firestore, etc.):
import { StorageAdapter, Engram } from 'engram';
class MyCustomStorage implements StorageAdapter {
async initialize() { /* ... */ }
async storeTurn(turn) { /* ... */ }
// ... implement all methods
}Environment Variables
| Variable | Default | Description |
|-----------------------|---------------------|------------------------------------------|
| ANTHROPIC_API_KEY | (required) | Your Anthropic API key |
| ENGRAM_DB_PATH | ./engram.db | SQLite database path |
| ENGRAM_MAX_TOKENS | 4000 | Token threshold before compression |
| ENGRAM_MODEL | claude-sonnet-4-6 | Claude model for compress/extract ops |
Roadmap
- [ ] Vector embeddings — Semantic similarity retrieval via pgvector / sqlite-vss
- [ ] Redis adapter — In-memory storage for high-throughput applications
- [ ] Multi-user session management — Group sessions, shared context, org-level memory
- [ ] Web dashboard — Visual memory explorer and fact editor
- [ ] Cloudflare Workers support — Edge-compatible storage via D1 / KV
- [ ] Auto-store hook — Middleware that automatically stores turns after each Claude call
- [ ] Memory versioning — Track how user facts evolve over time
- [ ] Export / import — Portable memory snapshots in JSON
Contributing
Contributions are welcome!
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-feature - Make your changes (TypeScript strict mode, no
any) - Run tests:
npm test - Submit a pull request
Please open an issue first for significant changes to discuss the approach.
Development Setup
git clone https://github.com/your-username/engram
cd engram
npm install
npm run build
npm testLicense
MIT — see LICENSE for details.
Built for the Anthropic Claude for Open Source Program — Ecosystem Impact Track.
