Skip to main content
Glama

chrysalis-bridge

One MCP tool that gives a frozen, small, local LLM graph memory, continual learning outside the weights, SMT reasoning, and evidence-based advice on when to use them.

Python 3.14 MCP Z3 sqlite-vec NumPy llama.cpp Gemma 4 pytest ruff uv License: Apache 2.0

NOTE

This is a research prototype with preliminary evidence. Every number below comes from small experiments on a single model; seeLimitations before drawing conclusions.

Table of contents

  1. What it is and why

  2. Key ideas

  3. Architecture

  4. The single tool contract

  5. Quickstart

  6. Validation and results

  7. Limitations

  8. Related work and what is different

  9. Roadmap

  10. Project layout

  11. Contributing

  12. License

  13. Acknowledgements

Related MCP server: mcp-engram

What it is and why

Small local models (here Gemma 4 E4B served by llama.cpp) are cheap, private and fast, but they forget everything between sessions, have a short window, and are unreliable at multi-constraint logic. Retraining or fine-tuning them for each of those gaps is expensive and, for a user running on a laptop or an iGPU, usually out of reach.

chrysalis-bridge augments a frozen model from the outside, over MCP, without retraining:

  • Memory that survives sessions and scales beyond the context window.

  • Learning that happens in an external store, not in the weights.

  • Reasoning delegated to an exact solver (Z3) for problems the model gets wrong.

  • Advice about when each capability helps or hurts that particular model on that particular task, derived from measured results and not from intuition.

The package is called chrysalis-mcp; the repository is chrysalis-bridge.

Key ideas

Idea

What it means here

Graph memory (HippoRAG-style PPR)

Knowledge goes in as triples. Recall seeds a Personalized PageRank walk over the graph, so multi-hop questions reach facts that a nearest-neighbour lookup misses. Dense vectors live in sqlite-vec.

Fly-inspired continual learning outside the weights

A FlyHash sparse code (random expansion plus winner-take-all) indexes nodes; memory.update strengthens associations Hebbian-style in the store. Old associations are not overwritten by new ones. FlyHash retrieval itself is experimental, see the results.

Z3 reasoning via a JSON DSL

reason.solve and reason.check take either SMT-LIB 2 or a safe JSON constraint DSL (never eval), run in an isolated worker process with a timeout.

Model-aware, task-conditioned, evidence-based advisories

The server holds evidence.json: rules that cite a measurement, keyed by model class, thinking mode and task features. It turns them into a plain-language hint and delivers it through the system prompt and the tool description.

Anti-chatty single-tool design

One tool, chrysalis(domain, action, ...), with self-discovery, server-side batching and graded verbosity, so the model spends its few tokens on the task instead of on a large tool list.

Architecture

Components

flowchart LR
    CLIENT["MCP client / LLM"] -->|"stdio"| SERVER["server.py: tool chrysalis"]
    SERVER --> REG["core/registry: domain and action dispatch"]
    SERVER --> ENV["core/envelope: typed response"]
    REG --> META["domain meta"]
    REG --> MEM["domain memory"]
    REG --> REASON["domain reason"]
    META --> ADV["core/advisory and profile"]
    META --> BATCH["core/batch and session cache"]
    ADV --> EVID[("data/evidence.json")]
    MEM --> SUB["substrate: SQLite and sqlite-vec"]
    MEM --> ALG["algorithms: FlyHash and PPR"]
    REASON --> DSL["constraint_dsl"]
    REASON --> Z3W["isolated Z3 worker process"]

Session flow

sequenceDiagram
    participant C as Client
    participant S as chrysalis server
    participant A as Advisor
    C->>S: initialize
    S-->>C: instructions (startup-profile hint) and tool description
    C->>S: chrysalis meta.handshake (model, params_b, thinking), session_id
    S->>A: resolve profile and rules
    A-->>S: plain-language hints
    S-->>C: profile and prompt_hint
    Note over C: client pastes prompt_hint into the system prompt
    C->>S: chrysalis meta.batch (steps with $cache refs, return_only)
    S->>S: run steps server-side, cache intermediate results
    S-->>C: projected result in a typed envelope

