The handoff button for your AI tools.
You're deep in a task with Claude Code. You hit your usage limit. You open Codex, type "resume" — and it already knows your goal, your decisions, what's half-done, and what to do next. No copy-paste. No re-explaining. That's CtxVault.
🔗 Live playground: ctxvault.madebyshubham.in · 🎥 3-min demo: add your video link
Every AI coding tool forgets everything the moment you leave it.
You plan a feature in one tool → hit a limit or want a different model → switch tools → the new one knows nothing. So you paste a wall of transcript and hope. And every tool keeps its own private memory, in its own format, in its own silo — none of it readable, portable, or yours.
CtxVault is a shared memory layer that sits between your AI tools. Any MCP-capable tool can talk to it. It gives them all the same brain:
save_context → summarize this session into a structured handoff + extract facts
resume_context → give me everything I need to continue, within a token budget
search_memory → what did we decide about X? (searches by meaning, not keywords)
list_facts → show me what this project "knows"
list_sessions → what threads of work are saved?
Everything lives on your machine: one SQLite file + a folder of plain markdown. No cloud, no account, no server to run. Delete the folder and the memory is gone — it's yours.
| CtxVault | Cloud memory services | Each tool's built-in memory | |
|---|---|---|---|
| Live handoff between different tools | ✅ the whole point | ❌ | ❌ locked to one tool |
| Where memory lives | your disk | someone's cloud | your disk, but siloed |
| Can you read/edit/git it? | ✅ plain markdown | rarely | partially |
| Setup needed | npm run build |
account + API | none |
Nobody else does live session handoff across tools — and nobody stores agent memory in files you can just open, grep, and commit.
Durable knowledge isn't buried in a database — every fact CtxVault learns is written as a markdown file in OKF (Open Knowledge Format), an open, frontmatter-based knowledge format from Google's research on portable agent memory. One fact, one file:
---
type: decision
title: Use Intl.DateTimeFormat for timezone display
tags: [timezone, intl-api]
---
Chose Intl.DateTimeFormat over date-fns/moment — native, cross-browser,
zero dependencies.What using OKF bought us, concretely:
- Readable without CtxVault. Any human — or any other agent — can
catthe memory. Your project's knowledge outlives the tool that wrote it. - Git-friendly. Facts diff cleanly, get code-reviewed, travel with the repo.
- Editable. Wrong fact? Open the file, fix it. Try that with a vector DB.
- A shelf, not a black box. Decisions (
type: decision), conventions, gotchas — each typed and tagged, solist_factsreads like a project wiki that wrote itself.
Find yours in ~/.ctxvault/knowledge/<project>/*.md.
git clone <this repo> && cd ctxvault
npm install
npm run build # builds the engine + MCP server → apps/mcp-server/dist/index.jsGrab the absolute path — every install below needs it:
echo "$(pwd)/apps/mcp-server/dist/index.js"(Wherever you see <PATH> below, paste that.)
npm run playground # → http://localhost:3111Three panes: Tool A, the Vault, Tool B. Plan something in A, hit Save context, Resume in Tool B, then search for a decision. Works without any API key (degraded demo mode); add a key for real summaries — see docs/PLAYGROUND.md.
claude mcp add ctxvault -s user \
-e CTXVAULT_MODEL=anthropic:claude-haiku-4-5 \
-e CTXVAULT_EMBED_MODEL=openai:text-embedding-3-small \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e OPENAI_API_KEY=sk-... \
-- node <PATH>Then /mcp inside Claude Code should list ctxvault with 5 tools.
Add to ~/.codex/config.toml:
[mcp_servers.ctxvault]
command = "node"
args = ["<PATH>"]
[mcp_servers.ctxvault.env]
CTXVAULT_MODEL = "anthropic:claude-haiku-4-5"
CTXVAULT_EMBED_MODEL = "openai:text-embedding-3-small"
ANTHROPIC_API_KEY = "sk-ant-..."
OPENAI_API_KEY = "sk-..."Add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):
{
"mcpServers": {
"ctxvault": {
"command": "node",
"args": ["<PATH>"],
"env": {
"CTXVAULT_MODEL": "anthropic:claude-haiku-4-5",
"CTXVAULT_EMBED_MODEL": "openai:text-embedding-3-small",
"ANTHROPIC_API_KEY": "sk-ant-...",
"OPENAI_API_KEY": "sk-..."
}
}
}
}Add to .vscode/mcp.json in your workspace (note: VS Code uses servers, not
mcpServers):
{
"servers": {
"ctxvault": {
"type": "stdio",
"command": "node",
"args": ["<PATH>"],
"env": {
"CTXVAULT_MODEL": "anthropic:claude-haiku-4-5",
"CTXVAULT_EMBED_MODEL": "openai:text-embedding-3-small",
"ANTHROPIC_API_KEY": "sk-ant-...",
"OPENAI_API_KEY": "sk-..."
}
}
}
}
⚠️ Keys go in the client'senvblock, not a.envfile. The MCP server is spawned by your editor/CLI and only sees the environment it's handed — a.envin the repo root is ignored. Full details, zero-cost setups, and a troubleshooting table: docs/REGISTER.md.
All tools share one vault at ~/.ctxvault/ — that's exactly what makes the
handoff work.
- In Claude Code: do some work, then say "save this to ctxvault under project myapp"
- In Codex (or Cursor, or VS Code): say "resume project myapp from ctxvault"
Tool #2 continues where tool #1 stopped. They never talked to each other — they just share the vault.
Every model call goes through the Vercel AI SDK, so the model is a config string, not a code change:
CTXVAULT_MODEL=anthropic:claude-haiku-4-5 # fast + cheap, great default
CTXVAULT_MODEL=openai:gpt-5.1
CTXVAULT_MODEL=openrouter:<any-model> # one key, hundreds of models
CTXVAULT_MODEL=compatible:llama3.1 # anything OpenAI-shaped (Ollama, Groq…)Runs free: one OpenRouter free-tier key covers chat and embeddings — or go fully offline with Ollama and no key at all:
# one free key
CTXVAULT_MODEL=openrouter:<free-model> CTXVAULT_EMBED_MODEL=openrouter:openai/text-embedding-3-small
# no key, no network
CTXVAULT_MODEL=compatible:llama3.1 CTXVAULT_EMBED_MODEL=compatible:nomic-embed-text
CTXVAULT_BASE_URL=http://localhost:11434/v1Pick a model that supports structured outputs — the summarizer constrains the model to a schema, and one that can't honour it falls back to raw storage.
No key at all? Nothing breaks. Saves store raw text, search falls back to a local word-match embedder. Never a dead button.
Embeddings are configured separately (CTXVAULT_EMBED_MODEL, default
openai:text-embedding-3-small) because Anthropic has no embeddings API.
One engine, two front doors. The memory brain never knows how it's being called:
flowchart TD
subgraph engine["🧠 memory engine — packages/engine"]
direction LR
S[summarizer] --- X[fact extractor] --- E[embedder] --- R[retriever] --- P[packer]
SA["StorageAdapter (the seam)"]
end
engine -->|MCP over stdio| LOCAL
engine -->|HTTP · Next.js API| HOSTED
subgraph LOCAL["💻 local — daily use"]
L1[Claude Code · Codex · Cursor · VS Code]
L2[(SQLite + OKF markdown on disk)]
end
subgraph HOSTED["☁️ hosted — the playground"]
H1[three-pane demo]
H2[(in-memory, per session)]
end
| Tier | What | Stored as | Answers |
|---|---|---|---|
| working | recent verbatim messages | raw snapshot | "what was just said" |
| episodic | a structured HandoffNote per session | goal, decisions, todos, gotchas, next step | "where was I?" |
| semantic | durable facts | OKF markdown + vectors | "what does this project know?" |
Save = summarize → extract facts → embed everything.
Resume = pack a priority stack into a token budget: HandoffNote → knowledge
index → relevant facts → recent transcript tail. Lowest priority gets truncated
first.
Search = semantic lookup ranked by 0.6·similarity + 0.3·recency + 0.1·project,
with a relevance floor so junk queries honestly return "no match" instead of weak
noise.
- Schema-constrained summarization. The summarizer hands the model a zod
schema via
generateObject— the model is forced into the HandoffNote shape, not politely asked. That's also why smaller/cheaper models work here. - ~60× semantic compression. A HandoffNote is a compressed brief of the whole session; that's what saves tokens on resume. The OKF files are a shelf, not a compressor — durable and auditable.
- Graceful degradation everywhere. Model error during save? You get raw storage and a warning, never a failed save. Durability first.
packages/engine # the brain — transport-agnostic memory logic
├─ ai/ # provider seam (any model via the AI SDK)
├─ llm/ # summarizer + fact extractor (schema-constrained)
├─ embed/ # real embedders + local fallback
├─ okf/ # OKF markdown read/write
├─ storage/ # StorageAdapter: SQLite (local) · in-memory (hosted)
├─ retriever.ts # similarity + recency ranking
└─ engine.ts # save / resume / search
apps/mcp-server # local front door — stdio MCP server (5 tools)
apps/playground # hosted front door — three-pane Next.js demo
docs/ # deep dives — start with CODE-TOUR.md
Deep dives: DATA-FLOW.md · CODE-TOUR.md · SUMMARIZER.md · EMBEDDINGS.md · OKF-FACTS.md · PLAYGROUND.md · REGISTER.md
📸 Screenshot / GIF placeholder — add
docs/assets/handoff.gifafter a live run.
- OKF merge intelligence — today an updated fact overwrites only when the slug matches; contradicting facts can coexist until then (known, on the list)
- Auto-capture hooks (save without asking) · cloud sync & team vaults
- Encryption at rest · re-import hand-edited OKF files as authoritative memory
TypeScript · npm workspaces · MCP SDK · better-sqlite3 · zod · gray-matter · Next.js · Vercel AI SDK.
Built for a hackathon by someone who switched AI CLIs four times while building it — CtxVault carried the context every time.