coalent

MCP serverDev tools

Coalent keeps your AI working from fresh facts pulled from your sources, with every fact attributed to where it came from. Cached facts are invalidated the moment a source changes, so answers stay current instead of out of date.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

After adding coalent, ask your AI questions that depend on facts from your sources. Check the attribution that comes back with each answer to confirm where the information originated.

What your AI can do with it

  • Pull fresh facts from your sources on demand
  • Attribute each fact to the source it came from
  • Reuse cached facts so repeat lookups don't re-fetch everything
  • Drop cached facts the moment a source changes
  • Answer from the latest version of your sources rather than an outdated copy

From the project's README

As published by vectorlink-labs/coalent in README.md.

Real-time, provenance-invalidated context for AI agents & RAG. Build understanding once. Reuse it everywhere. Keep it fresh — automatically.


Your agent re-reads the same sources on every call — and the moment a source changes, every cached answer is silently wrong.

Coalent builds the understanding once, caches it by what the query means, and invalidates it surgically the instant an underlying source changes. As correct as re-reading everything, at a fraction of the cost — and never stale.

Why Coalent

Every context layer is forced to trade off three things. Coalent is built to hold all three at once:

  • 🧠 Extractive understanding, not chunks. It caches a query-independent set of atomic, source-grounded claims your LLM extracted — keeping every number and fact — so one cached unit answers many different later questions. The raw evidence is retained with each unit, so a hit that under-covers a query falls back to retrieval instead of answering thin.
  • ♻️ Reuse across queries, agents — and documents. A semantic cache keyed by query meaning: ask again, or from another agent, and it's a warm hit. Cross-unit recall pools claims across units to answer multi-hop questions whose evidence spans documents — at zero extra LLM calls.
  • 🌿 Fresh by provenance. Every unit remembers the exact sources it used. When one changes, only the units that actually used it go stale — precisely, automatically, and lazily.

Coalent sits above retrieval — bring any retriever (vector DB, hybrid search, GraphRAG, tools, APIs). It's the freshness-and-reuse layer, not another retriever — deliberately the opposite of GraphRAG's build-the-whole-graph-upfront tax: lightweight, independent units, built lazily only when a query actually needs one, and refreshed by dirtying a single unit (no graph surgery).

New in v0.6 — the pool read path (read_path="pool"): every read serves the token-budgeted, globally ranked fresh-claim pool. Measured on a 605-question news benchmark (strict grading): 0.731 accuracy @ 981 context tokens — matching naive top-9 (0.711 @ 1,311) at ~25% fewer tokens, and naive's best measured point (top-12: 0.731 @ 1,729) at ~43% fewer. Plus a default-OFF behavioral stack — residual spans → refusal fallback → append-only repair → query keys — measured at −33% refusals and +3.1 pts on the same store. All opt-in; the default read path is unchanged v0.5 behavior. See What's new.

New in v0.6.1 — the MCP server: coalent-mcp puts the cache one line away from Claude Code, Cursor, or any MCP client (Use it from Claude Code / Cursor), and langchain-coalent makes your existing LangChain stack the cache's substrate. Both additive-only.

Install

pip install coalent          # the core has zero required dependencies

Quickstart

Runs as-is — StubSynthesizer needs no API key, so you can feel the loop in ten seconds:

from coalent import SemanticCache, InMemoryRetriever, StubSynthesizer

# 1. Any retriever — a vector DB, a tool, an API. (In-memory here for the demo.)
retriever = InMemoryRetriever()
retriever.add("confluence:hr", "Leave policy: 21 days of annual leave per year.")

# 2. Build the cache. Swap StubSynthesizer for a real LLM below.
cache = SemanticCache(retriever, StubSynthesizer())

# 3. Ask. The first call builds understanding and caches it; the next is a warm hit.
result = cache.get("what is our leave policy?")
print(result.context["understanding"])
print(result.cache_hit)        # False (cold) -> True on the next call

# 4. A source changed? Only the units that used it go stale — surgically.
cache.source_changed("confluence:hr", text="Leave policy: now 25 days.")
# the next matching read rebuilds just that one unit, lazily

Wire in a real model — any text-in / text-out LLM works. In v0.4 the synthesizer builds extractive understanding by default (query-independent atomic claims that keep every fact), and the cache does cross-unit recall — both on automatically:

