AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Douglasjordan2 C0

mcp-douglasjordan2-c0 · by douglasjordan2

An external memory for LLMs: a bi-temporal knowledge graph with hybrid (keyword + vector) retrieval and a self-improving reflection loop. Benchmarked to beat flat vector RAG on corrections and time-versioned queries.

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add mcp-douglasjordan2-c0

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-douglasjordan2-c0)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
16d ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Douglasjordan2 C0? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

c0

An external memory for LLMs — a bi-temporal knowledge graph with hybrid (keyword + vector) retrieval and a self-improving reflection loop.

[](https://github.com/douglasjordan2/c0/actions/workflows/ci.yml) [](./LICENSE) [](https://www.rust-lang.org/)


Why

Language models are stateless between sessions and their training data goes stale. The usual fix — stuffing documents into a vector store — retrieves blobs of prose and has no notion of how knowledge changes over time.

c0 takes a different approach. It stores knowledge as a graph of concepts and the relationships between them, retrieves the relevant subgraph on demand, and tracks how each fact evolves. The result is a persistent, correctable memory layer you can query in natural language and grow as you work.

How it works

query ──▶ ❶ exact match ─▶ ❷ keyword (BM25) ─▶ ❸ hybrid (BM25 + vector, fused by RRF)
                                                          │
                                                          ▼
                                          resolve to a concept node in Neo4j
                                                          │
                                                          ▼
                                     traverse the graph for related context  ──▶  answer
                                                          │
                                               (no match) ▼
                                          reflection loop: learn from the miss
  • Graph storage (Neo4j). Knowledge lives as Concept nodes and typed relationships, not as text chunks — so retrieval can traverse from one idea to related ones.
  • Hybrid retrieval. A tiered cascade: exact match → keyword (Lucene/BM25) → hybrid, which runs keyword and vector search and merges them with Reciprocal Rank Fusion (RRF). Keyword nails exact names and identifiers; vectors catch synonyms and paraphrase; fusion gets the best of both without normalizing incompatible score scales.
  • Bi-temporal. Every concept carries two independent timestamps — when it was recorded (transaction time) and when it is true (valid time) — so you can run point-in-time ("as-of") queries, supersede a concept when it evolves, or invalidate it with a causal audit trail. Nothing is deleted; it's time-bounded.
  • Self-improving reflection loop. When a lookup finds nothing, the dead end is queued, and an LLM classifies it: commit a genuinely new, reusable concept, discard noise, or queue the uncertain ones for human review. Run it continuously (c0 reflector run) and c0 fills its own memory gaps as you work — [details below](#the-reflection-loop--c0s-learning-engine).

See it in action

Correct stale training data. A pre-2026 model insists you create a Shopify app in Admin → "Develop apps". c0 walks its graph and overrides that with a patch — current knowledge wins.

Hybrid retrieval — keyword + vector, fused by RRF. One paraphrased question, three ways: keyword (BM25) misses it, vector understands intent, hybrid fuses both.

Bi-temporal — ask "as of" any point in time. The same question returns the era-correct answer: the Pages Router in 2022, the App Router today, with a dated supersession trail.

Self-improving — a dead end becomes a new concept. A lookup misses, the reflection loop classifies it, and c0 commits the new knowledge to its own graph — no human in the loop.

Benchmark — does structured memory actually beat a vector store?

"It helps" is easy to assert. c0 bench makes it falsifiable: it seeds a synthetic knowledge world (invented entities no model saw in training), then answers the same questions three ways — a bare model, a naive flat vector store (embed → cosine top-k), and c0 — and grades every answer with an LLM judge. Because the facts are invented, the score isolates what the memory layer adds, not the model's prior knowledge.

10 questions, 4 categories, 3 trials each (majority vote):

| category | bare model | flat vector RAG | + LLM reranker | c0 | |----------------|:----------:|:---------------:|:---------------:|:----------:| | simple recall | 0/3 (0%) | 3/3 (100%) | 3/3 (100%) | 3/3 (100%) | | multi-hop | 0/2 (0%) | 1/2 (50%) | 0/2 (0%) | 2/2 (100%) | | correction | 0/2 (0%) | 0/2 (0%) | 0/2 (0%) | 2/2 (100%) | | temporal | 0/3 (0%) | 0/3 (0%) | 0/3 (0%) | 3/3 (100%) | | overall | 0/10 (0%) | 4/10 (40%) | 3/10 (30%) | 10/10 (100%) |

A vector store handles simple recall and not much else: it can't tell a corrected fact from the stale one it replaced (correction) and has no notion of an effective date (temporal) — and adding an LLM reranker doesn't help, because reranking reorders passages without synthesizing the date/supersession metadata that isn't in the text. Those are exactly what c0's temporal graph represents natively.

c0 bench --seed --arms bare,flat_rag,flat_rerank,c0 --trials 3

Full methodology, the synthetic corpus, and honest limitations: [BENCH.md](BENCH.md).

Retrieval eval — is the right concept surfacing?

c0 bench measures end-to-end answer quality; c0 eval measures the narrower thing the retrieval cascade is responsible for: given a natural-language query, does the right concept rank in the top k? It scores the cascade over the same synthetic fixture with the standard IR metrics — recall@k and MRR — so a change to HybridSearchConfig, the RRF fusion, or the fulltext query builder that silently degrades retrieval shows up as a number instead of a feeling.

c0 eval --seed --k 3                 # full cascade (exact → fulltext → hybrid, temporal)
c0 eval --seed --no-embeddings       # fulltext-only; no Ollama/API needed (the CI path)
c0 eval --judge                      # + opt-in LLM-as-judge context-relevance pass

The fulltext-only path is local-first (Neo4j only), so CI runs it as a gate (--min-recall) and fails the build on a regression — no model dependency required. Details and the golden set: [EVAL.md](EVAL.md).

Requirements

  • Rust (2024 edition — 1.85+)
  • Neo4j 5 — a docker-compose.yml is included
  • Ollama for local embeddings and the reflection loop's classifier (defaults: nomic-embed-text for embeddings, hermes3:8b for classification) — the whole loop runs locally, no key required
  • (optional) Claude for the background LLM — opt in with [claude] enabled = true to use Claude instead of a local model for classification/extraction, either via an Anthropic API key or your Claude subscription (the claude CLI). See [Configuration](#configuration)

Quickstart

# 1. Start Neo4j (binds to localhost only)
docker compose up -d

# 2. Pull the embedding model
ollama pull nomic-embed-text

# 3. Build & install
cargo install --path .

# 4. Point c0 at Neo4j (defaults shown; the bundled compose uses no auth)
export NEO4J_URI="bolt://localhost:7687"
export NEO4J_USER=""        # empty for the bundled docker-compose
export NEO4J_PASSWORD=""
# export ANTHROPIC_API_KEY="sk-..."   # optional; only for [claude] enabled = true (defaults are local Ollama)

# 5. Create indexes (vector + fulltext), then a namespace
c0 migrate
c0 init --namespace my-project

# 6. Add knowledge and recall it
c0 add concept "reciprocal rank fusion" -d "Rank-based fusion of multiple result lists; score = weight/(k+rank)."
c0 add concept "hybrid search" -d "Keyword (BM25) + vector retrieval, fused by RRF." --force
c0 relate "reciprocal rank fusion" USED_BY "hybrid search"   # both endpoints must exist
c0 walk "reciprocal rank fusion"                             # traverses outgoing edges -> "hybrid search"

> --force on the second concept skips the similar-concept guard: closely related ideas > often score as near-duplicates, and relate requires both endpoints to already exist.

Core commands

| Command | What it does | |---|---| | c0 walk | Recall: resolve a concept (exact → keyword → hybrid) and traverse for context | | c0 walk --as-of | Point-in-time recall (bi-temporal) | | c0 search | Hybrid search without traversal (--vector-only / --keyword-only) | | c0 add concept -d "" | Add a concept (embedded on write) | | c0 add patch --content "" | Add a knowledge patch that corrects/augments a concept | | c0 relate | Create a typed relationship | | c0 supersede --with | Mark a concept evolved into a newer one | | c0 invalidate concept --reason "" | Retract a concept with a causal trail | | c0 describe "" | Update a description (and re-embed) | | c0 reflector run | Run the learning loop: classify dead ends → commit new concepts (see [below](#the-reflection-loop--c0s-learning-engine)) | | c0 health --fix | Check Neo4j / Ollama / indexes | | c0 audit enrich | Reconnect orphaned concepts to nearest neighbours (--dry-run, --rollback) | | c0 export · c0 audit · c0 move | Maintenance utilities |

Run c0 --help for the full set.

More

Beyond the core commands, c0 includes a few subsystems worth knowing about (fuller docs are on the way):

  • Live sourcesc0 link source add --url fetches and embeds a page; c0 fetch and c0 link source search retrieve over them, so external references stay fresh in the graph.
  • Triggersc0 trigger add (or --semantic) decide when a prompt should consult c0; pair one with a [hook](#using-c0-with-claude-code) for hands-off recall.
  • Sessions — index your assistant transcripts (build with --features sessions), then c0 sessions search, c0 sessions resume, and track spend with c0 sessions cost.
  • Raw queries & historyc0 find "" runs Cypher directly against the graph; c0 invalidation-chain reads a concept's causal history.
  • Maintenancec0 backfill embeddings, c0 audit, c0 move, c0 export, c0 status, c0 config show.

Run c0 --help for flags.

Configuration

c0 reads connection details from the environment, with a per-namespace .c0/config.toml for local settings:

| Variable | Default | Purpose | |---|---|---| | NEO4J_URI | bolt://localhost:7687 | Neo4j connection | | NEO4J_USER / NEO4J_PASSWORD | empty | Neo4j auth | | ANTHROPIC_API_KEY | — | Optional; used only when [claude] enabled = true. By default, classification & extraction run on a local Ollama model — no key needed |

Embedding host/model (Ollama) default to http://localhost:11434 and nomic-embed-text, and are configurable.

Security & threat model

The bundled docker-compose.yml runs Neo4j with auth disabled and binds its ports to 127.0.0.1 only. This is fine for the intended default: a single-user local machine, where the graph is trusted memory for one person.

Be aware of the tradeoff: with auth off, any local process or user on the machine can read or rewrite the entire memory graph — and because this graph is explicitly positioned to override the model's training data, poisoning it is high-value. If you share the machine, run untrusted local code, or expose Neo4j beyond loopback, enable auth:

# Generate a password once and start Neo4j with it
export NEO4J_AUTH="neo4j/$(openssl rand -hex 16)"
docker compose up -d

# Point the CLI at the same credentials
export NEO4J_USER="neo4j"
export NEO4J_PASSWORD=""

Background LLM: local, API, or your Claude subscription

The reflection loop, concept extraction, and session enrichment use a chat LLM. By default that's local Ollama — keyless and offline. To use Claude instead, set [claude] in .c0/config.toml to one of:

Anthropic API — billed per token, needs ANTHROPIC_API_KEY:

[claude]
enabled  = true
provider = "claude"

Your Claude subscription, via the Claude Code CLI — no API key, no per-token cost (it shells out to claude -p, using whatever that CLI is logged into):

[claude]
enabled  = true
provider = "claude-cli"
binaries = { claude = "claude" }   # path to your `claude` binary

Either way, you can route per task — e.g. local extraction but Claude classification — with classification_provider, extraction_provider, enrichment_provider, and concept_extraction_provider. (droid, codex, and gemini are supported as providers too.)

> Unattended setups: the claude-cli provider only works where that CLI is authenticated. Interactive/desktop use is fine, but a headless cron/systemd daemon needs the CLI logged in in that environment or classification will fail — for always-on servers the API key (provider = "claude") is the robust choice.

Running locally (modest hardware is fine)

c0's core recall path — walk / search / add — needs almost nothing. The only moving parts are Neo4j and a single small embedding model (nomic-embed-text, ~140M parameters, well under 1 GB), and both run comfortably CPU-only. No GPU, no cloud. A few gigabytes of free RAM cover the graph, the embedder, and Neo4j's page cache for a personal-scale knowledge base.

The heavier work is optional and runs in the background. The reflection loop, concept extraction, and session enrichment use a chat LLM — and by default that's a local Ollama model (e.g. qwen2.5:7b/:14b for extraction/enrichment, hermes3:8b for classification), so the whole thing runs keyless and offline. Opt into Claude ([claude] enabled = true) only if you want its higher-quality haiku/sonnet judgment — via the Anthropic API or your Claude subscription's claude CLI ([Configuration](#configuration)). Because none of this is on the recall hot path, CPU inference is fine — a slow background tick never affects how fast walk feels.

| If you want… | You need | Notes | |---|---|---| | Core recall (walk / search / add) | CPU + ~2–4 GB free RAM | Neo4j + nomic-embed-text. No GPU. | | + run the loop's classifier on-device (no API key) | ~8 GB RAM for a 7B model | local qwen2.5:7b instead of the API; background, so CPU speed is fine | | Faster / higher-quality local LLM | a GPU (optional) | ~6 GB VRAM runs the embedder + a 7B model fast; 12–24 GB unlocks 14B–32B |

The short version: the embedding hot path is light enough for any laptop, and the only reason to add a GPU is to make the optional local LLM work faster — never to make c0 usable in the first place.

> On a slow CPU-only host, watch the enrichment timeout. Recall stays fast (embeddings are tiny), but enriching a large session can take minutes — and if a single call exceeds the per-request timeout (default 600 s) it fails with TimedOut. If you hit that: raise it with C0_ENRICH_TIMEOUT_SECS, do less per call (C0_ENRICH_MAX_CONCEPTS, C0_ENRICH_TEXT_BUDGET, or a smaller --limit), or use a faster/smaller model. And if you schedule several LLM jobs (enrich, extract, the reflector), serialize them — e.g. wrap each in a shared flock — so they don't dogpile Ollama's single-threaded queue, where a waiting request still burns its timeout.

> How enrichment picks what to read. Each session is enriched from a C0_ENRICH_TEXT_BUDGET-sized window (default 8,000 chars). Rather than the first N characters, c0 fills that budget with the most representative turns (ranked by similarity to the session's embedding centroid, boosting turns that ran tools and the opening turn), prefixed with a signal block of the files touched and commands run — where library and framework names show up most reliably. Set C0_ENRICH_FULL=1 to instead map-reduce over the entire session in budget-sized chunks and merge the concepts (full coverage, more LLM calls — costs scale with session length).

Using c0 with Claude Code

c0 pays off most when you treat the graph as where knowledge lives and keep your CLAUDE.md for protocol

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.