Skip to main content
Glama

ContextD

Entwicklerkontext- und semantischer Erinnerungs-Manager für KI-Codierungsagenten.

Du erklärst Claude Code, Codex und Cursor in jeder Session dasselbe: was dieses Projekt ist, warum die Warteschlange NATS statt Redis ist, dass du vor dem Commit mit rustfmt formatierst, und wo du letzte Nacht aufgehört hast. ContextD speichert das einmal – projekteübergreifend und agentübergreifend – und gibt über eine CLI und einen MCP-Server nur die Teile zurück, die für die jeweilige Aufgabe relevant sind.

Claude Code ─┐
Codex ───────┤
Cursor ──────┼── MCP ── ContextD ── SQLite + FTS5 + embeddings
other agents ┘

Zwei Regeln, denen das Design folgt

Alles speichern, nur das Relevante einspeisen. Ein Jahr Erinnerungen passt nicht simpackage in ein Kontext fenster. Der Abruf ist hybrid (Volltext + Vektor), gewichtet und in ein explizites Token-Budget queeft; was nicht mehr hineinpasst, wird gezählt und nie stillschweigend verworfen.

Aktuelle Wahrheit muss von historischer Wahrheit unterscheinbare. Wenn eine Warteschlange von Redis über PostgreSQL zu NATS wechselt, muss einem Agent NATS gesagt werden, nicht die Option, die zufällig am häufigsten erwähnt wurde. Überholte Erinnerungen behalten ihren – und bleiben durchsuchbar, sind aber markiert, werden geteert und aus Retrieval ausgeschlossen, es sei denn, sie werden explizit angefragt.

Related MCP server: ContextAtlas

Installation

uv tool install contextd        # puts `contextd` on your PATH
contextd --version

uv installiert das veröffentlichte wheel, das die kompilierte Binary enthält – keine Rust-Toolchain und keine Python-Medizin zur Laufzeit. Wird contextd danach nicht gefunden, führe uv tool update-shell aus (uv installiert in ~/.local/bin) und öffne eine neue Shell. Ohne Installation kannst du es testen mit: uvx contextd status.

Aus einem checkout, oder um eine unveröffentlichte Änderung auszuführen:

uv tool install .               # builds with your Rust toolchain
cargo install --path .          # the same thing, straight from cargo

SQLite ist eingebaut – keine Systembibliotheken, kein Docker, keine Dienste, die laufen müssen. Linux, macOS und Windows. Builds aus dem Umbruch erfordert Rust 1.85+.

Optionale Umgebungsvariablen:

Variable

Wirkung

CONTEXTD_HOME

Wo der Speicher liegt (default ~/.contextd) – lege es auf einen kompatiblen Ordner, or halten Arbeits-,

  • Oder Du may keep work and personal Speicher getrennt. | | NO_COLOR | Farbe deaktivieren, wie auch --no-color und general.color = "never" | | RUST_LOG | Protokolllevel für die CLI und den MCP-Server; Logs gehen nach stderr, nie nach stdout. |

Schnellstart

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 stdio

contextd 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 serve

Befehle

Befehl

Funktion

init

Erstellt Home-Verzeichnis, Datenbank und Konfiguration

attach / detach / list

Verfolgt ein Repository als Projekt

status

Anzahl, Git-Status, letzter Checkpoint, Index-Zustand

add / edit / delete / show / memories

CRUD für Memory

supersede <old> <new>

Hobides end that one Memory replaced another

search

Schlüsselwort-first Suche über Memory, ADRs and Checkpoints

recall

Frage stelden;hybridisches semantisches + Schlüsselwort-Retrieval

checkpoint / resume

"Where was I?" speichern und öfterherstellen

decision add/list/show/supersede

Architecture Decision Records

session start/end/list/show

Arbeitsanzeige und Sockets das Ergebnis

refresh

Duplikate zusammenführen, Historie markieren, Indizes neu

sync

Markdown- Mirror and bound agent files

import / export <agent>

Context in and from agent files

remote add/list/remove

Maschinen, with denen Informationen exchange

remote scan

Bestandsaufnahme a-macht; was es hat, ohne compare it

inventory

Das gleiche Lokal control

remote pull / remote push

Sync memory over SSH, record for record

bundle export/import

Dieselbe Entsetze als JSON-Datei

mcp serve / mcp tools

MCP auslisted; showing tools

config

Zeigt Pfade und Einstellungen; set, get, --check

