Inspiration

Every AI coding agent is a goldfish. Open a new session with Claude, Codex, or Gemini and it has no memory of yesterday — what you decided, why, or what's half-finished. So it re-reads the codebase and re-derives context that already existed in someone's head an hour ago, burning tokens just to get back to where you left off. Multiply that by every session, every crash, every handoff between tools or teammates, and you're paying the same tax over and over.

The existing fixes felt wrong in two different ways. CLAUDE.md-style files stuff the same static context into every request whether it's relevant or not — a tax paid on every message. Checkpoint tools like Almanac write memory after the fact (after a commit, after a session ends), so the record is always at least one step behind — and if something crashes before the checkpoint, the memory never happened at all.

We wanted memory that's a tool call, not a prompt — costing zero tokens until the model decides it's relevant — and written during the session, not after, so it's never more than one step stale. We also didn't want it locked to one vendor's proprietary format: markdown in a real database means a fetch from Claude looks the same as a fetch from Codex, Gemini, or a teammate reading it in a dashboard. And since that memory is already structured project knowledge — decisions, status, architecture — it can double as documentation nobody has time to write separately.

What it does

Corpus gives AI coding agents persistent, cross-tool project memory through four MCP tools:

  • corpus_load — fetches the project's current state (Status, Next steps, Decisions) plus any topically relevant docs, at session start or on "continue where we left off." Zero tokens spent if it's never called.
  • corpus_log — appends a one-line decision, change, or bug note immediately after it happens, so nothing is lost between now and the next checkpoint.
  • corpus_save — a schema-forced end-of-session dump (summary, completed items, in-progress items with file references, decisions with reasons, ordered next steps) that gets merged into the project's living state document — current-state sections are rewritten, history sections are appended.
  • codebase_search — a pass-through to a bundled code-graph tool (Graphify) for structural questions ("what calls X?"), instead of grep-and-read exploration.

All of it is stored as markdown documents in a documentation database (Supabase for teams, a local folder for solo use) — never in the repo itself. A Next.js dashboard lets you browse a project's memory as real documentation, watch it update live, and see a token-savings counter. The demo: Claude Code works a feature, gets killed mid-session; Codex picks up with "continue" and actually continues.

How we built it

  • mcp-server-2 (TypeScript) — the one thing a user installs. Implements the four tool contracts, validates and renders model-submitted state into the fixed-section state document format, and shells out to the Graphify CLI for code-structure queries.
  • Documentation DB (Postgres) — the canonical store: workspaces, workspace_members, documents, usage_events. Documents are keyed by (workspace_id, name) so folder-name collisions between unrelated repos can't happen.
  • Pluggable storage — the same tool interface runs against Supabase (team mode) or a local ~/.corpus/<project>/ folder (zero-config solo mode), decided by whether a shared workspace was ever configured.
  • CLI connect verbs (corpus-setup, corpus-connect, corpus-disconnect, corpus-status, corpus-ls) — wire all three clients (Claude, Codex, Gemini configs) symmetrically, so a workspace id is the single bearer credential that ties a repo to shared memory.
  • frontend/ — a Next.js dashboard with Realtime subscriptions to the documents table, rendering a project browser, doc viewer/editor, force-graph view, and the token-savings counter.
  • Graph layer — Graphify builds a tree-sitter-based code graph once per server process (at setup, then at most once more on a session's first query), so codebase_search answers with symbols and source locations instead of raw file dumps.

Challenges we ran into

  • Keeping the repo untouched while still feeling "live." The non-negotiable design rule was that Corpus never writes into the target repo — but that meant getting real-time updates (dashboard, cross-tool handoff) entirely through the DB layer, with no filesystem signal to lean on.
  • Re-keying documents mid-build. We shipped server code that assumed a workspace_id column before the DB migration existed, which broke every tool call against un-migrated databases. We had to make the server detect the schema shape at runtime and fall back to the old project-slug keying with a loud warning, rather than assuming everyone had migrated.
  • Avoiding a silent memory fork. It was tempting to have a disconnected or misconfigured repo just quietly fall back to local memory. We decided against it — a repo that can't reach its configured workspace gets memory turned off with the fix spelled out, because a silent local fork is worse than no memory: it diverges invisibly from the real record.
  • Merge vs. append semantics. Getting corpus_save to rewrite current-state sections (Status, Next steps) while appending to history sections (Decisions, Session log) — instead of one giant append-only file that becomes an unreadable, self-contradicting pile — took a few iterations of the document schema before it held up under repeated saves.
  • Honest token accounting. We didn't want to quote Graphify's marketing multiplier, so we built usage_events.baseline_tokens to record a measured without-Corpus cost only where a real substitute exists to compare against (full corpus size vs. one fetched doc; full graph-indexed sources vs. a budgeted graph answer) — and left it null for writes, rather than inventing a savings number for actions that have no real baseline.

Accomplishments that we're proud of

  • A cross-LLM handoff that actually works end to end: Claude Code logs and saves state, the session is killed, and Codex or Gemini picks up cold from corpus_load and continues seamlessly.
  • A memory model that costs genuinely zero tokens when unused — it's a tool call the model chooses to make, not context injected into every request regardless of relevance.
  • Runtime schema detection that let us ship a breaking DB migration without bricking every existing workspace.
  • A design rule we actually held to under pressure: never fork memory silently. Degrade to "off" with instructions, every time, rather than the easy shortcut of a hidden local copy.
  • Turning memory into documentation for free — the dashboard's project pages aren't a separate deliverable, they're the same markdown the agents are already reading and writing.

What we learned

  • Memory-as-a-checkpoint and memory-as-a-ledger are fundamentally different reliability guarantees. A ledger written during the session, bullet by bullet, survives crashes that a "save after the session" model cannot.
  • Prompt-stuffed context and tool-fetched context have opposite cost curves. The former taxes every request; the latter costs nothing until it's actually useful — a small architectural choice with a large practical difference at scale.
  • Bearer credentials are simple but have a ceiling. Using a workspace id as the sole access credential got us to a working multi-client demo fast, but we learned exactly where it breaks down (no revocation, no per-user enforcement while the server holds a service-role key) — which shaped our roadmap more than any other decision.
  • Symmetry is the whole security model for a multi-client tool. Any connect/disconnect action that doesn't apply identically across all wired clients quietly reopens the hole you thought you closed.
  • Honest numbers build more trust than impressive ones. Choosing to measure real token deltas instead of quoting a vendor's multiplier made the eventual number less flashy — and far more defensible.

What's next for Corpus

  • Real per-user access control. Move the server off the Supabase service-role key onto per-user tokens and RLS policies, unlocking actual invites, roles, and revocation instead of today's all-or-nothing bearer access.
  • Topical documentation pages. Beyond the single state doc, grow a full Almanac-style wiki per project ("Authentication," "Schema migrations") that accumulates over many sessions.
  • pgvector relevance matching for corpus_load. Bring back the embeddings-based recall from the earlier server version, now layered on top of the graph-and-markdown model instead of replacing it.
  • Auth0 on the dashboard and workspace writes, closing the last piece of Phase 2.
  • Careful, opt-in auto-triggering. We deliberately rejected fragile hook-based auto-save for the MVP — every write is an explicit tool call — but a well-scoped, reliable auto-log on top of harness lifecycle events is worth exploring once the core is proven.
  • More export targets. One-click export of any document to GitBook, Notion, or a plain file in the repo, so the "no vendor lock-in" pitch extends past the tools and into wherever teams already keep docs.

Built With

Share this project:

Updates