persisted-memory
v1.1.1
Published
Persistent memory system for Claude Code via MCP — dual-write to Markdown + LanceDB with hybrid semantic search
Maintainers
Readme
Persisted Memory for Claude Code
A persistent memory system that survives Claude Code's context compaction. Memories are stored as Markdown files (source of truth) and indexed in LanceDB (vector search). Uses Ollama for local embeddings and AI-powered summarization with graceful degradation to keyword-only search.
How It Works
Claude Code Session (any project)
│
├─ Hooks capture context automatically
│ SessionStart → injects SUMMARY.md
│ PreCompact → saves context before compaction
│ PostToolUse → logs file changes
│ Stop → marks turn completion
│ SessionEnd → auto-persist to LanceDB + AI summary
│
└─ MCP Server provides 10 tools
memory_store, memory_search, memory_recall,
memory_list, memory_status, memory_forget,
memory_consolidate, memory_graph, memory_viewerMemories are stored per-project at <project>/.claude/memory/. The MCP server and hooks are installed once globally and work with any project in any language.
Features
- Dual-write architecture -- Markdown (human-readable, git-friendly) + LanceDB (fast vector search)
- Hybrid search -- 70% semantic + 30% keyword, with automatic fallback
- Knowledge graph -- automatic entity/relationship extraction across memories
- Progressive disclosure -- token-efficient recall with summary/detailed/ids_only modes
- Privacy tags -- wrap sensitive content in
<private>...</private>to exclude from outputs - AI-powered summaries -- Ollama-generated session summaries (falls back to rule-based)
- Web viewer -- browser-based UI for exploring and searching memories
- Auto-persist -- hook-captured activity is automatically stored into LanceDB (no manual
memory_storeneeded) - Configurable embeddings -- swap model and vector dimension via environment variables
- Zero external APIs -- everything runs locally with Ollama
Prerequisites
- Node.js 20+ (LTS)
- Ollama with
nomic-embed-textmodel (optional -- falls back to keyword search)
# Install Ollama (if not already installed)
brew install ollama
# Pull the embedding model
ollama pull nomic-embed-text
# Optional: pull the summarization model
ollama pull llama3.2Installation
Step 1: Install the package
npm install -g persisted-memoryStep 2: Register the MCP server with Claude Code
claude mcp add --scope user memory -- persisted-memoryStep 3: Register hooks
Create or edit ~/.claude/settings.json to add the lifecycle hooks. Run the setup script that comes with the package:
# Find where the package is installed
INSTALL_DIR=$(npm root -g)/persisted-memory
# Run the setup script
bash "$INSTALL_DIR/scripts/install.sh"The install script will:
- Register the MCP server at user-level (
claude mcp add) - Register hooks in
~/.claude/settings.json(SessionStart, PreCompact, PostToolUse, Stop, SessionEnd) - Add memory instructions to
~/.claude/CLAUDE.md
Step 4: Restart Claude Code
The MCP server and hooks activate on restart.
Alternative: Install from source
git clone <repo-url> ~/Works/persisted_memory
cd ~/Works/persisted_memory
npm install && npm run build
bash scripts/install.shSetup for an Existing Project
No per-project setup is needed. Once installed globally, persisted-memory works automatically in every project you open with Claude Code.
On first use in a project, it creates the memory directory at:
<your-project>/.claude/memory/The only recommended step is to add the LanceDB index to your .gitignore:
echo ".claude/memory/.lance/" >> .gitignoreThe .md and .json files in .claude/memory/ are human-readable and optionally committable -- they provide shareable context for team members.
Verification
After restarting Claude Code:
# Check MCP server is connected
/mcp
# Check system status (inside any Claude Code session)
Ask: "Check memory status"
→ Claude calls memory_status → shows total entries, Ollama status, memory dirUsage
Automatic (via hooks)
Hooks fire automatically during every Claude Code session:
- Session start: Previous session's SUMMARY.md is injected into context
- File changes: Edit/Write operations are logged to daily Markdown
- Context compaction: Transcript is saved before compaction occurs
- Session end: Daily log entries are auto-persisted to LanceDB, then AI-powered summary is generated (or rule-based fallback)
Manual (via MCP tools)
Ask Claude to use these tools naturally:
| What you say | Tool called |
|---|---|
| "Remember that we use Yarn, not npm" | memory_store |
| "What did we decide about authentication?" | memory_search |
| "Recall everything about the database schema" | memory_recall |
| "Show me all stored memories" | memory_list |
| "Check memory system status" | memory_status |
| "Forget memory abc-123" | memory_forget |
| "Clean up duplicate memories" | memory_consolidate |
| "What concepts are related to Docker?" | memory_graph |
| "Open the memory viewer" | memory_viewer |
Privacy Tags
Wrap sensitive content in <private> tags to keep it out of summaries, public views, and the web viewer:
memory_store: "API key is <private>sk-abc123</private>, use it for auth"The private content is stored in LanceDB for search but stripped from all outputs. Tags are case-insensitive.
Progressive Disclosure
The memory_recall tool supports three detail levels to optimize token usage:
| Level | Description |
|---|---|
| summary (default) | Truncated text (200 chars), most token-efficient |
| detailed | Full text of each memory |
| ids_only | Just IDs, types, and scores -- minimal tokens |
Knowledge Graph
Every stored memory automatically extracts entities (files, technologies, concepts) and creates relationships between them. Explore the graph:
"What entities are related to TypeScript?" → memory_graph entity: "typescript"
"Show me all known entities" → memory_graph list_entities: true
"How big is the knowledge graph?" → memory_graph (default)Web Viewer
Launch a browser-based memory explorer:
"Open the memory viewer" → memory_viewerOr from the command line:
npm run viewer
# Opens at http://localhost:3777Features: dark theme, search with debounce, type/importance filter chips, expandable cards, pagination.
Memory Types
When storing memories, specify a type:
| Type | Use for |
|---|---|
| decision | Architecture/design decisions |
| architecture | System design notes |
| code_change | File modifications |
| error_fix | Problem + solution pairs |
| pattern | Reusable code patterns |
| context | General session context |
| preference | User/project preferences |
Importance Levels
| Level | Use for |
|---|---|
| critical | Must never be forgotten |
| high | Important decisions and patterns |
| medium | Useful context (default) |
| low | Minor notes |
Auto-Persist
Hook-captured activity (file modifications, pre-compact saves, session context) is automatically stored into LanceDB at session end -- no manual memory_store calls needed.
How it works:
- Hooks write activity to daily log files (
daily/YYYY-MM-DD.md) throughout the session - On session end, the auto-persist system parses the daily log and classifies each section
- Already-stored memories (
memory_storeentries) are skipped to avoid duplicates - File modifications are aggregated into a single
code_changeentry - New entries are embedded and stored in LanceDB + knowledge graph
- A character offset is tracked to avoid re-processing on subsequent runs
All auto-persisted entries are tagged with "auto-persisted" for traceability.
| Daily Log Pattern | Classification | Action |
|---|---|---|
| **[type]** (importance) | memory_store | Skip (already in LanceDB) |
| - HH:MM Modified: path | file_modification | Aggregate into one code_change entry |
| _Turn completed at_ | turn_marker | Skip (noise) |
| ## Session ended at | session_marker | Skip (noise) |
| ## Pre-compact save | pre_compact | Store as context / medium |
| Other (>50 chars) | content | Store as context / low |
Configuration
Embedding model and vector dimensions are configurable via environment variables:
| Variable | Default | Description |
|---|---|---|
| MEMORY_EMBED_MODEL | nomic-embed-text | Ollama embedding model name |
| MEMORY_VECTOR_DIM | 768 | Vector dimension (must match model output) |
| OLLAMA_URL | http://localhost:11434 | Ollama server URL (localhost only) |
Storage math (768 dims): ~3KB per vector. 1,000 memories = ~3MB, 10,000 = ~30MB.
Example with a smaller model:
export MEMORY_EMBED_MODEL=all-minilm
export MEMORY_VECTOR_DIM=384Important: MEMORY_VECTOR_DIM must match the actual output dimension of your chosen model. A mismatch will cause vectors to fall back to zero-vectors, disabling semantic search.
Per-Project Data
Each project gets its own memory directory:
<your-project>/.claude/memory/
├── .lance/ # LanceDB vector index (add to .gitignore)
├── .auto-persist-offset # Tracks last-persisted position per daily log
├── SUMMARY.md # Auto-generated context summary
├── knowledge-graph.json # Entity/relationship graph
├── daily/ # Daily session logs
│ └── 2026-02-18.md
├── sessions/ # Archived session summaries
│ └── 2026-02-18-10-30.md
└── decisions.md # Append-only decisions logSearch
Search uses a hybrid approach:
- 70% semantic (vector cosine similarity via Ollama embeddings)
- 30% keyword (LanceDB full-text search)
If Ollama is unavailable, search falls back to 100% keyword automatically.
Disaster Recovery
Markdown files are the source of truth. If LanceDB data corrupts:
- Delete
.claude/memory/.lance/ - The index will be rebuilt from Markdown on next use
Uninstallation
# If installed from source
bash ~/Works/persisted_memory/scripts/uninstall.sh
# If installed globally via npm
INSTALL_DIR=$(npm root -g)/persisted-memory
bash "$INSTALL_DIR/scripts/uninstall.sh"
npm uninstall -g persisted-memoryThis removes the MCP server and hooks. Per-project memory data at <project>/.claude/memory/ is preserved -- delete manually if no longer needed.
Project Structure
persisted-memory/
├── src/
│ ├── index.ts # Entry point (stdio MCP transport)
│ ├── server.ts # MCP server with 10 tool definitions
│ ├── storage/
│ │ ├── types.ts # MemoryEntry, MemoryType, Importance
│ │ ├── lance-store.ts # LanceDB CRUD + vector/FTS search
│ │ ├── markdown-store.ts # Markdown file read/write/append
│ │ └── knowledge-graph.ts # Entity/relationship extraction + graph
│ ├── embeddings/
│ │ └── ollama.ts # Ollama nomic-embed-text client
│ ├── search/
│ │ └── hybrid.ts # Merge vector + keyword results
│ ├── utils/
│ │ ├── chunking.ts # Split text into embeddable chunks
│ │ ├── project.ts # Resolve CLAUDE_PROJECT_DIR
│ │ ├── privacy.ts # Private tag stripping
│ │ ├── summarize.ts # AI-powered summary generation
│ │ ├── daily-log-parser.ts # Parse daily log into classified sections
│ │ └── auto-persist.ts # Auto-persist orchestrator
│ ├── cli/
│ │ ├── viewer.ts # Web viewer CLI entry point
│ │ ├── generate-summary.ts # Summary generation CLI
│ │ └── auto-persist.ts # Auto-persist CLI entry point
│ └── viewer/
│ ├── server.ts # HTTP server for web viewer
│ └── index.html # Self-contained web UI
├── hooks/ # Shell scripts for Claude Code hooks
├── scripts/
│ ├── install.sh # Build + register MCP + hooks
│ └── uninstall.sh # Clean removal
├── package.json
└── tsconfig.jsonDependencies
Only 4 runtime packages:
| Package | Purpose |
|---|---|
| @modelcontextprotocol/sdk | MCP server protocol |
| @lancedb/lancedb | Embedded vector database |
| apache-arrow | Columnar data format (LanceDB dep) |
| zod | Schema validation for tool parameters |
No Python. No Docker. No background daemons. No external APIs.
License
MIT