from coalent import SemanticCache, LLMSynthesizer, OpenAIProvider, OpenAIEmbedder

cache = SemanticCache(
    retriever,
    LLMSynthesizer(OpenAIProvider(), model="gpt-4o-mini"),   # extract=True by default (v0.4)
    embedder=OpenAIEmbedder(),   # match queries by MEANING (recommended for real use)
)
# Multi-hop across documents? recall is already on; raise its trigger to bridge units:
#   SemanticCache(retriever, synth, embedder=..., recall_threshold=0.7)

The v0.6 pool read path — opt in, and every read serves the budget-packed, globally ranked fresh-claim pool instead of one routed unit. Attribution is the one thing to wire: a 3-line pool_header callable mapping each unit to [title | source | date] from your own corpus metadata. This is the measured golden path — on a 605-question news benchmark (strict grading), 0.68 accuracy with the bare built-in header vs 0.73 with this callable, same store, same queries:

DOC_META = {  # your corpus metadata, keyed by artifact id
    "docs:azure-refresh": {"title": "Azure region refresh", "source": "CloudWire", "date": "2026-05-02"},
}

def pool_header(unit) -> str:   # the [title | source | date] golden path — 3 lines
    meta = DOC_META.get(unit.evidence[0].artifact_id if unit.evidence else "")
    return f"[{meta['title']} | {meta['source']} | {meta['date']}]" if meta else f"[source: {unit.id}]"

cache = SemanticCache(retriever, synthesizer, embedder=OpenAIEmbedder(),
                      read_path="pool", pool_header=pool_header)
result = cache.get("which regions got the refresh?")
result.context["pool"]   # the packed, attributed claim payload — hand it to your answer model

Runnable no-API-key demo, including the refusal loop: examples/pool_read_path.py.

Use it from Claude Code / Cursor (MCP)

coalent-mcp serves fresh, attributed facts from a Coalent cache to any MCP client — and the facts are invalidated the instant their source changes. One line to wire it into Claude Code:

pip install "coalent[mcp,openai]"
claude mcp add coalent -- coalent-mcp --cache-factory my_cache:build

(Cursor / Claude Desktop / any MCP client: register the same coalent-mcp ... command in its MCP config.)

Bring your own cache (--cache-factory module:function) — the primary mode. Your factory returns a fully constructed SemanticCache: your vector DB, your embedder, your LLM, every knob. The server adds protocol glue only — and the glue is measured to add zero quality loss: factory mode reproduced the library's own benchmark result byte-identically (0.710 on a 100-question validation run drawn from our n=605 news benchmark — identical CIs, 100/100 serves, 98/100 answer payloads byte-equal to the library run).

# my_cache.py — importable from the directory you launch in
from coalent import (SemanticCache, LLMSynthesizer, OpenAIProvider,
                     OpenAIEmbedder, SQLiteCognitionStore)

def build() -> SemanticCache:
    return SemanticCache(
        my_vector_retriever,                  # YOUR vector DB / retriever
        LLMSynthesizer(OpenAIProvider()),     # YOUR synthesis model
        embedder=OpenAIEmbedder(),            # YOUR embedder
        read_path="pool",
        residual_spans=True, query_keys=True, # the behavioral stack, opt-in as ever
        pool_header=my_metadata_header,       # [title | source | date] — the measured golden path
        store=SQLiteCognitionStore("kb.db"),  # persistence is yours too
    )

Freshness here is signal-driven: your ingestion pipeline calls the source_changed tool when a document changes and the affected facts invalidate immediately. (Adding --watch DIR alongside the factory also fires it on file edits — invalidation only; it never ingests into your index, and it matches only when your artifact ids equal the watch-relative paths.)

Zero-config folder mode (--watch DIR) — the demo wedge. Point it at a folder of docs and you get the recommended v0.6 deployment (pool path, residual spans, query keys, SQLite persistence, automatic [path | modified date] attribution) with no code at all:

claude mcp add coalent --env OPENAI_API_KEY=$OPENAI_API_KEY -- coalent-mcp --watch ./docs

