GhostMem
A local-first MCP memory server for Claude Code, opencode, Cursor, and any MCP client. One memory across clients, in one SQLite file you own.
GhostMem remembers project knowledge between agent sessions. It stores memories, tasks, and decisions locally, searches them with SQLite FTS5 and optional local embeddings, and works with the MCP clients you already use.
- Local by default: one SQLite database; no account, cloud service, or required vector database.
- Cross-client: Claude Code, opencode, Codex, Goose, Cursor, and other MCP clients can share the same memory.
- Graceful degradation: Ollama is optional. Without it, GhostMem still provides full-text search.
- Transparent lifecycle: consolidation, resolution, and supersession are dry-run by default and can be undone or disabled.
Contents
- Why GhostMem?
- Quick start
- Choose your client
- What GhostMem remembers
- How it works
- Privacy, control, and cost
- Optional integrations
- Commands
- Configuration
- Benchmarks
- Project status
- Contributing
Why GhostMem?
Most agent memory is trapped inside one product. GhostMem provides one portable memory layer:
Native memory features are useful inside their own products. GhostMem is for the gap between them: a project convention learned in one client can inform the next client you use.
Quick start
Requirements
- Go 1.26+ for a source installation.
- A supported MCP client for integration setup.
- Optional: Ollama with
nomic-embed-text:v1.5for vector embeddings. GhostMem works without it using FTS5 only.
Install and initialize
go install github.com/wcatz/ghost/cmd/ghost@latest
ghost mcp init
ghost mcp init detects supported clients on PATH, configures the integrations it finds, and creates the local GhostMem store. It is idempotent and non-destructive. Preview its changes first with:
ghost mcp init --dry-run
Verify the installation:
ghost version
ghost mcp status --client claude # use opencode, codex, or goose for those integrations
After initialization, start a session in your project. GhostMem injects the project context automatically and exposes its tools to the client.
Prefer a prebuilt binary or a client-specific setup? See the installation guide.
Choose your client
The full setup matrix, Windows instructions, Docker usage, and uninstall steps live in docs/installation.md.
What GhostMem remembers
Memories
Memories are concise, durable notes with one of eight categories:
architecture— system design and component relationshipsdecision— a choice, rationale, or rejected alternativepattern— a recurring approachconvention— a repository or workflow rulegotcha— a bug, pitfall, or surprising behaviordependency— a version, API quirk, or external constraintpreference— a user preferencefact— general project knowledge
The agent saves memories through MCP tools. Near-duplicates are detected within the same project: same-category saves fold at the standard similarity bar, and cross-category re-saves of the same rule fold too when their token overlap is near-identical (Jaccard >= 0.7) — and those cross-category folds never target dead records (resolved/superseded). GhostMem preserves the new text as a linked row, strengthens the existing row, and records the relationship without overwriting the original — the existing memory keeps its category. Memories can also be pinned, updated, promoted, searched, or deleted. A save can pin in the same call — ghost_memory_save takes an optional pin — because nothing an agent writes is excluded from consolidation by its source, so this is the way to keep a non-negotiable rule out of a ghost reflect rewrite.
Projects and global knowledge
GhostMem resolves a project by longest canonical path prefix, then by repository remote, then by basename. A basename match is accepted only when exactly one candidate survives the recorded-path and remote checks; an ambiguous name is rejected rather than guessed. A project with no recorded location is reached by name only. Project knowledge stays scoped to that project; the special _global project holds preferences and facts that apply everywhere. Use cross-project search when the relevant context may live under another repository.
Tasks and decisions
Alongside memories, GhostMem stores:
- Tasks with
pending,active,done, andblockedstates. - Decision records with the chosen direction, rationale, alternatives, and status.
- An append-only history per memory: every save, edit, consolidation rewrite, duplicate fold, resolve, supersession, restore, import and deletion, with the content and importance the memory held at the time.
ghost history <memory-id>prints it, and it outlives the memory — a deleted memory's last state is still readable, orghost history purge <id>erases both when the point is to remove a secret rather than retire a memory.
See the usage guide for the complete mental model.
How it works
Save → Embed → Link → Search → Consolidate → Decay
- Save: MCP tools store a memory, task, or decision in SQLite.
- Embed: an optional local Ollama worker creates vectors asynchronously.
- Link: an optional background worker links semantically related memories.
- Search: FTS5 and vectors are fused with Reciprocal Rank Fusion when embeddings are available. Resolved memories and
_globalrows are scored at half weight — under RRF k=60 that ranks them below the live candidates the search fetched — but they stay searchable. - Consolidate:
ghost reflectcan merge duplicates and prune noise, with snapshots and dry-run protection. - Decay: category-aware scoring keeps stable conventions and preferences from fading while fresh operational facts can outrank stale ones.
Core memory reads, writes, and search do not need an LLM. Reflection, resolution, and supersession use the calling session's CLI harness (claude, opencode, codex, or goose) when invoked; GhostMem does not silently switch to a different harness.
For implementation details, see docs/architecture.md.
Privacy, control, and cost
Where the data lives
The database is normally:
$XDG_DATA_HOME/ghost/ghost.db
# or, when XDG_DATA_HOME is unset:
~/.local/share/ghost/ghost.db
It is a plain SQLite file. You can inspect, back up, move, or delete it without a GhostMem-specific export format — and for the supported ways to do each of those:
ghost backup # consistent snapshot of the live database
ghost backup --out ~/backups/ghost.db.snap # safe while a session is running
ghost backup verify ~/backups/ghost.db.snap # check a copy before you restore it
ghost export --out ghost-export.jsonl # readable, diffable JSONL
ghost import ghost-export.jsonl # dry run; --apply to write
ghost import is dry-run by default over a read-only connection — the preview cannot migrate or seed the store — and never overwrites a record whose id already exists. Imported memories are stamped source = "onboarding" and unpinned whatever the artifact claims, so a file from somewhere else cannot plant rows that read as your own words or as Ghost's shipped rules; add --trust-provenance when the artifact is your own export.
ghost backup uses SQLite's online VACUUM INTO, so it takes a consistent snapshot of a WAL database that a live MCP server is writing to — copying ghost.db by hand can capture a torn state or lose whatever its -wal file held. It refuses to replace an existing file, and prints the row count of each table it copied so a restore can be checked.
Every backup also writes a sidecar manifest beside the snapshot — <snapshot>.manifest.json — recording the schema version, the row count of each table, the size, and a SHA-256 of the snapshot. ghost backup verify <file> checks a copy against it before you rely on it: the hash, SQLite's own integrity check, the schema version against this build, and the recorded row counts. It does not open the database in your data directory, so it can neither migrate nor seed your live store, and it exits non-zero on a file it cannot vouch for. A copy with no manifest beside it — a pre-migration backup, for instance — is reported as checked rather than verified, with the two manifest-derived checks marked skipped, so an unrun check is never read as a passed one.
ghost export writes one JSON object per line — projects, memories, tasks and decisions, every column of each, with a schema-version header — and two exports of an unchanged database are byte-identical, so the file can be diffed. ghost import is dry-run by default, never overwrites a record whose id already exists, and holds imported content to the same length cap and validation as a normal save. See the CLI reference for the format and the restore procedure.
What can leave the machine
In normal operation GhostMem does not make network calls. The exceptions are explicit:
- Local Ollama for optional embeddings.
- The calling AI CLI harness when you run or enable reflection, resolution, or supersession.
- The GitHub API when you run
ghost upgrade.
GhostMem does not require a separate Anthropic API key. The selected CLI harness handles its own authentication and billing.
Turning things off
- Remove the
ghostMCP entry to stop the server for a client. - Set
embedding.enabled: falseto use FTS5 only. - Keep
reflection.auto_reflect,reflection.auto_resolve, andreflection.auto_supersededisabled to avoid automatic lifecycle work. If you do enable the last one,reflection.supersede_consensus(default 3) makes the automatic phase write only the edges its classifier agrees on across that many passes. - Delete
$XDG_DATA_HOME/ghost(or~/.local/share/ghost) to remove the local store.
The configuration reference is docs/configuration.md.
Optional integrations
Obsidian mirror
Export memories, decisions, and tasks as a one-way Markdown vault:
ghost obsidian export --out ~/Documents/GhostVault
ghost obsidian sync --interval 30s
The mirror is read-only from GhostMem's perspective. Edits in the vault are not synced back, and a running sync can overwrite hand edits on the next database change. See the usage guide.
Agent workflows
GhostMem works well with structured agent workflows such as Superpowers: recall context before planning, search memory before changing unfamiliar code, record decisions when alternatives matter, and save durable findings when a phase completes.
Commands
The common commands are:
ghost mcp # Run the MCP server over stdio
ghost mcp init # Configure detected MCP clients
ghost mcp status --client <name> # Check one client integration
ghost reflect <project> # Preview memory consolidation
ghost resolve <project> # Preview resolved-evidence marking
ghost supersede <project> # Preview supersession links
ghost obsidian export|sync # Mirror the store to Obsidian
ghost bench # Run the built-in retrieval benchmark
ghost upgrade # Update a standalone binary
See docs/cli.md for flags, dry-run behavior, lifecycle details, and project operations, and docs/mcp.md for the complete MCP tool/resource/prompt surface.
Configuration
GhostMem works with zero configuration. A minimal optional setup is:
embedding:
enabled: true
ollama_url: "http://localhost:11434"
model: "nomic-embed-text:v1.5"
linking:
enabled: true
threshold: 0.70
Configuration is loaded from compiled defaults, system YAML, user YAML, and GHOST_* environment variables, with supported command-line flags applied last. See docs/configuration.md for paths, precedence, lifecycle settings, and all common keys.
Benchmarks
GhostMem publishes reproducible retrieval and end-to-end results with the harnesses that produced them. The headline results are:
- LongMemEval-S retrieval: hybrid Recall@5 96.3% and Recall@10 98.2% on the 470 answerable questions (complete CI run
38125202586, task-prefixed vector space). - End-to-end LongMemEval-S: 96.2% blended accuracy across 500 questions with the documented DeepSeek v4 Pro generator and judge.
ghost bench: hybrid NDCG@10 0.832 on 220 graded queries and 549 memories, ahead of the vector leg by 0.0178 (paired 95% CI [+0.0033, +0.0327]) and of the keyword leg by 0.073 — both intervals printed byghost benchitself.- What that table cannot see: the no-answer false-positive rate is 1.000 at the shipped similarity floor in every condition (0.875 for the shipped hybrid path at a 0.50 cosine, against the keyword leg's 0.625), and on a separate graded corpus carrying ages and
supersedesedges the shipped ranking paths read 0.214 R@1 against 0.571 with both off. Both are indocs/benchmarks.mdrather than in this list, because they are the same measurement's limits and not separate achievements.
Different generators and judges make cross-system scores directional rather than strictly comparable. Full tables, methodology, caveats, and reproduction commands are in docs/benchmarks.md.
Project status
GhostMem is a solo project used for real infrastructure work. The project intentionally favors a small, readable system:
- Pure Go with
CGO_ENABLED=0and eight direct Go dependencies. - SQLite + FTS5 persistence with optional local Ollama embeddings.
- Race-enabled tests,
go vet,golangci-lint, vulnerability scanning, and actionlint in CI. - Release binaries for Linux, macOS, and Windows on amd64 and arm64, plus a multi-architecture Docker image.
Contributing
Issues and pull requests are welcome. Run go test ./... and go vet ./... before submitting changes, and keep work on a feature branch.
License
Apache License 2.0 — see LICENSE.