Advisory decision

flowchart TD
    P["Model profile: class, thinking flag, forced or declared or calibrated"] --> M["Match rules"]
    T["Task features: n_vars, n_atoms, goal, facts_exceed_ctx"] --> M
    E[("evidence.json: each rule cites a measurement")] --> M
    M --> L{"Level"}
    L -->|"recommended"| H["Plain-language hint"]
    L -->|"discouraged"| H
    L -->|"experimental"| H
    L -->|"neutral"| N["No hint"]
    H --> SP["System prompt via MCP instructions and prompt_hint"]
    H --> TD["Tool description line"]
    H --> AN["advisory field on the call response"]

The single tool contract

The server exposes exactly one MCP tool:

chrysalis(domain, action, params={}, verbosity="summary",
          session_id=None, store_as=None, telemetry=None, agent_id=None)

Call meta.schema for the full machine-readable contract, or meta.discover for a compact map.

Domain

Action

Purpose

meta

schema

Full contract: every domain, action, params and an example.

meta

discover

Compact orientation, optionally for one domain.

meta

handshake

Declare the calling model (model, family, params_b, active_params_b, context, thinking, client); returns prompt_hint.

meta

calibrate

Session-specific probes checked by the server; measured profile overrides a declared one.

meta

advise

Advisory level of a domain/action for a given task and the session model.

meta

stats

Calls, errors, bytes, est_tokens and time, grouped by action or agent_id.

meta

batch

Chain up to 25 steps server-side.

memory

ingest

Add knowledge triples (nodes, edges, FlyHash code, dense vector).

memory

recall

Associative recall: mode ppr (default) or seeds. ppr includes the learned boost (lambda, on a [0,1] scale via BOOST_SCALE); for learned nodes it also returns learned_as facts and a learned flag.

memory

update

Reinforce the association between a query and a node; stores up to 3 teach examples per node.

memory

forget

Soft-invalidate the edges of a source.

memory

consolidate

Decay learned associations and prune weak ones; drops orphan examples.

memory

restore

List or restore verified rotating backups taken before forget/consolidate.

reason

solve

Solve an SMT-LIB 2 problem (smt2) or a JSON constraint problem (constraints) with Z3.

reason

check

Check whether an assignment satisfies the problem.

Envelope. Every response is one typed object with status, severity (INFO, WARN, ERROR), summary, next_action, truncated, cursor, and a telemetry block (request_id, ms, bytes, est_tokens). Oversized payloads are clamped; the telemetry block is omitted in brief and terse unless requested.

Verbosity. detail, summary (default), brief, terse. Lower levels cap list and string sizes; error text and advisory hints are never truncated.

Batch with $ref and return_only. Intermediate results stay on the server:

{
  "domain": "meta", "action": "batch", "session_id": "run-1",
  "params": {
    "steps": [
      {"id": "a", "domain": "memory", "action": "recall",
       "params": {"query": "who owns the lab?", "k": 3}, "store_as": "hits"},
      {"id": "b", "domain": "meta", "action": "stats", "params": {"scope": "all"}}
    ],
    "return_only": "last",
    "select_fields": ["status"]
  }
}

A step can reference an earlier result as $cache.<key>.<field> (the key is a step id or its store_as). return_only is all, last or summary; select_fields projects dot-paths. A batch stops at the first error unless continue_on_error is set, and nested batches are rejected.

session_id. Scopes the in-process result cache (store_as, $cache), the model profile set by handshake/calibrate, and per-session stats. State is in memory and per process.

Quickstart

Requires Python 3.14 and uv.

git clone <this repository>
cd chrysalis-bridge
uv sync
uv run chrysalis-mcp          # stdio MCP server

MCP client configuration (stdio):