Every read rescans the watched files (mtime + content hash) before serving — you cannot get a stale answer after saving a file — and an untouched folder restarts fully warm. The honest number: on the same 100-question validation run, folder mode scored 0.46 vs 0.71 for a factory-built cache (40 vs 18 refusals) — the measured cost of the generic paragraph chunker and on-demand keyhole builds. Use it to feel the freshness loop in a minute; bring your own stack for production quality. One regime note: a question about a just-added file can honestly refuse from a warm cache until a read triggers that file's first build — a refusal, never a stale or wrong answer.

One shared cache for many agents (--transport http).

COALENT_MCP_TOKEN=<secret> coalent-mcp --cache-factory my_cache:build --transport http --port 8765

One long-lived process, many concurrent MCP clients, ONE shared cache — shared compounding, no store races (validated: two concurrent clients matched the sequential reference on all 20 reads, zero duplicate builds). When COALENT_MCP_TOKEN is set, every request must carry Authorization: Bearer <token> — bind localhost or trusted networks. Corollary for stdio: each stdio launch is its own process, so never point two apps at the same --store path — HTTP mode is the shared-cache answer.

The seven tools: get_context(query, budget?) → the attributed, budget-packed payload + a read_id · report_refusal(read_id) / report_success(read_id) → the behavioral repair loop over MCP · source_changed(artifact_id, text?) → the BYO freshness feed (unchanged content is hash-detected and skipped) · list_sources() · cache_stats() · refresh().

LangChain

langchain-coalent makes Coalent a LangChain-native freshness/reuse layer — BYO-first: your existing VectorStore (or retriever), embeddings, and chat model become the cache's substrate, unchanged.

pip install langchain-coalent
from langchain_coalent import create_coalent_cache, CoalentRetriever

cache = create_coalent_cache(my_vectorstore, llm=my_chat_model, embeddings=my_embeddings)
retriever = CoalentRetriever(cache=cache)      # drop-in LangChain BaseRetriever

docs = retriever.invoke("what is our leave policy?")
docs[0].page_content              # the served, attributed context payload
docs[0].metadata["read_id"]       # -> cache.report_refusal() / report_success()
docs[0].metadata["cache_hit"]     # True == served with zero LLM spend

cache.source_changed("policy.md", text=new_text)   # surgical, provenance-keyed invalidation

Every Coalent knob passes through create_coalent_cache; the refusal→repair loop ships as a runnable LangGraph-shaped example in the package. Depends only on coalent>=0.6 and langchain-core>=0.3.

What's new in v0.6

The pool-first release. Every n=605 number below comes from one frozen rig — a 609-article news corpus, 605 held-out questions, gpt-4.1-mini answerer, strict grading — the same rig the v0.5 numbers were measured on. Full details in the CHANGELOG and UPGRADE-0.5-to-0.6.md.

  • read_path="pool" — the claim-pool-first read path (opt-in). Reads are answered by budget-packing the global fresh-claim pool; units remain the ownership / freshness / provenance skeleton. Measured: 0.731 strict accuracy @ 981 mean context tokens — matching naive top-9 (0.711 @ 1,311) at ~25% fewer tokens and naive's best measured point (top-12: 0.731 @ 1,729) at ~43% fewer. The claim is parity at fewer tokens (CIs overlap) — not an accuracy beat. Gold-claim serving rank: p50/p75/p90 = 1/6/15 in pool order.
  • Attribution by default, and a measured header ladder. pool_header=None now renders a built-in per-source header. Same store, same queries: opaque id 0.641 → shipping default 0.678 → your [title | source | date] metadata callable 0.731. The gap is a unit-metadata limit (outlet/date live in your corpus, not on the unit) — wire the callable (quickstart above).
  • The behavioral stack (all default-OFF): spans → fallback → repair → keys. residual_spans=True captures fact-bearing sentences the extractor missed as tier-2 spans on the unit (never in the pool). When your answerer refuses, report_refusal(read_id) returns an attributed retry payload; report_success(read_id) confirms the rescue and — with query_keys=True — earns a durable alternate key; lossy-marked units self-repair append-only on their next rebuild. Measured, driven through the full loop: refusals 91 → 61 (−33%), +3.1 pts final accuracy at +2.1% tokens (same-store comparison), zero newly-wrong answers; keyed-class first-pass 0% → 61% on paraphrase revisits.
  • Adaptive serve gate (serve_gate=None) — adapts against the pool's own noise ceiling; an explicit float disables adaptation (reproducible benches). Shipped only after a $0 replay gate: 605/605 identical serve decisions on both arms, zero builds, zero LLM calls.
  • Hardening & plumbing: every payload surface carries source attribution (pool payload and escalation raw); cross-owner near-duplicate claims are kept as corroboration; 14 new observability events (pool_served, residual_fallback, key_confirmed, ...); reranker hook (serving order only — it can never cause a false serve); claim_index BYO pool storage; a v0.5 pool-preview stale-serve hole is fixed.
  • Deprecated: serve="pool" (the v0.5 preview) — still works verbatim in 0.6, removed in v0.7; migrate to read_path="pool". The default read path stays "unit" (exact v0.5 behavior); flipping the default is a v0.7 decision behind five pre-registered gates, of which only one (the replay gate) has passed.