Jeder Befehl nimmt --json aspezifische Ausgaben, --project <name>, um auf einem anderen Projekt zu arbeiten, and --home <dir> (oder $CONTEXTD_HOME ), um auf einen anderen Speicher zu zeigen.

MCP

contextd mcp serve            # newline-delimited JSON-RPC on stdio
contextd mcp serve --read-only

Registriere es bei jedem MCP-Client – für Claude Code:

claude mcp add contextd -- contextd mcp serve

Angebotene Tools:

Tool

Verwendung

project_context

Kontext am Sitzungsbeginn, limitiert auf eine Token-Menge

semantic_recall

Beantwortet eine Frage aus dem Speicher (hybrid Abruf)

memory_search

Schlüsselwort zuerst Suche

memory_get

Ein einzelnen Speicher vollständig

project_status

Ab und Hal +2 Branch, Index-Zustand

checkpoint_latest

Aktuelles Ziel, Erledigtes, Nächst, offene Probleme

`architecture_decisions:

Zurzeit geltende Entscheidungen

session_history

Welcher Agent wann gewirkt hat, und was dabei froh ist

memory_add, checkpoint_create, session_summarize

Schreibe (übersprungen bei --read-only)

Ergebnisse tragen Lebensphases-Status, und alles, was superseded (überholt) ist, wird NICHT current markiert, damit ein Modell Historynicht mit dem gegenwärtigen Stand verwechselt.

Mehrere Rechner

Zwei Geräte benutzen – Laptop und Desktop – war früher die Sommer bedeutet, zwei Zustand. ContextD tauscht records aus, notfiles:

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 first

remote scan inventarisiert ein Gerät, bevor man sich festlegt. Es meldet Zahlen instead of content; was ist uns on a Maschine ist used little Kilobyte statt deren ganzen Memory, and it zuggte with a destination that has not yet been configured as remote:

$ 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 fügt eine Auflistung pro Projekt hinzu. contextd inventory macht dieselbe Bibliothek lokal. Das Konto is das, unter dem du per SSH eingeloggt bist; das Home-Verzeichnis is on that machine resolved ($CONTEXTD_HOME, otherwise ~/.contextd). Wenn sie woanders liegt, gib --remote--home an.

Rechner, die ein Passwort wollen

Run it in a terminal and ssh will ask, just like in standalone:

$ contextd remote scan dev@lab-box
dev@lab-box's password:

Passworteingabe, Host-Key-Checks and 2FA work because ssh reads them from the terminal. Each command decides by itself: if a terminal is present, ssh gets asked; if not no previous date – cron, a Pipeline, the MCP server – it passes BatchMode=yes, so a missing key fails after instead of waiting and waiting. Force either side with --interactive or --batch.

Wenn das Remote über ein installiertes contextd verfügt, ssh es aber nicht findet

ssh host command tries to start a non-interactive, non-login shell, and the stock ~/.bashrc immediately returns those – before the lines that would put ~/.local/bin or ~/.cargo/bin into PATH. Thus contextd can be installed and work there but still be "not found". Which case that is for you:

ssh you@host 'command -v contextd'                  # nothing? not installed
ssh you@host 'bash -lc "command -v contextd"'       # found? a PATH problem

Both ways to fix:

contextd remote add lab you@host --login-shell                 # read ~/.profile first
contextd remote add lab you@host --command '~/.local/bin/contextd'

Note the quotes. If they are missing, your own shell expands ~ before ContextD ever sees it, and so the remote config got a path expressed from that machine – which is relevant when both accounts have different home directories. ContextD notes this if you forget.

A quoted ~/ or $HOME/ path is expanded remote rather than local, and a login shell that emits banner text still doesn't break anything – the JSON result is extracted from the output.

Asking once instead of every time

Each command opens its own connection, so scan and then pull would ask twice. Two ways to avoid 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=5m

pull runs contextd bundle export on the remote via SSH and merges the result. Merging is by UUID, therefore:

  • Run it twice, second run has no effect;

  • if a record exists on both sides, the newer updated_at wins;

  • if both sides were changed, the local copy is kept and the divergence is reported instead of silently resolving;

  • supersensible tags travel, so endA history closed on one machine remains closed on the other;

  • deletions travel too, and they keep: a memory deleted on the laptop goes from the desktop and reaches a third machine via either one.

Deleting on multiple machines

contextd delete writes a tombstone – a note of that deletion with a time – and that note travels like any other record. Without it, the next sync from a machine that still has the memory would happily hand it back.

A deletion is treated as a decision with a time, so the most recent decision decides:

Situation

Ergebnis

On A deleted, on B unchanged

Also removed on B, and on every subsequent machine

On A deleted, on B later edited

The edit wins, the record returns, the tombster is cleared

On A and B deleted

Removed everywhere, exactly once

Deleting an entire project (contextd detach --purge) is a local cleanup and deliberately not synced: when one machine tidies up, it shouldn't force the others to forget the project.

Tombstones are kept for sync.tombstone_retention_days (one year by default) and are then discarded by contextd refresh. A machine that hasn't synced in that time can later reanimate a record that it never knows it's deleted – reduce the retention only if you actually sync enough.

If you might want a record back, prefer contextd delete --archive: it is reversible, it also syncs, and archived memories remain out of Abruf but appear in contextd memories --all.

Copying contextd.db around was deliberately rejected: two devices that both record something since the last exchange must both keep their work, and a file copy can only pick one winner.

Projects are matched on multiple machines by Git remote (SSH and HTTPS URL forms are treated as the same repo), then by slug. An arriving “projekt” has no local path; contextd attach in your checkout pulls it in instead of creating a detour second something projekt.

no SSH? Same exchange as a file:

contextd bundle export --out memory.json     # on one machine
contextd bundle import --file memory.json    # on the other

Embeddings are not transported – they are derived; the other machine may use a different provider, and a pull locally rebuilds embeddings faster than it woulda be to send them.

Sitzungen

A session is one sich clinging workstring on one project by one agent. contextd mcp serve opens one automatically as soon as a client connects – the agent’s name comes from the MCP handshake – and closes it when the connection ends. Our terminal:

contextd session start --agent claude
contextd session end "heartbeat wired up"
contextd session list
contextd session show          # what the current or last session produced

Checkpoints can be linked to during a session; memories and decisions are attributed using the 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 transport

Pro Projekt ist nur eine Sitzung geöffnet: Wird eine neue gestartet, schließt das die vorherige, sodass ein abgestürzter Agent nicht die Arbeit des nächsten einsammeln kann. Sitzungen zeichnen Aktivitäten auf dieser Maschine auf und bleiben daher lokal — contextd bundle transportiert das Wissen, nicht die Anwesenheit.

So funktioniert das Retrieval

query → project detection → FTS5 → semantic → ranking → token budget → context

Der Score eines Kandidaten ist eine gewichtete Summe, multipliziert mit einem Lifecycle-Faktor:

(fts + semantic + priority + recency + project_match) × status_multiplier

Jedes Gewicht steht in config.toml, und der Scorer ist ein Trait (search::scoring::Scorer), sodass die Formel ausgetauscht werden kann, ohne das Retrieval anzufassen. contextd search --explain gibt die Aufschlüsselung pro Treffer aus.

Embeddings

Der Standard-Provider ist lokal: ein Offline-Feature-Hashing-Embedder — kein Modell-Download, kein Netzwerk, kein API-Schlüssel. Er erfasst lexikalische Überlappung und Formulierung, was für hybrides Retrieval reicht, um Schlüsselwörter allein zu schlagen, aber er kann Wörter, die nie gemeinsam vorkommen, nicht in Beziehung setzen.

Für echtes Paraphrase-Matching richtest du ContextD auf einen beliebigen OpenAI-kompatiblen Endpunkt (Ollama, TEI, vLLM, LM Studio, OpenAI selbst). bge-m3 ist ein guter Standard: mehrsprachig, sodass eine Frage auf Chinesisch eine auf Englisch geschriebene Erinnerung findet.

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 model

Der API-Schlüssel wird, wenn einer benötigt wird, aus der Umgebungsvariable gelesen, die in embeddings.api_key_env benannt ist — und nie in die Konfigurationsdatei oder die Datenbank geschrieben. provider = "none" deaktiviert Vektoren vollständig und ContextD fällt auf Volltextsuche zurück.

Vektorspeicher

Vektoren werden über einen VectorIndex-Trait mit zwei Backends durchsucht:

Backend

Wann

sqlite (Standard)

Brute-Force-Cosinus über die Vektoren, die bereits in der Datenbank sind. Nichts zu installieren, sub-Millisekunden in persönlichen Maßstab.

qdrant

Du betreibst bereits Qdrant, oder deine Erinnerung ist über einen Scan hinausgewachsen.

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 --check

Die Kollektion wird beim ersten Verwenden erstellt, dimensioniert nach dem Embedding-Modell und mit Cosinus-Distanz; eine bestehende Kollektion mit falscher Breite (etwa beim Wechsel von einem 384-Dimensionen-Modell auf bge-m3's 1024) wird mit dem Befehl gemeldet, der sie korrigiert, statt bedeutungslose Nachbarn zu erzeugen.

SQLite behält die maßgebliche Kopie jedes Vektors, welches Backend auch immer gewählt ist, sodass ein externer Index immer neu erstellt werden kann, contextd bundle weiter läuft und eine Maschine ohne Qdrant dieselbe Erinnerung lesen kann.

Wenn der Vektorspeicher oder der Embedding-Endpunkt nicht erreichbar ist, fällt das Retrieval auf Volltextsuche zurück und sagt das auch — context viele status zeigt das Backend und ob es antwortet.

Speicherlayout

SQLite ist die Quelle der Wahrheit. Der Markdown-Spiegel existiert, damit du deine Erinnerungen lesen, diffen und committen kannst:

~/.contextd/
├── config.toml
├── contextd.db
├── projects/Orbit/
│   ├── overview.md  architecture.md  decisions.md  tasks.md
│   └── checkpoints/
└── global/
    ├── coding.md  git.md  preferences.md

Deine Dateien gehören dir

Generierte Inhalte leben in einem markierten Block:

# House rules            ← yours, never touched
Never force-push to main.

<!-- contextd:begin -->
...generated context...  ← ContextD's
<!-- contextd:end -->

ContextD zeichnet einen Hash dessen auf, was es geschrieben hat. Wenn der Block sich danach verändert hat, verweigert contextd export den Export und beendet sich mit einem Nicht-Null-Exitcode, bis du --force übergibst. Das Gleiche gilt für den Markdown-Spiegel, wo contextd sync --adopt deine manuellen Bearbeitungen in Erinnerungen verwandelt, statt sie zu verwerfen.

Architektur

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 implementation

Jede Schicht hängt nur von den darunterliegenden ab. Nichts oberhalb von storage erwähnt SQLite, nichts oberhalb von embeddings nennt einen Provider, und der MCP-Server ist ein Client von core ganz genau wie das CLI — damit aus der geplanten Entwicklung (SQLite → FTS → Embeddings → semantisches Gedächtnis → MCP) nicht ein verworren fand eines einzigen Moduls wird.

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 formatting

Entwicklung

cargo fmt
cargo clippy --all-targets
cargo test              # unit + CLI + MCP + migration tests

uv build --wheel        # the artefact `uv tool install contextd` ships

CI führt die gleichen drei Befehle auf Linux, macOS und Windows aus und prüft, dass das Wheel sich installieren und läuft. Das Taggen mit v* baut Wheels für jede Plattform und veröffentlicht sie über Trusted Publishing auf PyPI.

Tests laufen gegen temporäre CONTEXTD_HOME-Verzeichnisse und fassen deinen echte Speicher nie an.

Konfiguration

contextd config gibt Pfade und aktuelle Einstellungen aus; contextd config --toml gibt die Datei aus. Wichtige Stellschrauben:

[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 clusters

Status

Heute funktioniert: Projekte, Erinnerungen, Checkpoints, Entscheidungen, Sessions, FTS5-Suche, hybrides semantisches Abrufen, Kontext-Budgetierung, Claude/Codex/Cursor/generische Adapter, Markdown-Spiegel mit Konflikterkennung, Refresh, Cross-Machine-Sync über SSH, austauschbare Embedding-Provider (lokal oder ein beliebiger OpenAI-kompatibler Endpunkt), austauschbare Vektorspeicher (SQLite oder Qdrant) und der MCP-Server.

Geplant: Umfassendere Konfliktlösung in refresh, weitere Agent-Adapter und ein geplanter Hintergrund-Pull für Maschinen, die in der Regel erreichbar sind.

Lizenz

MIT — siehe LICENSE.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    A
    quality
    D
    maintenance
    Provides 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.
    8
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI coding agents to retrieve and manage code context with hybrid search, project memory, and observability via MCP tools.
    29
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Provides 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.
    5
    5
    MIT

View all related MCP servers

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.

View all MCP Connectors

Latest Blog Posts

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