{
  "mcpServers": {
    "chrysalis": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/chrysalis-bridge", "chrysalis-mcp"],
      "env": {
        "CHRYSALIS_DB": "/path/to/chrysalis/substrate.db"
      }
    }
  }
}

Environment variables:

Variable

Effect

CHRYSALIS_DB

SQLite path. Default $XDG_DATA_HOME/chrysalis/substrate.db, else ~/.local/share/chrysalis/substrate.db.

CHRYSALIS_MODEL_PROFILE

Force the model profile for every session (overrides any handshake).

CHRYSALIS_ENFORCE_ADVISORY

1 makes a discouraged advisory block the call unless params.override is true. Default: advisory only.

Example handshake (first call of a session):

{
  "domain": "meta", "action": "handshake", "session_id": "run-1",
  "params": {"model": "gemma-4-e4b", "family": "gemma", "params_b": 4, "thinking": false}
}

The response includes a prompt_hint: paste it into the system prompt of the model.

Development:

uv run pytest
uv run ruff check . && uv run ruff format --check .

Validation and results

Results are generated from validation/results/*.json by python -m validation.report. They are reported as measured, including the ones that did not support the hypothesis.

Hypothesis

Verdict

Result

H-b: continual learning does not forget (synthetic)

Supported

Accuracy on old associations after 1000 new updates: retention 1.00 with the sparse store vs 0.00 with an overwrite baseline.

H-a: two-hop retrieval

Supported

recall@5 0.45 (PPR) vs 0.40 (seeds); one call instead of two.

H-b-e2e: learned aliases, model in the loop

Supported

Gemma 4 E4B, 12 learned invented aliases, 300 interference updates. fly_update 0.75 vs no_learn 0.0 in both thinking modes. The classic alias-ingest baseline scores 0.83 (thinking off) and 1.0 (thinking on). Retrieval recall@5 0.967, retention 1.0 after interference.

H-a: end to end

Supported

Accuracy 0.0 alone vs 0.6 with memory.

H-a-ctx: memory vs full context

Not supported

0.6 with memory vs 0.9 with the full context when the facts fit in the window.

H-a-scale: facts beyond the window

Supported

0.35 with memory vs 0.0 with context (+0.35).

H-a': FlyHash vs exact cosine

Not supported

At N=1000, FlyHash cluster precision is worse than cosine (about 0.54 x cosine at the shipped setting, best swept 0.69 x). FlyHash retrieval is therefore experimental.

H-c: solver on small puzzles

Not supported

0.8 alone vs 0.6 with the solver (thinking off).

H-c-hard: solver on hard puzzles

Supported

+0.12 accuracy with the solver in both thinking modes (off 0.18 to 0.29; on 0.82 to 0.94).

H-adv held-out, thinking on

Supported

Advised arm 16/16 vs best fixed arm 15/16 (n=16).

H-adv held-out, thinking off

Not supported

Advised 0.25 vs best fixed 0.375; the model rarely acts on the hint with thinking off.

H-mcp: batching cost

Supported

Estimated tokens: batch 921, sequential 2127, granular tools 7977 (granular is simulated).

Additional finding: an unconditional "reply with ONLY the JSON object" reminder in the prompt suppressed tool calls (solver use fell to 0 in the ablation), so the project only appends a format reminder conditionally.

Fixes found by the evaluation

The validation loop found two real defects in the learning path, both fixed:

  • Boost leak. The learned boost was unbounded, so unrelated updates destroyed recall (1.0 to 0.06 after 10 updates). Fixed by normalizing the boost to the KC overlap fraction (BOOST_SCALE).

  • Invisible learned associations. The first LLM run scored fly 0/12 because the model never saw why a node was recalled. Fixed with learned_as provenance facts; the result became 9/12.

Limitations

  • Small n. Held-out sets have 16 items; hard sets 17. Differences of one or two items are within noise and no statistical significance is claimed.

  • A single model. All live results use Gemma 4 E4B. Nothing here shows the effects transfer.

  • Synthetic memory benchmarks. H-a, H-a', H-b and H-a-scale use generated fictional facts.

  • Thinking off is not supported. The advisory path works with thinking on and not yet with it off.

  • Tool-call parsing. Gemma 4 tool calls are sometimes returned as plain content by llama.cpp (issue 22786); the harness has to tolerate this.

  • Common-word aliases. On realistic aliases such as "the big apple", false learned_as facts appear 17-58% of the time.

  • Interference. Recall degrades at 300 high-overlap interference updates.

  • Snapshots. Substrate v4 cannot restore v3 snapshots.

  • Advisories are self-referential. The evidence rules come from the same benchmarks that report them.

Reproduce

uv run pytest                                   # unit and contract tests (no model needed)
uv run pytest -m bench                          # offline benchmarks; write validation/results
python -m validation.eval_llm --hyp hc,hard,adv --thinking both    # needs a live OpenAI-compatible model
python -m validation.report                     # merge results into a verdict table

eval_llm talks to an OpenAI-compatible endpoint at CHRYSALIS_LLM_URL. For local runs, validation/llm_server.py --profile e4b is a resource-guarded launcher for llama.cpp (admission control, RAM caps, watchdog); read its docstring first, since it manages a GPU-backed process. Hypothesis groups: ha, ctx, hc, hard, adv, cal, hascale, heldout, hbe2e.

  • HippoRAG 2 (arXiv:2502.14802): graph-based non-parametric continual learning with Personalized PageRank. The recall path here follows the same idea at a much smaller scale; it is not a reproduction and has not been compared on their benchmarks.

  • FlyHash (Dasgupta et al., Science 2017, doi:10.1126/science.aam9868): a fly-olfaction-inspired similarity-search code. In this project's tests it underperforms exact cosine.

  • Larimar (arXiv:2403.11901): episodic memory for LLMs with fast updates. Different mechanism: here the memory is an external SQLite graph.

  • Logic-LM (arXiv:2305.12295): LLM plus symbolic solver for logical reasoning. reason is in the same family, exposed as an MCP action.

  • SMART (arXiv:2502.11435) and When2Tool (arXiv:2605.09252): work on when an agent should or should not call a tool. Closest to the advisory idea; they are about the agent's own decision.

  • Dispatch-level instrumentation (arXiv:2608.28439): tool-dispatch measurement for agent pipelines; related to the envelope telemetry only loosely.

  • Code Mode (Cloudflare) and code execution with MCP (Anthropic): motivation for keeping intermediate results out of the model context. meta.batch is a restricted, declarative analogue, not general code execution.

Plausibly novel (not established): server-side, training-free, model-aware and task-conditioned tool advisories, learned from evals and delivered through the system prompt. The evidence for this is preliminary (one model, small n, one positive held-out result with thinking on) and no exhaustive literature search was done, so treat the claim as a hypothesis.

Roadmap

  • A better learned_as gate (global df, stoplist, coverage; boost share from the query's own KCs) and a realistic common-word alias dataset.

  • More tasks per hypothesis, with confidence intervals.

  • Small model families: Gemma 4 E4B plus a small Qwen. No large models (local hardware) and no Gemma 3.

  • Rival baselines on the original benchmarks: SMART, When2Tool, HippoRAG 2, Logic-LM.

  • A better answer for thinking off, where the hint is currently not acted upon.

Project layout

chrysalis-bridge/
├── src/chrysalis_mcp/
│   ├── server.py            # FastMCP app, the single tool, stdio entry point
│   ├── core/                # registry, envelope, batch, session, profile, advisory, task_features
│   ├── domains/             # meta, memory, reason, constraint_dsl, isolated Z3 worker
│   ├── algorithms/          # FlyHash, Personalized PageRank
│   ├── substrate/           # SQLite store, read/write paths, rotating backups, sqlite-vec
│   └── data/evidence.json   # advisory rules, each citing a measurement
├── tests/                   # unit, contract and stdio end-to-end tests
├── validation/              # benchmarks, LLM harness, guarded llama.cpp launcher, results/*.json
├── pyproject.toml
├── uv.lock
├── LICENSE
└── NOTICE

Contributing

Issues and pull requests are welcome. Before opening a PR, run uv run pytest and uv run ruff check . && uv run ruff format --check .. Keep the single-tool contract: new capabilities are new domain.action registrations (the schema is generated from the registry), not new MCP tools. Claims about behaviour should come with a result under validation/.

License

Apache License 2.0. See LICENSE and NOTICE.

Runtime dependencies (versions from uv.lock; licenses from package metadata, all compatible with Apache-2.0 distribution):

Package

License

mcp 1.30

MIT

numpy 2.5

BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0

sqlite-vec 0.1.9

MIT or Apache-2.0 (dual)

z3-solver 5.3

MIT

pytest 9.2 (dev)

MIT

ruff 0.17 (dev)

MIT

Model weights are not distributed. Gemma weights are used only if you download them yourself, and your use is governed by the Gemma Terms of Use, not by this repository's license. Likewise llama.cpp is a separate project under its own license.

Acknowledgements

  • Several infrastructure patterns (typed envelope, domain/action dispatcher, $cache batching, session cache, rotating verified backups, version stamp, tier-to-advisory precedence) were reverse-engineered, not imported, from the author's agent-execution-plane (Apache-2.0); see NOTICE.

  • Inspiration: HippoRAG 2, the FlyHash work of Dasgupta et al., Logic-LM, and the Code Mode and code-execution-with-MCP write-ups cited above.

  • Built on the Model Context Protocol, Z3, sqlite-vec, NumPy and llama.cpp.

Available Tools

1 tool
chrysalisC

Single dispatcher: chrysalis(domain, action, params). Call domain='meta', action='schema' for the full contract, or action='discover' for a compact map. Chain steps in one call with meta.batch. verbosity: detail|summary|brief|terse. Advisory memory.recall: rec:facts_exceed_ctx=true. In words: worth using when the facts do not fit in your context window.

Inputs: action (required), domain (required), params, agent_id, +4 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.7/5.0

    Scored across 1 tool

    Disambiguation3/5

    With a single tool there is no inter-tool overlap, so classic misselection risk is eliminated. However, the ambiguity is pushed inward: the domain/action/params space is opaque from the tool listing, so an agent cannot tell what operations exist or which action to pick without first making an exploratory meta.schema or meta.discover call.

    Naming Consistency3/5

    The lone tool name 'chrysalis' matches the server and is not inconsistent with anything, but it is an opaque noun with no verb_noun convention to establish predictability. The actual naming surface (domain and action identifiers) is not visible here, so consistency cannot be verified from the tool set.

    Tool Count3/5

    One tool is intentionally thin as a dispatcher pattern, which is a legitimate design, but by the rubric a single entry point is borderline and forces a discovery round-trip before any real work. It is not a trivial tool, so it avoids the worst score, but the surface is not self-evident from the count alone.

    Completeness3/5

    The tool advertises schema discovery, batching, verbosity levels, and advisory memory, which suggests a broad internal surface, but none of the actual domains or actions are enumerated so coverage cannot be assessed. Completeness is deferred to a runtime meta.schema call, leaving a potential dead end if discovery fails or is skipped.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Long-term memory for AI agents over MCP — episodic + semantic memory, a temporal knowledge graph, and a dialectic user model, exposed as 32 tools (recall, remember, context, graph, dreaming, peers). Zero dependencies, runs fully offline; leads the LoCoMo benchmark at ~35x fewer LLM calls.
      2
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables LLMs to build and explore a cognitive neuroscience-inspired knowledge graph with SQLite, supporting search, graph traversal, temporal sequences, and structured reasoning.
      24
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Provides persistent memory for AI tools by building a local knowledge graph from conversations, enabling cross-session recall and context awareness without cloud dependencies.
      9
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      Provides LLM agents with hallucination-free external memory and knowledge tools, including ask, remember, forget, math, and code reasoning, with deterministic, traceable outputs.
      MIT