Honest limits (measured, not hypothetical)

  • The refusal fallback flips ~20% of natural refusals (33% when the payload contains the answer verbatim) — the 68% figure from lab questions holds only where the payload contains the answer by construction. It is a net over the extraction tail, not a second retriever.
  • Query keys can collide across sibling articles in dense same-topic corpora (observed 3/605 reads; answers still correct). Raise key_floor above its 0.85 default there. Keys convert refusal round-trips into first-pass answers; they do not raise final accuracy on diverse rewordings.
  • The shipping default header can only attribute what the unit knows — the 0.678 → 0.731 gap is a unit-metadata limit, closed today by the pool_header callable; an optional ingest-time metadata field is a v0.7 item.

What's new in v0.5

The pool release — everything a month-long, pre-registered benchmark war on real news data (MultiHopRAG, 609 articles, third-party questions) taught us, shipped as opt-in features:

  • preset="multi_hop" — one argument arms cross-unit recall + the hop-2 bridge with calibrated thresholds. Explicit kwargs always win.
  • Source widening (widen_chunks=24) — a miss-triggered build reads up to N chunks of the dominant source instead of only the retrieved keyhole. Effect in E2E: rebuild churn 460 → 31, warm-pass accuracy flipped from decaying to compounding. Never fires at ingest.
  • Provenance admission (provenance_admission=True) — an exact-text containment probe prevents duplicate understanding: covered reads serve without building; thin coverage widen-rebuilds in place.
  • Adaptive hit gate (adaptive_hit=True) — self-calibrates against score inflation as the cache grows (fixed thresholds provably absorb everything at scale).
  • Pool serving preview (serve="pool", serve_budget=600, pool_header=...) — serve the token-budgeted, globally ranked fresh-claim pool instead of one routed unit (experimental; superseded by read_path="pool" in v0.6 — the preview still works in 0.6, removed in v0.7). Held-out n=605: 0.699 accuracy vs 0.579 for unit serving (z=6.66); statistically ties naive's k9 arm — its best measured at the time — at 0.79× its tokens; 95% null honesty. Stale units' claims are masked from the pool the moment a source changes.
  • fast="auto" — numpy-accelerated read path when numpy is present (pip install "coalent[fast]"); results are equivalence-pinned to the pure-Python core.
  • Observability (on_event=...) — structured freshness events: builds, rebuilds, admission reuse, stale reads prevented, recall and bridge activity.
  • Deprecated: select_floor (superseded by pool serving).

Full numbers and method in the benchmark section and CHANGELOG.

What's new in v0.4

Two capabilities that were an opt-in preview are now the defaults, because they're strictly better on the structured / reuse-heavy corpora Coalent targets — and free or dormant everywhere else. Both have a one-line escape hatch back to exact v0.3 (extract=False, cross_unit_recall=False).

  • 🎯 Extractive understanding (extract=True, default). Instead of a question-shaped prose summary, the synthesizer extracts a query-independent list of atomic, source-grounded claims. The same unit now answers many different later questions, and no number is dropped — a prose summary silently lost ~40% of the numbers in a source in our tests.
  • 🔗 Cross-unit claim recall (cross_unit_recall=True, default). When one unit under-covers a query, the cache pools per-claim memory across all fresh units (MaxSim) and surfaces the bridge facts — answering multi-hop questions naive retrieval structurally can't (evidence in a document that doesn't resemble the question), at zero extra LLM calls. Dormant/free on single-hop; auto-off under a non-semantic embedder. Surfaced as result.recalled.
  • 🛡️ Precision & serving knobs (opt-in, default off): hit_margin (refuse ambiguous ties), select_floor (serve atoms by meaning, fewer tokens), residual_floor (recover extractor-missed number spans). See the gate ladder for when to reach for each.

