Doramagic Project Pack · Human Manual

context-keeper

MCP server that maintains project memory across Claude conversations — records decisions, pipelines, and constraints in human-editable JSON. Zero dependencies.

Overview & System Architecture

Related topics: Core Tools, Data Model & Retrieval

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Entry store and data integrity

Continue reading this section for the full explanation and source context.

Section Retrieval: lexical, semantic, and abstention

Continue reading this section for the full explanation and source context.

Section Capture-time guardrails and hooks

Continue reading this section for the full explanation and source context.

Related topics: Core Tools, Data Model & Retrieval

Overview & System Architecture

Purpose and Scope

context-keeper is an MCP (Model Context Protocol) memory server that gives coding agents durable, queryable knowledge about a project's decisions, constraints, conventions, and prior session outcomes. It runs as a local stdio server, ships as the PyPI package context-keeper-mcp, and is designed to be installed once per project so every connected agent shares the same store. Its distinguishing architectural commitments are stated explicitly in the project documentation: zero runtime dependencies, offline-by-default operation, and human-readable on-disk storage. Source: README.md:1-40

The server exposes a small set of record_* and query tools (notably record_decision, update_entry, deprecate_entry, plus retrieval endpoints) plus a hooks/ directory of MCP hook handlers that react to client events such as SessionStart and PostToolUse. Decisions written through these tools are indexed for both lexical and semantic recall, projected to an optional DECISIONS.md for human readers, and reinjected at session start via additionalContext so the next agent inherits the prior team's reasoning. Source: CLAUDE.md:1-60

High-Level Architecture

The system has three logical layers, all running in a single Python process and communicating with the MCP client over stdio:

LayerResponsibilityRepresentative modules
MCP transport / tool surfaceJSON-RPC framing, tools/list, tool dispatch, additionalContext deliveryserver.py, context_keeper/__init__.py
Domain logicEntry CRUD, supersession, ranking, abstention, summary generation, markdown projectioncontext_keeper/store.py, context_keeper/retrieval.py
Persistence and hooksAtomic file writes, index files, hook-driven constraint injectioncontext_keeper/store.py, hooks/scope_guard.py

The project ships with an audited build (v0.15.0) that tightened stdio UTF-8 handling, fixed a mirror-watermark data-loss bug, and trimmed the PyPI description for registry validation. Source: pyproject.toml:1-40

flowchart LR
    Agent[MCP client / agent] -->|tools/call| Server[server.py]
    Agent -->|SessionStart / PostToolUse| Hooks[hooks/scope_guard.py]
    Server --> Store[(context_keeper/store.py)]
    Hooks -->|additionalContext| Agent
    Store -->|write via os.replace| Disk[(Entry JSON files)]
    Store --> Index[(Lexical + semantic index)]
    Store -->|render-on-write| MD[DECISIONS.md]
    Index --> Server

Core Subsystems

Entry store and data integrity

Entries are persisted as individual files keyed by id, with writes performed through a temp-file + os.replace swap so a mid-write crash cannot corrupt existing entries. If a target entry file exists but cannot be parsed, mutating tools (record_*, update_entry, deprecate_entry) refuse to write rather than silently overwriting, preserving recoverability. This guard was introduced alongside the broader v0.5.0 data-integrity pass. Source: context_keeper/store.py:1-120

Retrieval: lexical, semantic, and abstention

Recall combines lexical scoring with pluggable embedding backends (multiple backends landed in v0.9.0) and optional clustering. Two ranking ideas were adapted from the competitor Curion: supersession-as-ranking (newer decisions outrank the entries they replace) and abstention (return a clear "no relevant memory" signal when nothing crosses a confidence threshold, rather than fabricating context). The project's release notes explicitly note that Curion's per-call LLM controller was rejected to keep the server zero-dependency and offline. Source: context_keeper/retrieval.py:1-160

Capture-time guardrails and hooks

The hooks/ package implements MCP hooks. hooks/scope_guard.py runs as a PostToolUse hook on Edit|Write|NotebookEdit: when an agent edits a file that falls under a stored constraint's scope, the matching constraint is injected via additionalContext. This complements the session-start injection of project rules, enforcing them at the exact moment they are most likely to be violated. Source: hooks/scope_guard.py:1-80

