ContextD
Provides OpenAI Codex agents the same persistent project memory and context capabilities, including export to AGENTS.md.
Click on "Install 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., "@ContextDrecall where I left off with the distributed scheduler"
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.
ContextD
Developer context & semantic memory manager for AI coding agents.
You explain the same things to Claude Code, Codex and Cursor every session: what
this project is, why the queue is NATS and not Redis, that you format with
rustfmt before committing, and where you left off last night. ContextD stores
that once — across projects and across agents — and hands back only the parts
that matter for the task at hand, through a CLI and an MCP server.
Claude Code ─┐
Codex ───────┤
Cursor ──────┼── MCP ── ContextD ── SQLite + FTS5 + embeddings
other agents ┘Two rules the design follows
Store everything, inject only what matters. A year of memory does not fit in a context window. Retrieval is hybrid (full-text + vector), ranked, and packed into an explicit token budget; what did not fit is counted, never silently dropped.
Current truth must be distinguishable from historical truth. When the task queue moves Redis → PostgreSQL → NATS, an agent must be told NATS, not the option that happens to be mentioned most often. Superseded memories keep their content and stay searchable, but they are marked, penalised in ranking, and excluded from retrieval unless asked for.
Related MCP server: ContextAtlas
Install
uv tool install contextd # puts `contextd` on your PATH
contextd --versionuv installs the published wheel, which carries the compiled binary — no Rust
toolchain and no Python at runtime. If contextd is not found afterwards, run
uv tool update-shell (uv installs into ~/.local/bin) and open a new shell.
To try it without installing: uvx contextd status.
From a checkout, or to run an unreleased change:
uv tool install . # builds with your Rust toolchain
cargo install --path . # the same thing, straight from cargoSQLite is compiled in — no system libraries, no Docker, no services to run. Linux, macOS and Windows. Building from source needs Rust 1.85+.
Optional environment variables:
Variable | Effect |
| Where memory lives (default |
| Disable colour, as does |
| Log level for the CLI and MCP server; logs go to stderr, never stdout |
Quick start
contextd init # create ~/.contextd
cd ~/projects/orbit
contextd attach # detects git, name, agent files
contextd add --category architecture \
"GPU scheduler uses NATS for task transport"
contextd checkpoint "worker heartbeat completed" \
--goal "Implement distributed GPU scheduling" \
--done Coordinator --next "Lease-based GPU allocation" \
--problem "Worker reconnect"
contextd search "scheduler" # keyword search, ranked
contextd recall "which message transport does the scheduler use?"
contextd export claude # writes CLAUDE.md
contextd export codex # writes AGENTS.md
contextd status
contextd mcp serve # speak MCP on stdiocontextd status:
ContextD
─────────────────────────────────
Project Orbit
Branch main @ a1b2c3d (2 dirty)
Memories 124
Decisions 18
Checkpoints 7
Last checkpoint
worker heartbeat completed (2 hours ago)
Current goal
Implement distributed GPU scheduling
Next
- Lease-based GPU allocation
Semantic index ✓ 149/149 local · hashing-v1
Agents claude, codex
MCP ✓ contextd mcp serveCommands
Command | What it does |
| Create the home directory, database and config |
| Track a repository as a project |
| Counts, git state, latest checkpoint, index health |
| Memory CRUD |
| Record that one memory replaced another |
| Keyword-first search across memories, ADRs and checkpoints |
| Ask a question; hybrid semantic + keyword retrieval |
| Save and restore "where was I?" |
| Architecture decision records |
| Working sessions and what they produced |
| Merge duplicates, mark history, rebuild indexes |
| Write the Markdown mirror and bound agent files |
| Move context in and out of agent files |
| Machines to exchange memory with |
| Survey a machine: what it holds, without copying it |
| The same survey of this machine |
| Sync memory over SSH, record by record |
| The same exchange as a JSON file |
| Run the MCP server; list its tools |
| Show paths and settings; |
Every command takes --json for scripting, --project <name> to act on another
project, and --home <dir> (or $CONTEXTD_HOME) to point at a different store.
MCP
contextd mcp serve # newline-delimited JSON-RPC on stdio
contextd mcp serve --read-onlyRegister it with any MCP client — for Claude Code:
claude mcp add contextd -- contextd mcp serveTools exposed:
Tool | Use |
| Start-of-session context, budgeted to a token limit |
| Answer a question from memory (hybrid retrieval) |
| Keyword-first search |
| One memory in full |
| Counts, branch, index state |
| Current goal, done, next, open problems |
| Decisions that currently hold |
| Which agent worked when, and what came of it |
| Writes (omitted in |
Results carry lifecycle status, and anything superseded is labelled
NOT current so a model does not mistake history for the present design.
Several machines
Working on a laptop and a workstation used to mean two disjoint memories. ContextD exchanges records, not files:
contextd remote scan dev@lab-box # what does that account hold?
contextd remote add lab dev@lab-box # a Host alias from ~/.ssh/config works too
contextd remote pull lab # bring their memory here
contextd remote push lab # send yours there
contextd remote pull lab --dry-run # see what would change firstremote scan surveys an account before you commit to anything. It reports
counts, not content, so finding out what is on a machine costs a few kilobytes
rather than its whole memory, and it works on a destination that is not a
configured remote yet:
$ contextd remote scan lab
lab-box contextd 0.1.0
─────────────────────────────────
Home /home/dev/.contextd
Memories 124 (118 current, 6 superseded)
Decisions 18
Checkpoints 7
Last activity 2 hours ago
Embeddings openai · bge-m3 · vectors in qdrant
project mem adr ckpt last activity last checkpoint
Orbit 80 12 5 2 hours ago worker heartbeat completed
Sable 38 6 2 3 weeks ago parser rewrite landed
plus 6 global memories, applying to every project: 4 convention, 2 user
Nothing was copied. `contextd remote pull lab` merges it here.--detail adds a category breakdown per project. contextd inventory runs the
same survey locally. The account is whichever one you SSH as, and the home is
resolved on that machine ($CONTEXTD_HOME, else ~/.contextd) — pass
--remote-home if it lives somewhere else.
Machines that want a password
Run it from a terminal and ssh asks, as it would on its own:
$ contextd remote scan dev@lab-box
dev@lab-box's password:Password prompts, host-key confirmations and 2FA all work because ssh reads
them from the terminal directly. Every command decides for itself: with a
terminal present it lets ssh prompt, and without one — cron, a pipeline, the
MCP server — it passes BatchMode=yes so a missing key fails immediately
instead of hanging on a prompt nobody will answer. Force either way with
--interactive or --batch.
When the remote has contextd but ssh cannot find it
ssh host command runs a non-interactive, non-login shell, and a stock
~/.bashrc returns immediately for those — before the lines that put
~/.local/bin or ~/.cargo/bin on PATH. So contextd can be installed and
working over there and still be "not found". Which case you are in:
ssh you@host 'command -v contextd' # nothing? not installed
ssh you@host 'bash -lc "command -v contextd"' # found? a PATH problemEither fix works:
contextd remote add lab you@host --login-shell # read ~/.profile first
contextd remote add lab you@host --command '~/.local/bin/contextd'Note the quotes. Without them your own shell expands ~ before ContextD sees
it, and the remote is configured with a path from this machine — which is
worth knowing when the two accounts have different home directories. ContextD
says so if you forget.
A quoted ~/ or $HOME/ path is expanded on the remote rather than here, and
a login shell that prints a banner does not break anything — the JSON payload
is picked out of the output.
Asking once instead of every time
Each command opens its own connection, so scan then pull asks twice. Two
ways to stop that:
ssh-copy-id dev@lab-box # key-based auth, asked once, ever
# or reuse one authenticated connection for a few minutes
contextd remote add lab dev@lab-box \
--ssh-option=-o --ssh-option=ControlMaster=auto \
--ssh-option=-o --ssh-option=ControlPath=~/.ssh/cm-%r@%h:%p \
--ssh-option=-o --ssh-option=ControlPersist=5mpull runs contextd bundle export on the far side over SSH and merges what
comes back. Merging is by UUID, so:
running it twice changes nothing the second time;
where a record exists on both sides, the newer
updated_atwins;where both sides changed, the local copy is kept and the divergence is listed rather than silently resolved;
supersede links travel, so history closed on one machine stays closed on the other;
deletions travel too, and keep travelling: a memory deleted on the laptop is removed on the desktop, and reaches a third machine through either of them.
Deleting across machines
contextd delete records a tombstone — a note that the record was deleted, and
when — and that note syncs like any other record. Without it, the next sync
from a machine that still had the memory would helpfully hand it back.
Deletion is treated as a decision with a timestamp, so the most recent decision about a record stands:
Situation | Result |
Deleted on A, untouched on B | Removed on B, and on every machine after that |
Deleted on A, edited on B afterwards | The edit wins; the record comes back and the tombstone is cleared |
Deleted on A and on B | Removed everywhere, once |
Deleting a whole project (contextd detach --purge) is a local cleanup and is
deliberately not synchronised: one machine tidying up should not tell the
others to forget a project.
Tombstones are kept for sync.tombstone_retention_days (a year by default) and
then forgotten by contextd refresh. A machine that has not synced for longer
than that can still resurrect a record it never heard was deleted — lower the
retention only if every machine syncs often.
Prefer contextd delete --archive when you might want the record back: it is
reversible, it also syncs, and archived memories stay out of retrieval while
remaining in contextd memories --all.
Copying contextd.db around was rejected deliberately: two machines that both
recorded something since the last exchange must both keep their work, and a
file copy can only pick a winner.
Projects are matched across machines by git remote (SSH and HTTPS URL forms are
treated as the same repository), then by slug. A project that arrives from
elsewhere has no local path; running contextd attach in your checkout adopts
it instead of creating a second project for the same code.
No SSH? The same exchange works through a file:
contextd bundle export --out memory.json # on one machine
contextd bundle import --file memory.json # on the otherEmbeddings are not shipped — they are derived, the other machine may use a different provider, and a pull re-embeds locally faster than the transfer would take.
Sessions
A session is one stretch of work on a project by one agent. contextd mcp serve
opens one automatically when a client connects — the agent's name comes from the
MCP handshake — and closes it when the connection goes. From a terminal:
contextd session start --agent claude
contextd session end "heartbeat wired up"
contextd session list
contextd session show # what the current or last session producedCheckpoints made while a session is open are linked to it; memories and decisions are attributed by time window. That turns "what happened last time?" into a real answer:
$ contextd session show
Session b506bd93
─────────────────────────────────
agent claude
window 2026-08-24T14:42:21Z → 2026-08-24T15:10:03Z
ran 27m 42s
summary heartbeat wired up
Checkpoints
6e702570 worker heartbeat completed
Memories
069a5f19 [architecture] GPU scheduler uses NATS for task transportOnly one session is open per project: starting another closes the one before
it, so an agent that crashed cannot collect the next agent's work. Sessions
record activity on this machine, so they stay local — contextd bundle
carries the knowledge, not the attendance.
How retrieval works
query → project detection → FTS5 → semantic → ranking → token budget → contextThe score of a candidate is a weighted sum, multiplied by a lifecycle factor:
(fts + semantic + priority + recency + project_match) × status_multiplierEvery weight lives in config.toml, and the scorer is a trait
(search::scoring::Scorer) so the formula can be replaced without touching
retrieval. contextd search --explain prints the breakdown per hit.
Embeddings
The default provider is local: an offline feature-hashing embedder — no model download, no network, no API key. It captures lexical overlap and phrasing, which is enough for hybrid retrieval to beat keywords alone, but it cannot relate words that never co-occur.
For real paraphrase matching, point ContextD at any OpenAI-compatible endpoint (Ollama, TEI, vLLM, LM Studio, OpenAI itself). bge-m3 is a good default: multilingual, so a question in Chinese finds a memory written in English.
ollama pull bge-m3
contextd config set embeddings.provider openai
contextd config set embeddings.model bge-m3
contextd config set embeddings.api_base http://localhost:11434/v1
contextd config set embeddings.dimensions 1024
contextd config --check # asks the endpoint for a real vector
contextd refresh --force-embeddings # re-embed with the new modelThe API key, when one is needed, is read from the environment variable named in
embeddings.api_key_env — never written to the config file or the database.
provider = "none" disables vectors entirely and ContextD falls back to
full-text search.
Vector store
Vectors are searched through a VectorIndex trait with two backends:
Backend | When |
| Brute-force cosine over the vectors already in the database. Nothing to install, sub-millisecond at personal scale. |
| You already run Qdrant, or your memory has outgrown a scan. |
contextd config set vector.backend qdrant
contextd config set vector.url http://localhost:6333
contextd config set vector.collection contextd
contextd refresh --reindex-vectors # publish existing vectors, no re-embedding
contextd config --checkThe collection is created on first use, sized from the embedding model and using cosine distance; an existing collection of the wrong width (switching a 384-dimension model for bge-m3's 1024, say) is reported with the command that fixes it rather than producing meaningless neighbours.
SQLite keeps the authoritative copy of every vector whichever backend is
selected, so an external index can always be rebuilt, contextd bundle keeps
working, and a machine without Qdrant can still read the same memory.
If the vector store or the embedding endpoint is unreachable, retrieval falls
back to full-text search and says so — contextd status shows the backend and
whether it answers.
Storage layout
SQLite is the source of truth. The Markdown mirror exists so you can read, diff and commit your memory:
~/.contextd/
├── config.toml
├── contextd.db
├── projects/Orbit/
│ ├── overview.md architecture.md decisions.md tasks.md
│ └── checkpoints/
└── global/
├── coding.md git.md preferences.mdYour files are yours
Generated content lives inside a marked block:
# House rules ← yours, never touched
Never force-push to main.
<!-- contextd:begin -->
...generated context... ← ContextD's
<!-- contextd:end -->ContextD records a hash of what it wrote. If the block changed since then,
contextd export refuses and exits non-zero until you pass --force. The same
applies to the Markdown mirror, where contextd sync --adopt turns your hand
edits into memories instead of discarding them.
Architecture
cli / mcp entry points (thin)
↓
agents per-agent import/export adapters
↓
core projects, memories, checkpoints, context building
↓
search / embeddings retrieval, pluggable providers
↓
storage repository traits + SQLite implementationEach layer depends only on the ones below. Nothing above storage mentions
SQLite, nothing above embeddings names a provider, and the MCP server is a
client of core exactly as the CLI is — so the planned evolution (SQLite → FTS
→ embeddings → semantic memory → MCP) does not turn into one tangled module.
src/
├── cli/ argument parsing, rendering, one module per command group
├── core/ model, project, memory, checkpoint, decision, session, context, refresh
├── storage/ repository traits + sqlite/ (migrations, FTS, vectors)
├── search/ fulltext, semantic, hybrid fusion, scoring, indexer
│ └── vector/ VectorIndex trait, sqlite scan, qdrant client
├── embeddings/ EmbeddingProvider trait, local, openai-compatible
├── agents/ AgentAdapter trait, claude, codex, cursor, generic
├── sync/ agent files, Markdown mirror, bundles, SSH remotes
├── mcp/ JSON-RPC protocol, tools, stdio server
├── config/ config.toml, path resolution
└── ui/ terminal formattingDevelopment
cargo fmt
cargo clippy --all-targets
cargo test # unit + CLI + MCP + migration tests
uv build --wheel # the artefact `uv tool install contextd` shipsCI runs the same three commands on Linux, macOS and Windows, and checks that
the wheel installs and runs. Tagging v* builds wheels for every platform and
publishes them to PyPI through trusted publishing.
Tests run against temporary CONTEXTD_HOME directories and never touch your
real memory store.
Configuration
contextd config prints paths and current settings; contextd config --toml
prints the file. Notable knobs:
[context]
max_context_tokens = 6000 # the injection budget
max_memories = 40
[vector]
backend = "sqlite" # or "qdrant"
url = "http://localhost:6333"
collection = "contextd"
[search]
fts_weight = 1.0
semantic_weight = 1.0
priority_weight = 0.35
recency_weight = 0.25
project_weight = 0.5
recency_half_life_days = 90.0
superseded_penalty = 0.35 # how far history is pushed below current truth
[sync]
tombstone_retention_days = 365 # how long deletions keep propagating
[refresh]
duplicate_threshold = 0.9 # at or above this, memories are merged
similar_threshold = 0.65 # at or above this, they are reported
summarizer = "none" # or "openai" to consolidate clustersStatus
Working today: projects, memories, checkpoints, decisions, sessions, FTS5 search, hybrid semantic recall, context budgeting, Claude/Codex/Cursor/generic adapters, Markdown mirror with conflict detection, refresh, cross-machine sync over SSH, pluggable embedding providers (local or any OpenAI-compatible endpoint), pluggable vector stores (SQLite or Qdrant), and the MCP server.
Planned: richer conflict resolution in refresh, more agent adapters, and a
scheduled background pull for machines that are usually reachable.
Licence
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides persistent memory for AI agents using hybrid search (vector embeddings + BM25) with neural reranking, enabling storage and retrieval of insights, debugging solutions, and patterns across coding sessions.8MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents to retrieve and manage code context with hybrid search, project memory, and observability via MCP tools.29MIT
- AlicenseNot gradedqualityBmaintenanceEnables infinite searchable memory for coding agents across sessions, allowing them to recall past decisions and context.4814MIT
- AlicenseAqualityAmaintenanceProvides persistent, searchable memory across AI coding agent and chat history (Claude Code, Codex, Gemini CLI, ChatGPT, and more) via retrieval-augmented generation, enabling semantic and hybrid search to retain context across sessions.55MIT
Related MCP Connectors
Persistent memory and knowledge graphs for AI agents. Hybrid search, context checkpoints, and more.
Persistent memory for AI agents. Search, store, and recall across sessions.
Universal memory for AI agents and tools. Save, organize and search context anywhere.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/JohnsonWang1015/ContextD'
If you have feedback or need assistance with the MCP directory API, please join our Discord server