Upgrading from v0.3? See UPGRADE-0.3-to-0.4.md — additive, one behaviour change (understanding is now claims, not prose).

How it works

        query ──► embed ──► semantic cache
                               │  hit & fresh?  ──► serve cached understanding  (no retrieval, no LLM)
                               │  miss / stale? ─┐
                               ▼                 ▼
                          your Retriever ──► your Synthesizer ──► Cognition unit
                          (vector/tool/API)   (LLM or passthrough)  { understanding
                               ▲                                      + raw evidence
                               │                                      + provenance }
   source changed ────────────┘   dirties ONLY the units that used that source
  1. Embed the query and look for an existing unit with similar meaning.
  2. Hit + fresh → return the cached understanding (no retrieval, no LLM call).
  3. Miss or stale → retrieve, synthesize understanding, retain the raw evidence, record provenance (the exact sources used), and cache it.
  4. A source changessource_changed(id) marks only the units whose provenance includes that id; they rebuild lazily on the next read.

Unchanged content is skipped via a content-hash compare, so a no-op change costs nothing.

The read path — a ladder of gates

This is the default unit read path (read_path="unit", exact v0.5 behavior). The opt-in v0.6 pool path replaces unit routing with global claim-pool packing and makes these unit-routing knobs inert; its own knobs are in UPGRADE-0.5-to-0.6.md.

Coalent keys on what a unit knows — an embedding of its understanding, not the query's words — so "how many vacation days?" hits your leave unit, while "exchange policy" does not. Every get(query) then walks a fixed ladder of gates. The defaults are pure cosine — no extra model, no heavy dependency — and each gate is a tunable knob. In firing order:

#GateDefaultFires when → what happens
1hit_threshold — matchauto (OpenAI ~0.33)best unit's blended score (0.7·topic + 0.3·seed) below it → miss → retrieve + synthesize a new unit
2hit_margin — precision guard0.0 (off)top unit beats runner-up by less than the margin → ambiguous → build the query's own unit instead
3freshnessprovenance / TTLmatched unit dirty or expired → re-materialize it
4coverage — does it answer?max per-claim cosinehow well the matched unit covers this query (one perfect claim = covered)
5cross_unit_recallon (v0.4)coverage < recall_threshold → pool the best claims across all fresh units (MaxSim), can lift coverage. Free when dormant, no LLM call
6coverage_scorer (S2)None (off)in the ambiguous band [coverage_floor, coverage_ceiling) → a cross-encoder / NLI / LLM entailment check overrides cosine
7coverage_floor — the RAG floorauto (~0.28)coverage still below it → escalate: append fresh raw retrieval (no LLM call), so a thin hit falls back to retrieval rather than answering wrong
8select_floor — serveNone (lexical trim)serve the unit's atoms by meaning (per-claim cosine ≥ floor) instead of a keyword trim — the query-relevant facts, fewer tokens

Plus one build-time knob — residual_floor: retain number-bearing source spans the extractor dropped (best per-claim cosine < floor) as extra atoms. Embedding-only. Other hooks: route_by_claim (late-interaction routing over a fat unit's claims), relevance_gate (BYO reranker before synthesis), depth (synthesis completeness vs cost), calibrate_thresholds() / suggest_thresholds().

Which knob for which workload — the defaults are tuned for structured, single-hop reuse; reach for these when your data differs:

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
12
Forks
1
Last commit
Aug 2026

ahel review

  • S4low
    published under nisarg's namespace; repository belongs to vectorlink-labs

Automated review, not a security audit. Ruleset v1.

Advanced
Delivery
coalent MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-nisarg-pujara-vectorlink-coalent
Source
github.com/vectorlink-labs/coalent