Session-start summary and tool-schema budget

At SessionStart the server calls get_project_summary, which renders the canonical store into a token-bounded briefing. A regression test caps the tools/list payload at roughly 2,500 estimated tokens after v0.7.1 trimmed per-tool descriptions (for example, record_decision's description dropped from ~738 to a much smaller figure), so future schema additions must fit the budget. Source: server.py:1-160

Markdown projection and retrieval hints

When markdown_export.enabled is set, every decision mutation re-renders DECISIONS.md synchronously (render-on-write), giving humans a version-controllable view of the same data the agent sees. Separately, every record_* tool accepts retrieval_hints — two to four anticipated future phrasings — which are indexed alongside the entry body to mitigate vocabulary-mismatch queries. Source: context_keeper/store.py:120-220

Design Principles

Three principles recur across the changelog and the contributor guidance in CLAUDE.md:

  1. Zero-dependency, offline-first. All ranking, embedding (where a local backend is configured), and persistence happen in-process; no network calls are required for core operation. Source: pyproject.toml:1-40
  2. Source of truth is the file store. Hooks and tools derive state from the same on-disk entries, and the optional DECISIONS.md is a projection rather than a separate source. Source: CLAUDE.md:1-60
  3. Bounded context budgets. Both the tools/list schema and the SessionStart summary are explicitly token-capped with regression tests, so growth in capability cannot silently erode the agent's working context. Source: server.py:1-160

Together these choices make context-keeper a small, auditable memory layer that an agent team can adopt per repository without surrendering locality, determinism, or human readability.

Source: https://github.com/jarmstrong158/context-keeper / Human Manual

Core Tools, Data Model & Retrieval

Related topics: Overview & System Architecture, Hooks & Capture-Time Guardrails, Two-Way Mirror & Remote Sync (v0.15)

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Overview & System Architecture, Hooks & Capture-Time Guardrails, Two-Way Mirror & Remote Sync (v0.15)

Core Tools, Data Model & Retrieval

The MCP surface of context-keeper is intentionally narrow: a small set of record_* mutation tools, a single update_entry / deprecate_entry pair for lifecycle, a session-start get_project_summary reader, and a retrieval layer that fuses lexical and semantic matching. Everything is zero-dependency, offline by default, and persisted as one JSON file per entry under the project store.

Core Tools

The tools defined in server.py form the public API that every connected MCP client loads into context. Their schemas are tightly trimmed (see v0.7.1) so that the cumulative tools/list payload stays under the ~2500-token regression cap.

ToolPurposeKey fields
record_decisionCapture a new decisiontext, rationale, tags, scope, origin, retrieval_hints
update_entryEdit an existing entry in placeentry_id, patched fields
deprecate_entrySoft-delete / supersedeentry_id, supersedes, reason
get_project_summarySession-start injectiontruncation-bounded summary

record_decision alone previously consumed ~738 estimated tokens in its schema description; the v0.7.1 budget pass cut the whole surface from ~2800 to ~2370 tokens, and added a regression test that fails any future field which would breach 2500. Source: server.py:1-1

Mutation tools reject writes when an entry file already exists but cannot be parsed, and they always perform an atomic temp-file + os.replace swap. A crash mid-write therefore cannot leave a half-written entry on disk. Source: server.py:1-1

Data Model

Entries are flat JSON documents persisted one-per-file in the project store. Every mutation tool (record_*, update_entry, deprecate_entry) re-derives indexes and exports in the same call, so the store stays self-consistent without a background process. Source: server.py:1-1

The canonical shape includes:

  • id — stable identifier used by update_entry / deprecate_entry
  • text, rationale — captured content
  • tags — short labels for grouping
  • scope — file patterns a constraint applies to (consumed by hooks/scope_guard.py via PostToolUse on Edit|Write|NotebookEdit)
  • origin — provenance trust tag (introduced in v0.7.0)
  • retrieval_hints — 2–4 alternate phrasings indexed for both lexical and semantic search
  • supersedes — list of superseded entry ids; supersession is treated as a ranking signal, not a deletion

The mirror.py module projects the canonical store into auxiliary surfaces. When markdown_export.enabled is true, every decision mutation re-renders the human-readable DECISIONS.md at the configured path (render-on-write, no separate watcher). Source: mirror.py:1-1

usage.py records per-tool invocation telemetry that feeds the work-focus signals, while code_drift.py cross-references stored entries against the current working tree so stale decisions can be surfaced.

Retrieval

Retrieval is the part of the system where context-keeper diverges most visibly from LLM-controller competitors like Curion. There is no per-query API call: matching runs entirely on local indexes built from the entry text plus its retrieval_hints. Source: semantic_index.py:1-1

The pipeline combines:

  1. Lexical matching over the text and retrieval_hints fields (the zero-dependency fix for vocabulary-mismatch queries introduced in v0.7.0).
  2. Semantic matching through semantic_index.py, which supports multiple embedding backends added in v0.9.0.
  3. Clustering — entries with similar embeddings are grouped so that recalling one surfaces related context without needing every entry to match the exact phrasing.
  4. Abstention — when the combined score falls below a threshold the system returns no result rather than a low-confidence match (introduced in v0.10.0, adapted from studying Curion's design while keeping context-keeper offline).
  5. Supersession-as-ranking — a deprecated entry that has been superseded is demoted in result order rather than hidden, preserving auditability of the decision lineage.

get_project_summary is the session-start projection of this same store. It was the subject of a critical v0.9.0 fix: the truncation loop previously evaluated the *original* text in its while condition, so any store whose summary exceeded the token budget silently emitted an empty summary. The fix evaluates the *truncated* text, and the regression test now pins behavior for stores above the budget threshold. Source: server.py:1-1

Work Focus and Drift

work_focus.py consumes the usage telemetry from usage.py to compute what the agent has been actively editing, and feeds that into retrieval boost so recently-touched entries rank higher in the next session. Source: work_focus.py:1-1

code_drift.py runs a periodic comparison between entry scope / referenced symbols and the current file tree, flagging decisions whose referenced files no longer exist or whose content has moved substantially. Drift signals are surfaced through the same retrieval surface, not as a separate tool, so the agent encounters them in the natural flow of recall rather than as a noisy background task. Source: code_drift.py:1-1

Source: https://github.com/jarmstrong158/context-keeper / Human Manual

Hooks & Capture-Time Guardrails

Related topics: Core Tools, Data Model & Retrieval, Two-Way Mirror & Remote Sync (v0.15)

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Core Tools, Data Model & Retrieval, Two-Way Mirror & Remote Sync (v0.15)

summary>

The following source files were used to generate this page:

Hooks & Capture-Time Guardrails

context-keeper is a zero-dependency MCP memory server that relies on a set of small, single-purpose hooks to inject context at the moments when it matters most. The hooks collectively form *capture-time guardrails* — automatic reminders and constraint re-injections that fire the moment an agent begins a session, edits a file, runs a pre-compaction summarization, or prepares a commit. Each hook is independent, JSON-config-driven, and safe to fail silently so that MCP client behavior is never blocked. Source: hooks/__init__.py

Hook Inventory and Lifecycle Placement

Six hooks ship in the hooks/ package, each tied to a different stage of the agent loop:

HookTriggerPrimary Purpose
session_start.pySessionStart / first turnLoad get_project_summary and active constraints into context
pre_compact.pyPreCompactSnapshot relevant memory before the host summarizes the conversation
post_compact.pyPostCompactRe-inject project summary and constraints after compaction
scope_guard.pyPostToolUse on `EditWriteNotebookEdit`Inject constraint text when a touched file falls inside a constraint's scope
constraint_reinject.pyRecurring / turn boundaryRe-brief constraints that are still active in the current session
commit_capture_reminder.pyPre-commit / git hookRemind the agent (or human) to capture any pending decisions before commit

This placement is deliberately redundant: critical context is re-broadcast at every transition where a model could otherwise lose it. Source: hooks/session_start.py, hooks/post_compact.py, hooks/scope_guard.py

Session-Start and Summary Injection

hooks/session_start.py runs once when a client connects. It calls into the MCP server's get_project_summary tool to assemble a token-bounded project briefing — typically the most recent decisions, constraints, and superseded entries — and returns it through Claude Code's additionalContext channel. Since v0.5.0, the truncation loop walks the rendered text instead of the raw entries, which fixes the empty-injection bug that previously occurred for stores with ~30+ entries. Source: hooks/session_start.py, referencing the v0.9.0 release notes fix.

The pre-compact and post-compact hooks form a complementary pair. pre_compact.py records the current snapshot of relevant memory so a later recall can recover context that the host's auto-compactor is about to throw away. post_compact.py then re-injects the summary and constraints, mirroring the session-start behavior so the agent resumes with an intact brief. Source: hooks/pre_compact.py, hooks/post_compact.py

Scoped Constraint Injection (v0.6.0)

hooks/scope_guard.py is the headline feature of the v0.6.0 *Capture-time guardrails* release. Constraints stored in the decisions store can declare a scope — a glob, path prefix, or language tag that identifies the files they apply to. The hook subscribes to PostToolUse on Edit, Write, and NotebookEdit so it can inspect the file the agent is about to (or has just) modified. When the touched path matches a constraint's scope, the hook immediately injects the constraint's prose into context via additionalContext. Source: hooks/scope_guard.py

The design rationale, as documented in the v0.6.0 release, is that session-start injection briefs the rules once at turn one, but scope_guard enforces them at the exact moment the agent is most likely to violate them. A constraint that says "never edit migrations/ without a backup" is harmless if it has already scrolled out of the model window; firing it on the PostToolUse event restores it just-in-time. The hook is read-only against the memory store and returns a non-blocking dictionary, so a misconfigured scope never aborts the underlying tool call. Source: hooks/scope_guard.py

Recurring Re-injection and Capture Reminders

Two hooks address drift across longer sessions. constraint_reinject.py periodically re-asserts constraints that are still active but may have been pushed out of the model's context window. Unlike scope_guard, it does not depend on a file event — it is triggered by the host's recurring hook callback (or by a turn-boundary check) and re-emits the constraint text verbatim. This supplements, rather than replaces, scope_guard, because not every constraint is scoped to a file. Source: hooks/constraint_reinject.py

commit_capture_reminder.py is the final guardrail. It fires before any git commit and prompts the agent to record any latent decisions it has not yet stored — a common blind spot, since commits frequently codify behavior that was decided mid-session but never persisted as a record_decision call. The hook reads the staged diff, compares file paths and symbols against the in-memory store, and emits a reminder listing entries that look like they should be captured. Source: hooks/commit_capture_reminder.py

Design Properties

Across all six hooks, three properties are consistent:

  1. Zero-dependency: every hook imports only from the MCP server's own __main__ and the standard library, preserving the project's offline-first posture. Source: hooks/__init__.py
  2. Fail-soft: hook handlers catch and log exceptions internally, returning an empty additionalContext rather than raising — so a broken constraint file or unreadable store cannot crash the host. Source: hooks/scope_guard.py, hooks/constraint_reinject.py
  3. Render-on-read, not render-on-write: hooks re-query the live store on every invocation rather than caching, which keeps tooling token costs predictable and avoids stale-context bugs. Source: hooks/session_start.py, hooks/post_compact.py

Together these hooks turn context-keeper from a passive memory store into an active guardrail layer, ensuring that decisions, constraints, and project summaries are surfaced at precisely the moments the agent is most likely to need — or forget — them.

Source: https://github.com/jarmstrong158/context-keeper / Human Manual

Two-Way Mirror & Remote Sync (v0.15)

Related topics: Core Tools, Data Model & Retrieval

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Core Tools, Data Model & Retrieval

Two-Way Mirror & Remote Sync (v0.15)

Purpose and Scope

The Two-Way Mirror is a synchronization subsystem that keeps a derived, human-readable projection of the canonical decision store in lockstep with the on-disk entries managed by server.py. Rather than introducing a separate LLM-controlled controller layer, context-keeper’s mirror stays pure-Python, zero-dependency, and offline-by-default. The mirror module lives in mirror.py and is invoked at every decision-mutation boundary so that the projected artifact cannot drift from the canonical store. Source: mirror.py:1-1

The "two-way" terminology reflects the bidirectional responsibility of the module:

  • Canonical → projected: on every record_decision, update_entry, or deprecate_entry mutation, the canonical entry is re-rendered into the mirror artifact.
  • External → canonical: the same module exposes the read path that reconciles the mirror’s watermark with the on-disk entry set during sync.

This subsystem first shipped as opt-in markdown_export configuration in v0.8.0 and was hardened for production in the v0.15.0 audited build. Source: mirror.py:1-1

Rendering Pipeline

The mirror is a render-on-write projection. Every mutation observed by server.py forwards to mirror.py to regenerate the projected file. The configuration is minimal and stored alongside other context-keeper settings:

{
  "markdown_export": {
    "enabled": true,
    "path": "DECISIONS.md"
  }
}

Key behavioral properties:

  • Idempotent regeneration — the entire projected file is rewritten on each call, so partial corruption is impossible to observe in the output.
  • Path-controlled — the path field determines where the artifact is written; defaults to DECISIONS.md at the project root.
  • Mutation-triggered — write events come exclusively from the record/update/deprecate tools; read-side traffic does not trigger regeneration. Source: mirror.py:1-1

v0.15 Audit Fixes

The v0.15.0 release repaired three defects specific to the mirror subsystem, all of which had been latent since the v0.8.0 introduction:

DefectSymptomFix
Mirror watermark data-lossThe watermark tracking which canonical entries had been projected could be reset, causing previously projected decisions to disappear from the mirror output across restarts.Watermark persistence path corrected so that progress is durable across process restarts.
mirror.py packagingThe module was not always shipped inside the bundled distribution, causing imports of mirror helpers to fail under python -m context_keeper invocations.Packaging manifest updated to include mirror.py; verified by scripts/build-mcpb.sh.
UTF-8 stdioThe stdio transport could mangle non-ASCII entries during mirror-triggered writes.Encoding explicitly set to UTF-8 on all stdio paths.

Source: v0.15.0 release notes, mirror.py:1-1

Packaging and Distribution

The mirror module ships with the MCP server bundle. The audited v0.15.0 build ensures that whenever a user installs context-keeper via any of the supported packaging channels, mirror.py is present and importable:

  • PyPI (context-keeper-mcp) — the wheel includes mirror.py after the packaging fix; verified by the build script before publish.
  • MCP registry (server.json) — the entrypoint references the corrected module set; the registry manifest in server.json is consistent with the wheel contents.
  • Anthropic / Glama registries — the auxiliary manifests in glama.json and the MCPB bundle declared in mcpb/manifest.json list the same entrypoint, ensuring no channel ships a partial install.
  • MCPB bundle — produced by scripts/build-mcpb.sh, which archives the project tree after explicitly verifying that mirror.py is included.

Source: server.json:1-1, glama.json:1-1, mcpb/manifest.json:1-1, scripts/build-mcpb.sh:1-1

When to Enable the Mirror

The mirror is opt-in. Recommended usage patterns from project history:

  • Enable it in repositories where humans regularly read decisions without an MCP client available (the DECISIONS.md artifact is a Markdown file and works in any text viewer or version-control diff tool).
  • Disable it in automated CI environments where each record-tool call would trigger a write to the working tree and conflict with concurrent jobs.
  • Keep it disabled when the canonical store is the only authoritative surface and no external consumer needs a Markdown view.

Source: mirror.py:1-1, v0.8.0 release notes

Source: https://github.com/jarmstrong158/context-keeper / Human Manual

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

medium Configuration risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Capability evidence risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Maintenance risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Security or permission risk requires verification

May increase setup, validation, or first-run risk for the user.

Doramagic Pitfall Log

Found 7 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Configuration risk - Configuration risk requires verification.

1. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.host_targets | https://github.com/jarmstrong158/context-keeper

2. Capability evidence risk: Capability evidence risk requires verification

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.assumptions | https://github.com/jarmstrong158/context-keeper

3. Maintenance risk: Maintenance risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper

4. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: downstream_validation.risk_items | https://github.com/jarmstrong158/context-keeper

5. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: risks.scoring_risks | https://github.com/jarmstrong158/context-keeper

6. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: issue_or_pr_quality=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper

7. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: release_recency=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/jarmstrong158/context-keeper

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 9

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using context-keeper with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence