MentisDB is a durable semantic memory engine and versioned skill registry for AI agents — a persistent, hash-chained brain that survives context resets, model swaps, and team turnover.
It stores semantically typed thoughts in an append-only, hash-chained memory log through a swappable storage adapter layer. The skill registry is a git-like immutable version store for agent instruction bundles — every upload is a new version, history is never overwritten, and every version is cryptographically signable.
Harness Swapping — the same durable memory works across every AI coding environment. Connect Claude Code, OpenAI Codex, GitHub Copilot CLI, Qwen Code, Cursor, VS Code, or any MCP-capable host to the same mentisdbd daemon and your agents share one brain, regardless of which tool you picked up today.
Zero Knowledge Loss Across Context Boundaries — when an agent's context window fills, it writes a Summary checkpoint to MentisDB, compacts, reloads mentisdb_recent_context, and continues without losing a single decision. Chat history is ephemeral. MentisDB is permanent.
Fleet Orchestration at Scale — one project manager agent decomposes work, dispatches a parallel fleet of specialists, each pre-warmed with shared memory, and synthesizes results wave by wave. MentisDB is the coordination substrate: every agent reads from the same chain and writes its lessons back. The fleet's collective intelligence compounds.
Versioned Skill Registry — skills are not just stored, they are versioned like a git repository. Every upload to an existing skill_id creates a new immutable version (stored as a unified diff). Any historical version is reconstructable. Skills can be deprecated or revoked while full audit history is preserved. Uploading agents with registered Ed25519 keys must cryptographically sign their uploads — provenance is verifiable, not assumed.
Session Resurrection — any agent can call mentisdb_recent_context and immediately know exactly where the project stands, what decisions were made, what traps were already hit, and what comes next — without re-reading code, re-running exploratory searches, or asking the human to re-explain context that was earned through hours of work.
Self-Improving Agent Fleets — agents upload updated skill files after learning something new. A skill checked in at the start of a project is better by the end of it. Combine with Ed25519 signing to create a verifiable, tamper-evident record of which agent authored which version of institutional knowledge.
Multi-Agent Shared Brain — multiple agents, multiple roles, multiple owners can write to the same chain key simultaneously. Every thought carries a stable agent_id. Queries filter by agent identity, thought type, role, tags, concepts, importance, and time windows. The chain represents the full collective intelligence of an entire orchestration system, not just one session.
Lessons That Outlive Models — architectural decisions, hard constraints, non-obvious failure modes, and retrospectives written to MentisDB survive chat loss, model upgrades, and team changes. The knowledge compounds instead of evaporating. A new engineer or a new agent boots up, loads the chain, and inherits everything the team learned.
Install the daemon:
cargo install mentisdbConnect your local AI tools the fast way:
mentisdbd wizardOr target one integration explicitly:
mentisdbd setup codex
mentisdbd setup all --dry-run
mentisdbd wizard
mentisdbd add "The sky is blue"
mentisdbd search "cache invalidation" --limit 5 --scope session
mentisdbd agentsThen start the daemon:
mentisdbdOn an interactive first run with no configured client integrations,
mentisdbd offers to launch the setup wizard immediately after startup so you
do not have to guess the next command.
Run persistently after closing your SSH session:
nohup mentisdbd &Modern MCP clients bootstrap themselves from the MCP handshake:
initialize.instructionstells the agent to readmentisdb://skill/coreresources/read(mentisdb://skill/core)delivers the embedded operating skillGET /mentisdb_skill_mdremains available only as a compatibility fallback
If you need to wire a tool manually, here are the raw MCP commands/configs:
# Claude Code
claude mcp add --transport http mentisdb http://127.0.0.1:9471
# OpenAI Codex
codex mcp add mentisdb --url http://127.0.0.1:9471
# Qwen Code
qwen mcp add --transport http mentisdb http://127.0.0.1:9471
# GitHub Copilot CLI — use /mcp add in interactive mode,
# or write ~/.copilot/mcp-config.json manually (see below)mentisdb/ contains:
- the standalone
mentisdblibrary crate - server support for HTTP MCP and REST, enabled by default
- the
mentisdbddaemon binary - dedicated tests under
mentisdb/tests
A Makefile is included at the repository root. All common workflows have a target:
make build # fmt + release build
make build-mentisdbd # build only the daemon binary
make release # fmt, check, clippy, build, test, doc in sequence
make fmt # cargo fmt
make check # cargo check (lib + binary)
make clippy # cargo fmt + clippy --all-targets -D warnings
make test # cargo test
make bench # Criterion benchmarks, output tee'd to /tmp/mentisdb_bench_results.txt
make doc # cargo doc --all-features
make install # cargo install --path . --locked
make publish # cargo publish
make publish-dry-run
make clean
make help # list all targets with descriptionsmake buildOr directly with Cargo:
cargo build --releaseBuild only the library without the default daemon/server stack:
cargo build --no-default-featuresmake testOr directly:
cargo testRun tests for the library-only build:
cargo test --no-default-featuresRun rustdoc tests:
cargo test --docMentisDB ships a Criterion benchmark suite and a harness-free HTTP concurrency benchmark:
make benchOr directly:
cargo benchResults are also written to /tmp/mentisdb_bench_results.txt so numbers persist across terminal sessions.
Benchmark coverage:
benches/thought_chain.rs— 10 benchmarks: append throughput, query latency, traversal patternsbenches/search_baseline.rs— 4 benchmarks: lexical/filter-first search baseline over content, registry text, indexed+text intersections, and newest-tail limitsbenches/search_ranked.rs— 4 benchmarks: additive ranked retrieval over lexical content, filtered ranked queries, and heuristic fallback, plus a baseline append-order comparisonbenches/skill_registry.rs— 12 benchmarks: skill upload, search, delta reconstruction, lifecyclebenches/http_concurrency.rs— startsmentisdbdin-process on a random port; measures write and read throughput at 100 / 1k / 10k concurrent Tokio tasks with p50/p95/p99 latency reporting
Baseline numbers from the DashMap concurrent chain lookup refactor: 750–930 read req/s at 10k concurrent tasks, compared to a sequential bottleneck on the previous RwLock<HashMap> implementation.
make docOr directly:
cargo doc --no-depsGenerate docs for the library-only build:
cargo doc --no-deps --no-default-featuresThe standalone executable is mentisdbd.
Run it from source:
cargo run --bin mentisdbdInstall it from the crate directory:
make install
# or
cargo install --path . --lockedmentisdbd now owns both daemon startup and local integration setup:
mentisdbd setup codex
mentisdbd setup all --dry-run
mentisdbd wizard
mentisdbd add "The sky is blue"
mentisdbd search "cache invalidation" --limit 5 --scope session
mentisdbd agents
mentisdbdWhen it starts, it serves:
- an MCP server
- a REST server
- an HTTPS web dashboard
Before serving traffic, it:
- migrates or reconciles discovered chains to the current schema and default storage adapter
- verifies chain integrity and attempts repair from valid local sources when possible
- migrates the skill registry from V1 to V2 format if needed (idempotent; safe to run repeatedly)
- migrates chain relations from V2 to V3 format to add temporal edge validity (
valid_at/invalid_at) if needed (idempotent; safe to run repeatedly)
Once startup completes, it prints:
- the active chain directory, default chain key, and bound MCP/REST/dashboard addresses
- a catalog of all exposed HTTP endpoints with one-line descriptions
- a per-chain summary with version, adapter, thought count, and per-agent counts
mentisdbd is configured with environment variables:
MENTISDB_DIRDirectory where MentisDB storage adapters store chain files.MENTISDB_DEFAULT_CHAIN_KEYDefaultchain_keyused when requests omit one. Default:borganism-brain.MENTISDB_DEFAULT_KEYis accepted as a deprecated alias.MENTISDB_STORAGE_ADAPTERDefault storage backend for newly created chains. Onlybinaryis supported for new chains (JSONL is deprecated — existing JSONL chains remain readable). Default:binaryMENTISDB_VERBOSEWhen unset, verbose interaction logging defaults totrue. Supported explicit values:1,0,true,false.MENTISDB_LOG_FILEOptional path for interaction logs. When set, MentisDB writes interaction logs to that file even if console verbosity is disabled. IfMENTISDB_VERBOSE=true, the same lines are also mirrored to the console logger.MENTISDB_BIND_HOSTBind host for both HTTP servers. Default:127.0.0.1MENTISDB_MCP_PORTMCP server port. Default:9471MENTISDB_REST_PORTREST server port. Default:9472MENTISDB_DASHBOARD_PORTHTTPS dashboard port. Default:9475. Set to0to disable the web dashboard.MENTISDB_DASHBOARD_PINOptional PIN required to access the dashboard. Leave unset only for trusted localhost use.MENTISDB_AUTO_FLUSHControls per-write durability of thebinarystorage adapter.true(default): everyappend_thoughtflushes to disk immediately. Full durability.false: writes are batched and flushed every 16 appends (FLUSH_THRESHOLD). Up to 15 thoughts may be lost on a hard crash or power failure, but write throughput increases significantly for multi-agent hubs with many concurrent writers. Supported values:1,0,true,false. Has no effect on thejsonladapter.
MENTISDB_GROUP_COMMIT_MSGroup-commit window in milliseconds for the background binary writer. The writer batches appends within this window before flushing to disk. Lower values = lower latency; higher values = better throughput. Default:2MENTISDB_UPDATE_CHECKBackground GitHub release check formentisdbd. Enabled by default; set0,false,no, oroffto disable update checks after startup. Default:trueMENTISDB_UPDATE_REPOOptional GitHubowner/repooverride used by the updater. Default:CloudLLM-ai/mentisdbMENTISDB_HTTPS_MCP_PORTHTTPS MCP server port. Default:9473. Set to0to disable HTTPS MCP.MENTISDB_HTTPS_REST_PORTHTTPS REST server port. Default:9474. Set to0to disable HTTPS REST.MENTISDB_TLS_CERTPath to a PEM-encoded TLS certificate for the HTTPS servers and dashboard. Default:<MENTISDB_DIR>/tls/cert.pemMENTISDB_TLS_KEYPath to a PEM-encoded TLS private key for the HTTPS servers and dashboard. Default:<MENTISDB_DIR>/tls/key.pemMENTISDB_STARTUP_SOUNDPlay the 4-note "men-tis-D-B" startup jingle. Default:true. Set0,false,no, oroffto silence.MENTISDB_THOUGHT_SOUNDSPlay a unique short sound for each thought type on append. Default:false. Set1,true,yes, oronto enable.MENTISDB_DEDUP_THRESHOLDJaccard similarity threshold for automatic deduplication on append (0.0–1.0). When unset, auto-dedup is disabled. When set, a new thought whose content is sufficiently similar to a recent thought receives aSupersedesrelation instead of being stored as a duplicate.MENTISDB_DEDUP_SCAN_WINDOWNumber of recent thoughts to scan for dedup comparison. Default:64.
Example — full durability (production default):
MENTISDB_DIR=/tmp/mentisdb \
MENTISDB_DEFAULT_CHAIN_KEY=borganism-brain \
MENTISDB_STORAGE_ADAPTER=binary \
MENTISDB_VERBOSE=true \
MENTISDB_LOG_FILE=/tmp/mentisdb/mentisdbd.log \
MENTISDB_BIND_HOST=127.0.0.1 \
MENTISDB_MCP_PORT=9471 \
MENTISDB_REST_PORT=9472 \
MENTISDB_DASHBOARD_PIN=change-me \
MENTISDB_AUTO_FLUSH=true \
cargo run --bin mentisdbdExample — high-throughput write mode (multi-agent hub):
MENTISDB_DIR=/var/lib/mentisdb \
MENTISDB_AUTO_FLUSH=false \
MENTISDB_BIND_HOST=0.0.0.0 \
mentisdbdmentisdbd checks GitHub releases in the background after startup and can offer
to update itself with cargo install.
- checks are enabled by default
- version comparison uses only the first three numeric components, so a tag like
0.6.1.14is treated as core version0.6.1 - interactive terminals get an ASCII prompt window with
Y/N - non-interactive terminals never block; they print the exact manual
cargo installcommand instead
Disable the automatic check:
MENTISDB_UPDATE_CHECK=0 \
mentisdbdMCP endpoints:
GET /healthPOST /POST /tools/listPOST /tools/execute
REST endpoints:
GET /healthGET /mentisdb_skill_mdGET /v1/skillsGET /v1/skills/manifestGET /v1/chainsPOST /v1/chains/mergePOST /v1/vectors/rebuildPOST /v1/bootstrapPOST /v1/agentsPOST /v1/agentPOST /v1/agent-registryPOST /v1/agents/upsertPOST /v1/agents/descriptionPOST /v1/agents/aliasesPOST /v1/agents/keysPOST /v1/agents/keys/revokePOST /v1/agents/disablePOST /v1/thoughtPOST /v1/thoughtsPOST /v1/thoughts/genesisPOST /v1/thoughts/traversePOST /v1/retrospectivesPOST /v1/searchPOST /v1/lexical-searchPOST /v1/ranked-searchPOST /v1/context-bundlesPOST /v1/recent-contextPOST /v1/memory-markdownPOST /v1/import-markdownPOST /v1/skills/uploadPOST /v1/skills/searchPOST /v1/skills/readPOST /v1/skills/versionsPOST /v1/skills/deprecatePOST /v1/skills/revokePOST /v1/head
{
"chain_key": "my-chain",
"agent_id": "my-agent",
"agent_name": "My Agent",
"thought_type": "LessonLearned",
"role": "Execution",
"content": "...",
"scope": "session",
"tags": ["tag1"],
"concepts": ["concept1"],
"importance": 0.9,
"confidence": 0.8,
"refs": [14, 22],
"relations": [
{ "kind": "CausedBy", "target_id": "<uuid>" },
{ "kind": "ContinuesFrom", "target_id": "<uuid>", "chain_key": "other-chain" },
{ "kind": "Supersedes", "target_id": "<uuid>", "valid_at": "2025-01-01T00:00:00Z", "invalid_at": "2025-12-31T23:59:59Z" }
]
}chain_key, role, scope, tags, concepts, importance, confidence, refs, and relations are optional.
relations[].kind accepts: References, Summarizes, Corrects, Invalidates, CausedBy, Supports, Contradicts, DerivedFrom, ContinuesFrom, RelatedTo, Supersedes.
relations[].chain_key is optional — omit for intra-chain edges, set for cross-chain references.
relations[].valid_at and relations[].invalid_at are optional RFC 3339 timestamps that define when a relation edge is temporally valid, enabling point-in-time queries with as_of.
MentisDB keeps its baseline thought search surface filter-first and append-order. Ranked, graph-aware, and vector retrieval are additive surfaces layered on top of that stable baseline.
Today, the main search APIs are:
MentisDb::query(&ThoughtQuery)POST /v1/searchmentisdb_search
Current behavior:
- indexed filters narrow the candidate set for
thought_type,role,agent_id, tags, and concepts textis a case-insensitive substring match over:- thought
content agent_id- tags
- concepts
- agent-registry display name, aliases, owner, and description
- thought
- results are returned in append order
limitkeeps the newest matching tail after filtering rather than applying a ranking score
That means plain ThoughtQuery / /v1/search behavior is deterministic and explainable, but that baseline path is not BM25, hybrid, or vector retrieval. Additive ranked and graph-aware retrieval now exist on separate crate, REST, and MCP surfaces.
Examples:
use mentisdb::{MentisDb, ThoughtQuery, ThoughtType};
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let chain = MentisDb::open(&PathBuf::from("/tmp/tc_query"), "agent1", "Agent", None, None)?;
let lexical = ThoughtQuery::new()
.with_types(vec![ThoughtType::Decision])
.with_tags_any(["search"])
.with_text("latency");
let results = chain.query(&lexical);
# let _ = results;
# Ok(())
# }{
"chain_key": "mentisdb",
"thought_types": ["Decision"],
"tags_any": ["search"],
"text": "latency",
"limit": 20
}Design note:
- treat this lexical/filter-first behavior as the baseline
- keep ranked, vector, and hybrid retrieval as additive, explicitly documented surfaces on top of that baseline
- do not silently change the semantics of
ThoughtQueryor/v1/searchfrom append-order filtering to score-ranked retrieval
The dedicated benchmark benches/search_baseline.rs and evaluation tests in tests/search_eval_tests.rs are intended to preserve that baseline while world-class search evolves.
MentisDB now also exposes an additive ranked-search surface for direct crate use:
RankedSearchQueryRankedSearchGraphMentisDb::query_context_bundles(&RankedSearchQuery)MentisDb::query_ranked(&RankedSearchQuery)RankedSearchBackend::{Lexical, Hybrid, LexicalGraph, HybridGraph, Heuristic}
This surface is intentionally separate from ThoughtQuery.
ThoughtQuery still decides which thoughts are eligible. Ranked search then decides how those eligible thoughts are ordered.
Current ranked-search behavior:
RankedSearchQuery.filteruses the same deterministic semantics asMentisDb::query- when
textnormalizes to a non-empty query, the backend islexicalorhybriddepending on whether a managed vector sidecar is active for the current handle - lexical ranking scores indexed thought text plus agent metadata from the filtered candidate set
- when a managed vector sidecar is active for the current handle, ranked search blends lexical scoring with vector similarity and the backend becomes
hybrid - when
graphis enabled alongside non-emptytext, the backend becomeslexical_graphorhybrid_graphdepending on whether vector scoring is available - graph expansion starts from lexical seed hits, walks
refsand typedrelations, and can surface supporting context that did not lexically match as_of(RFC 3339 timestamp) filters to only relation edges that were valid at the given point in time, using each edge'svalid_at/invalid_attemporal window — edges without temporal bounds are always includedscopenarrows results to thoughts tagged with a matching memory scope (user,session, oragent); omitted scope returns all- when
textis absent or blank, the backend falls back toheuristic - heuristic ordering uses lightweight importance, confidence, and recency signals
total_candidatescounts the hits after filter application and ranked-signal gating, before finallimittruncation- each ranked hit includes
matched_termsplusmatch_sourcessuch ascontent,tags,concepts,agent_id, andagent_registry - each ranked hit also includes a
vectorscore component when semantic sidecars contribute to the ranking - graph-expanded hits also expose
graph_distance,graph_seed_paths,graph_relation_kinds, andgraph_pathprovenance so callers can explain why a supporting thought surfaced - grouped context delivery is available through
query_context_bundles, which anchors supporting graph hits beneath lexical seeds in deterministic order
Lexical ranked example:
use mentisdb::{MentisDb, RankedSearchQuery, ThoughtQuery, ThoughtType};
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let chain = MentisDb::open(&PathBuf::from("/tmp/tc_ranked"), "agent1", "Agent", None, None)?;
let ranked = RankedSearchQuery::new()
.with_filter(
ThoughtQuery::new()
.with_types(vec![ThoughtType::Decision])
.with_tags_any(["search"]),
)
.with_text("latency ranking")
.with_limit(10);
let results = chain.query_ranked(&ranked);
assert!(matches!(
results.backend,
mentisdb::RankedSearchBackend::Lexical | mentisdb::RankedSearchBackend::Hybrid
));
# let _ = results;
# Ok(())
# }Lexical + graph ranked example:
use mentisdb::{MentisDb, RankedSearchGraph, RankedSearchQuery, ThoughtQuery, ThoughtType};
use mentisdb::search::GraphExpansionMode;
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let chain = MentisDb::open(&PathBuf::from("/tmp/tc_ranked"), "agent1", "Agent", None, None)?;
let ranked = RankedSearchQuery::new()
.with_filter(
ThoughtQuery::new()
.with_types(vec![ThoughtType::Decision])
.with_tags_any(["search"]),
)
.with_text("latency ranking")
.with_graph(
RankedSearchGraph::new()
.with_max_depth(1)
.with_mode(GraphExpansionMode::Bidirectional),
)
.with_limit(10);
let results = chain.query_ranked(&ranked);
# let _ = results;
# Ok(())
# }Temporal and scoped ranked example:
use mentisdb::{MentisDb, MemoryScope, RankedSearchQuery, ThoughtQuery};
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let chain = MentisDb::open(&PathBuf::from("/tmp/tc_ranked"), "agent1", "Agent", None, None)?;
let ranked = RankedSearchQuery::new()
.with_filter(ThoughtQuery::new())
.with_text("cache invalidation")
.with_as_of("2025-06-01T00:00:00Z")
.with_scope(MemoryScope::Session)
.with_limit(10);
let results = chain.query_ranked(&ranked);
# let _ = results;
# Ok(())
# }Vector-backed ranked example:
use mentisdb::{MentisDb, RankedSearchBackend, RankedSearchQuery};
use mentisdb::search::LocalTextEmbeddingProvider;
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let mut chain = MentisDb::open_with_key(&PathBuf::from("/tmp/tc_ranked_vectors"), "semantic-ranked")?;
chain.append("planner", mentisdb::ThoughtType::Decision, "Tail latency ceiling for the Europe rollout.")?;
// Register the built-in local-text-v1 embedding sidecar so ranked search can
// blend lexical and semantic signals for the current chain handle.
chain.manage_vector_sidecar(LocalTextEmbeddingProvider::new())?;
let ranked = chain.query_ranked(&RankedSearchQuery::new().with_text("performance budget"));
assert_eq!(ranked.backend, RankedSearchBackend::Hybrid);
assert!(ranked.hits.iter().any(|hit| hit.score.vector > 0.0));
# Ok(())
# }Grouped context example:
use mentisdb::{MentisDb, RankedSearchGraph, RankedSearchQuery, ThoughtQuery};
use mentisdb::search::GraphExpansionMode;
use std::path::PathBuf;
# fn main() -> std::io::Result<()> {
let chain = MentisDb::open(&PathBuf::from("/tmp/tc_ranked"), "agent1", "Agent", None, None)?;
let bundles = chain.query_context_bundles(
&RankedSearchQuery::new()
.with_filter(ThoughtQuery::new().with_tags_any(["search"]))
.with_text("latency ranking")
.with_graph(
RankedSearchGraph::new()
.with_mode(GraphExpansionMode::Bidirectional)
.with_max_depth(2),
)
.with_limit(5),
);
# let _ = bundles;
# Ok(())
# }Product rule:
- keep
ThoughtQuerystable and explainable for append-order filtering - evolve ranked search as a separate surface with its own benchmarks, tests, and transport layers
- treat registry-aware filtering and future transport exposure as additive work on top of the current crate API
- use
query_rankedfor flat ranked retrieval andquery_context_bundleswhen the caller wants seed-anchored support context instead of one mixed list
The ranked-search benchmark benches/search_ranked.rs and evaluation tests in tests/search_ranked_eval_tests.rs are the guardrails for that additive surface.
Starting in 0.8.1, ranked search uses six key improvements:
- Porter stemming — the lexical tokenizer stems all tokens before indexing and querying so word variants share a common root (e.g.
prefers/preferred/preferences→prefer). - Smooth exponential vector-lexical fusion — replaces the 0.8.0 step-function boosts with a continuous decay curve:
vector × (1 + 35 × exp(-lexical / 3.0)). Pure-semantic matches get ~36× amplification; by lexical=3.0 the boost has decayed to ~12×; at lexical=6.0 it's additive. This eliminates discontinuities between tiers. - Session cohesion scoring — thoughts within ±8 positions of a high-scoring lexical seed (score ≥ 3.0) receive a proximity boost up to 0.8, decaying linearly with distance. This surfaces evidence turns adjacent to the matching turn but sharing no lexical terms.
- BM25 document-frequency cutoff — terms appearing in >30% of documents (corpus ≥ 20 docs) are skipped during scoring. This filters non-discriminative entity names without blanket stopword removal.
- Importance-weighted scoring — replaces flat multipliers with a differential boost proportional to lexical score:
lexical × (importance - 0.5) × 0.3. User-originated thoughts (importance ≈ 0.8) outrank verbose assistant responses (importance ≈ 0.2) in close BM25 races. - Graph expansion limits and relation boosts —
MAX_GRAPH_SEEDS=20bounds BFS cost;ContinuesFromboost raised to 0.30; graph proximity 1.0/depth.
These changes took LongMemEval R@5 from 57.2% to 67.6% and LoCoMo 2-persona R@10 from 55.8% to 88.7%.
Starting in 0.8.2, ranked search adds temporal, scoped, and dedup-aware features:
- Temporal
as_ofpoint-in-time filtering —RankedSearchQuery::with_as_of(rfc3339)restricts graph expansion to only relation edges whosevalid_at/invalid_atwindow covers the given timestamp. Edges without temporal bounds are always included. This enables queries like "what did the agent know at the start of the sprint?" - Memory scope filtering via
scopetag — thoughts carry ascopefield (user,session, oragent) stored asscope:<value>tags.RankedSearchQuery::with_scope(MemoryScope::Session)narrows results to session-scoped memories only. Omitting scope returns thoughts from all scopes. - Auto-dedup with
Supersedesrelations — whenMENTISDB_DEDUP_THRESHOLDis set, appending a thought whose content is sufficiently similar (Jaccard ≥ threshold) to a recent thought automatically creates aSupersedesrelation instead of storing a duplicate. The superseded thought's id is recorded for fast exclusion. invalidated_thought_idsfor O(1) superseded detection — each thought tracks which earlier thoughts it supersedes. Ranked search uses this set to skip superseded thoughts in constant time without walking the full relation graph.
MentisDB now exposes an additive Phase 3 vector sidecar surface for direct crate use:
search::EmbeddingProvidersearch::EmbeddingMetadatasearch::VectorSidecarVectorSearchQueryMentisDb::vector_sidecar_path(&EmbeddingMetadata)MentisDb::load_vector_sidecar(&EmbeddingMetadata)MentisDb::vector_sidecar_freshness(&VectorSidecar, &EmbeddingMetadata)MentisDb::rebuild_vector_sidecar(&provider)MentisDb::manage_vector_sidecar(provider)MentisDb::unmanage_vector_sidecar(&EmbeddingMetadata)MentisDb::managed_vector_sidecars()MentisDb::apply_persisted_managed_vector_sidecars()MentisDb::managed_vector_sidecar_statuses()MentisDb::set_managed_vector_sidecar_enabled(kind, enabled)MentisDb::sync_managed_vector_sidecar_now(kind)MentisDb::rebuild_managed_vector_sidecar_from_scratch(kind)MentisDb::query_vector(&provider, &VectorSearchQuery)
Contract:
- embeddings remain optional, and MentisDB still works with no vector dependencies at all
- vector state lives in a rebuildable sidecar, never in the canonical append-only chain
- vector sidecars are separated by
chain_key,thought_id,thought_hash,model_id, embedding dimension, and embedding version - changing the embedding model or version invalidates old vector state instead of silently mixing incompatible embeddings
- callers can opt one embedding space into append-time synchronization on a live handle by registering a managed vector sidecar provider
- vector hits surface whether they came from a
Freshor stale sidecar - deleting or corrupting the sidecar degrades only vector retrieval; plain chain reads, appends, and lexical/graph search still work
Operational flow:
- rebuild a sidecar explicitly for one provider and chain
- or register that provider as a managed vector sidecar and keep it fresh on future appends for that open handle
- load or query that sidecar later with the same embedding metadata
- if the chain head changes, the sidecar becomes stale and results report that freshness state until the sidecar is rebuilt
mentisdbd now applies a persisted managed-vector setting every time it opens a chain.
- by default each chain gets the built-in FastEmbed MiniLM embedding provider (
fastembed-minilm), which runs locally via ONNX with no cloud dependencies. The legacylocal-text-v1provider is also available. - the daemon keeps that sidecar synchronized on append unless the user disables auto-sync for that chain
- ranked search in the daemon and dashboard now uses that managed sidecar transparently, blending lexical, graph, and vector signals whenever it is enabled and available
- the web dashboard exposes per-chain controls to:
- enable or disable append-time auto-sync
- sync the sidecar to the latest chain state without changing the enable/disable setting
- rebuild the sidecar from scratch after an explicit confirmation that the previous file will be deleted and recreated
- if auto-sync is disabled, new thoughts can make the sidecar stale until the user syncs or rebuilds it
The daemon also exposes the Phase 1 ranked lexical surface over REST at POST /v1/lexical-search.
This is the right endpoint when you want lexicographical/lexical text ranking only, with simple score and match provenance fields.
Request shape:
{
"chain_key": "mentisdb",
"text": "latency ranking",
"agent_ids": ["planner"],
"thought_types": ["Decision"],
"offset": 0,
"limit": 10
}Example response:
{
"total": 1,
"results": [
{
"thought": {
"index": 42,
"agent_id": "planner",
"content": "Latency budget for the Europe rollout."
},
"score": 2.91,
"matched_terms": ["latency", "ranking"],
"match_sources": ["content", "tags", "agent_registry"]
}
]
}Phase 4 transport work keeps plain POST /v1/search and POST /v1/lexical-search
compatibility and adds two additive endpoints:
POST /v1/ranked-searchfor flat ranked retrievalPOST /v1/context-bundlesfor seed-anchored grouped support context
Example POST /v1/ranked-search request:
{
"chain_key": "mentisdb",
"text": "performance budget",
"thought_types": ["Decision"],
"limit": 10
}When a managed vector sidecar such as the built-in fastembed-minilm provider is active for that chain, the ranked backend becomes hybrid or hybrid_graph and the response includes a non-zero score.vector component for semantic-only or semantic-boosted hits.
Ranked response contract fields:
backendresults[].score.{lexical,vector,graph,relation,seed_support,importance,confidence,recency,total}results[].matched_termsresults[].match_sourcesresults[].graph_distanceresults[].graph_seed_pathsresults[].graph_relation_kindsresults[].graph_path
Context-bundle response contract fields:
total_bundlesconsumed_hitsbundles[].seed.{locator,lexical_score,matched_terms,thought}bundles[].support[].{locator,thought,depth,seed_path_count,relation_kinds,path}
MCP transport mirrors this split with additive tools:
mentisdb_ranked_searchmentisdb_context_bundles
Acceptance coverage for these transport contracts lives in:
tests/search_transport_contract_tests.rs
Response shape:
{
"total": 2,
"results": [
{
"thought": { "index": 42, "agent_id": "planner", "content": "..." },
"score": {
"lexical": 2.91,
"vector": 0.27,
"graph": 0.18,
"relation": 0.05,
"seed_support": 0.0,
"importance": 0.0,
"confidence": 0.0,
"recency": 0.0,
"total": 3.14
},
"matched_terms": ["latency", "ranking"],
"match_sources": ["content", "tags", "agent_registry"]
}
]
}The daemon includes an embedded browser UI at:
https://127.0.0.1:9475/dashboard
The dashboard is served over HTTPS with the same self-signed certificate used by the HTTPS MCP and REST surfaces.
Dashboard capabilities:
- live chain listing with thought and agent counts
- thought exploration with grouped ThoughtType filters, refs, and typed relations
- chain-scoped ranked search with text and live-agent filters
- grouped context bundles for seed-anchored supporting search context
- ranked result inspection in the thought modal, including score breakdowns, matched terms, graph distance, relation kinds, and bundle support preview
- per-chain vector sidecar inspection plus enable/disable, sync, and rebuild controls
- agent detail management for display name, description, owner, status, and signing keys
- latest agent-thought browsing without restarting the daemon after new thoughts are appended
- chain import from
MEMORY.md - cross-chain agent-memory copy with agent metadata preserved on the target chain
- skill browsing, diffing, deprecation, and revocation
Protect the dashboard with MENTISDB_DASHBOARD_PIN whenever the daemon is reachable
outside localhost.
The daemon currently exposes 34 MCP tools:
mentisdb_bootstrapCreate a chain if needed and write one bootstrap checkpoint when it is empty.mentisdb_appendAppend a durable semantic thought with optional tags, concepts, refs, scope, and signature metadata.mentisdb_append_retrospectiveAppend a retrospective memory intended to prevent future agents from repeating a hard failure.mentisdb_searchSearch thoughts by semantic filters, identity filters, time bounds, and scoring thresholds.mentisdb_lexical_searchReturn flat ranked lexical matches with explainable term and field provenance.mentisdb_ranked_searchReturn flat ranked lexical, graph-aware, or heuristic results with additive score breakdowns. Supportsas_offor point-in-time queries andscopefor memory scope filtering.mentisdb_context_bundlesReturn seed-anchored grouped support context beneath the best lexical seeds.mentisdb_list_chainsList known chains with version, storage adapter, counts, and storage location.mentisdb_merge_chainsMerge all thoughts from a source chain into a target chain, then permanently delete the source.mentisdb_list_agentsList the distinct agent identities participating in one chain.mentisdb_get_agentReturn one full agent registry record, including status, aliases, description, keys, and per-chain activity metadata.mentisdb_list_agent_registryReturn the full per-chain agent registry.mentisdb_upsert_agentCreate or update a registry record before or after an agent writes thoughts.mentisdb_set_agent_descriptionSet or clear the description stored for one registered agent.mentisdb_add_agent_aliasAdd a historical or alternate alias to a registered agent.mentisdb_add_agent_keyAdd or replace one public verification key on a registered agent.mentisdb_revoke_agent_keyRevoke one previously registered public key.mentisdb_disable_agentDisable one agent by marking its registry status as revoked.mentisdb_recent_contextRender recent thoughts into a prompt snippet for session resumption.mentisdb_memory_markdownExport aMEMORY.md-style Markdown view of the full chain or a filtered subset.mentisdb_import_memory_markdownImport aMEMORY.md-formatted Markdown document into a target chain.mentisdb_get_thoughtReturn one stored thought by stable id, chain index, or content hash.mentisdb_get_genesis_thoughtReturn the first thought ever recorded in the chain, if any.mentisdb_traverse_thoughtsTraverse the chain forward or backward in append order from a chosen anchor, in chunks, with optional filters.mentisdb_skill_mdReturn the official embeddedMENTISDB_SKILL.mdMarkdown file.mentisdb_list_skillsList versioned skill summaries from the skill registry.mentisdb_skill_manifestReturn the versioned skill-registry manifest, including searchable fields and supported formats.mentisdb_upload_skillUpload a new immutable skill version from Markdown or JSON.mentisdb_search_skillSearch skills by indexed metadata such as ids, names, tags, triggers, uploader identity, status, format, schema version, and time window.mentisdb_read_skillRead one stored skill as Markdown or JSON. Responses include trust warnings for untrusted or malicious skill content.mentisdb_skill_versionsList immutable uploaded versions for one skill.mentisdb_deprecate_skillMark a skill as deprecated while preserving all prior versions.mentisdb_revoke_skillMark a skill as revoked while preserving audit history.mentisdb_headReturn head metadata, the latest thought at the current chain tip, and integrity state.
The detailed request and response shapes for the MCP surface live in
MENTISDB_MCP.md. The REST equivalents live in
MENTISDB_REST.md.
MentisDB distinguishes three different read patterns:
headmeans the newest thought at the current tip of the append-only chaingenesismeans the very first thought in the chain- traversal means sequential browsing by append order, forward or backward, in chunks
That traversal model is deliberately different from graph/context traversal through refs and typed relations. Graph traversal answers "what is connected to this thought?" Sequential traversal answers "what came before or after this thought in the ledger?"
Lookup and traversal support:
- direct thought lookup by
id,hash, orindex - logical
genesisandheadanchors forwardandbackwardtraversal directionsinclude_anchorcontrol for inclusive vs exclusive paging- chunked pagination, including
chunk_size = 1for next/previous behavior - optional filters reused from thought search, such as agent identity, thought type, role, tags, concepts, text, importance, confidence, and time windows
- numeric time windows expressed as
start + deltawithsecondsormillisecondsunits for MCP/REST callers
MentisDB includes a versioned skill registry stored alongside chain data in a binary file. Skills are ingested through adapters:
- Markdown ->
SkillDocument - JSON ->
SkillDocument SkillDocument-> MarkdownSkillDocument-> JSON
Each uploaded skill version records:
- registry file version
- skill schema version
- upload timestamp
- responsible
agent_id - optional agent display name and owner from the MentisDB agent registry
- source format
- integrity hash
Uploaders must already exist in the agent registry for the referenced chain. Reusing an existing skill_id creates a new immutable version; it does not overwrite history.
read_skill responses include explicit safety warnings because SKILL.md content can be malicious. Treat every skill as advisory until provenance, trust, and requested capabilities are validated.
Each upload to an existing skill_id creates a new immutable version rather than overwriting history:
- The first upload stores the full content (
SkillVersionContent::Full). - Subsequent uploads store a unified diff patch against the previous version
(
SkillVersionContent::Delta), keeping storage efficient for iteratively improved skills. - Each version receives a monotone
version_number(0-based, assigned in append order). - Pass a
version_idtoread_skill/mentisdb_read_skillto retrieve any historical version. The system reconstructs it by replaying patches forward from version 0. skill_versions/mentisdb_skill_versionslists all versions with their ids, numbers, and timestamps.
Agents that have registered Ed25519 public keys in the agent registry must sign their uploads.
Required fields when the uploading agent has active keys:
signing_key_id— thekey_idregistered viaPOST /v1/agents/keysormentisdb_add_agent_keyskill_signature— 64-byte Ed25519 signature over the raw skill content bytes
Agents without registered public keys may upload without signatures.
Upload flow for signing agents:
- Register a public key:
or via MCP:
POST /v1/agents/keys { agent_id, key_id, algorithm: "ed25519", public_key_bytes }mentisdb_add_agent_key - Sign the raw content bytes with the corresponding private key (Ed25519).
- Include
signing_key_idandskill_signaturein the upload request:or via MCP:POST /v1/skills/upload { agent_id, skill_id, content, signing_key_id, skill_signature }mentisdb_upload_skillwith the same fields.
mentisdbd exposes both:
- a standard streamable HTTP MCP endpoint at
POST / - the legacy CloudLLM-compatible MCP endpoints at
POST /tools/listandPOST /tools/execute
That means you can:
- use native MCP clients such as Codex and Claude Code against
http://127.0.0.1:9471 - keep using direct HTTP calls or
cloudllm's MCP compatibility layer when needed
Codex CLI expects a streamable HTTP MCP server when you use --url:
codex mcp add mentisdb --url http://127.0.0.1:9471Useful follow-up commands:
codex mcp list
codex mcp get mentisdbThis connects Codex to the daemon's standard MCP root endpoint.
Qwen Code uses the same HTTP MCP transport model:
qwen mcp add --transport http mentisdb http://127.0.0.1:9471Useful follow-up commands:
qwen mcp listFor user-scoped configuration:
qwen mcp add --scope user --transport http mentisdb http://127.0.0.1:9471Claude for Desktop connects to MCP servers through claude_desktop_config.json.
It requires Node.js >= 20 and the
mcp-remote npm package as a
bridge between the desktop app and the MentisDB HTTPS endpoint.
The recommended setup path is automatic:
mentisdbd setup claude-desktopThis command:
- checks for Node.js >= 20 and
mcp-remoteon PATH - installs
mcp-remotevianpmif missing - writes
claude_desktop_config.jsonwith the correct absolute paths tonodeandmcp-remoteso the desktop app always uses the right Node version - sets
NODE_TLS_REJECT_UNAUTHORIZED=0for self-signed certificate support
To set it up manually instead:
Step 1 — Install prerequisites (Node.js >= 20 required):
npm install -g mcp-remoteStep 2 — Edit the config file (location by OS):
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
The config should use the node binary as the command and pass the
mcp-remote script path as the first argument. This avoids the shebang
resolution issue where mcp-remote's #!/usr/bin/env node may pick an older
Node.js version on systems with multiple Node installs (e.g. nvm).
macOS (with nvm):
{
"mcpServers": {
"mentisdb": {
"command": "/Users/you/.nvm/versions/node/v22.18.0/bin/node",
"args": ["/Users/you/.nvm/versions/node/v22.18.0/bin/mcp-remote", "https://my.mentisdb.com:9473"],
"env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}
}
}macOS (with Homebrew Node):
{
"mcpServers": {
"mentisdb": {
"command": "/opt/homebrew/bin/node",
"args": ["/opt/homebrew/bin/mcp-remote", "https://my.mentisdb.com:9473"],
"env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}
}
}Windows:
{
"mcpServers": {
"mentisdb": {
"command": "node",
"args": ["mcp-remote", "https://my.mentisdb.com:9473"],
"env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}
}
}Use which mcp-remote and which node to confirm the binary paths on your
machine. Both must point to the same Node.js installation (>= 20).
Linux:
{
"mcpServers": {
"mentisdb": {
"command": "/usr/local/bin/node",
"args": ["/usr/local/bin/mcp-remote", "https://my.mentisdb.com:9473"],
"env": { "NODE_TLS_REJECT_UNAUTHORIZED": "0" }
}
}
}Why
NODE_TLS_REJECT_UNAUTHORIZED: "0"?
MentisDB ships with a self-signed TLS certificate. Node.js rejects self-signed certs by default, which causesmcp-remoteto disconnect immediately after the MCPinitializehandshake. This env var disables that check for themcp-remoteprocess only. As an alternative, trust the certificate at the OS level (sudo security add-trusted-certon macOS) and remove theenvblock.Why use
nodeas the command instead ofmcp-remotedirectly?
Themcp-remoteshell script uses#!/usr/bin/env nodeas its shebang. On systems with multiple Node.js versions (e.g. managed by nvm), the shebang may resolve to an older Node that doesn't supportmcp-remote's dependencies (which require Node >= 20). Using the explicitnodepath as the command bypasses the shebang entirely and guarantees the correct Node version is used.
Restart Claude for Desktop after saving the config file.
Claude Code supports MCP servers through its claude mcp commands and
project/user MCP config. For a remote HTTP MCP server, the configuration shape
is transport-based:
claude mcp add --transport http mentisdb http://127.0.0.1:9471Useful follow-up commands:
claude mcp list
claude mcp get mentisdbmentisdbd setup claude-code merges the MCP server entry into
~/.claude.json (or %USERPROFILE%\.claude.json on Windows), preserving your
existing Claude Code settings. The older ~/.claude/mcp/mentisdb.json path is
treated as a legacy companion file, not the canonical config target. The
MentisDB HTTP MCP block it writes looks like this:
{
"mcpServers": {
"mentisdb": {
"type": "http",
"url": "http://127.0.0.1:9471"
}
}
}Important:
/mcpinside Claude Code is mainly for managing or authenticating MCP servers that are already configured- the server itself must already be running at the configured URL
GitHub Copilot CLI can also connect to mentisdbd as a remote HTTP MCP
server.
From interactive mode:
- Run
/mcp add - Set
Server Nametomentisdb - Set
Server TypetoHTTP - Set
URLtohttp://127.0.0.1:9471 - Leave headers empty unless you add auth later
- Save the config
You can also configure it manually in ~/.copilot/mcp-config.json (or
$XDG_CONFIG_HOME/copilot/mcp-config.json when XDG_CONFIG_HOME is set):
{
"mcpServers": {
"mentisdb": {
"type": "http",
"url": "http://127.0.0.1:9471",
"headers": {},
"tools": ["*"]
}
}
}MentisDB supports a dedicated retrospective workflow for lessons learned.
- Use
mentisdb_appendfor ordinary durable facts, constraints, decisions, plans, and summaries. - Use
mentisdb_append_retrospectiveafter a repeated failure, a long snag, or a non-obvious fix when future agents should avoid repeating the same struggle.
The retrospective helper:
- defaults
thought_typetoLessonLearned - always stores the thought with
role = Retrospective - still supports tags, concepts, confidence, importance, and
refsto earlier thoughts such as the original mistake or correction
MentisDB currently defines 30 semantic ThoughtType values and 8 operational
ThoughtRole values.
Thought types:
PreferenceUpdate,UserTrait,RelationshipUpdateFinding,Insight,FactLearned,PatternDetected,Hypothesis,SurpriseMistake,Correction,LessonLearned,AssumptionInvalidated,ReframeConstraint,Plan,Subgoal,Goal,Decision,StrategyShiftWonder,Question,Idea,ExperimentActionTaken,TaskCompleteCheckpoint,StateSnapshot,Handoff,Summary
Thought roles:
MemoryWorkingMemorySummaryCompressionCheckpointHandoffAuditRetrospective
Use ThoughtType to say what the memory means semantically, and ThoughtRole
to say how the system should treat it operationally. The crate rustdoc is the
authoritative source for per-variant semantics, and the Agent Guide on the docs
site contains a human-oriented explanation of when to use each one.
MentisDB defines a MemoryScope enum that controls how far a thought should propagate:
User(default) — visible to all agents and sessions sharing the chainSession— visible only within the current agent sessionAgent— visible only to the agent that created it
Scopes are stored as tags on the thought:
scope:userscope:sessionscope:agent
Crate API:
ThoughtInput::with_scope(MemoryScope)sets the scope on appendRankedSearchQuery::with_scope(MemoryScope)filters search results by scope
REST API:
POST /v1/thoughtsaccepts an optionalscopefield ("user","session", or"agent")POST /v1/ranked-searchandPOST /v1/context-bundlesaccept an optionalscopefield
Multiple agents can write to the same chain_key.
Each stored thought carries a stable:
agent_id
Agent profile metadata now lives in the per-chain agent registry instead of being duplicated into every thought record. Registry records can store:
display_nameagent_ownerdescriptionaliasesstatuspublic_keys- per-chain activity counters such as
thought_count,first_seen_index, andlast_seen_index
That allows a shared chain to represent memory from:
- multiple agents in one workflow
- multiple named roles in one orchestration system
- multiple tenants or owners writing to the same chain namespace
Queries can filter by:
agent_idagent_nameagent_owner
Administrative tools can also inspect and mutate the agent registry directly, so agents can be documented, disabled, aliased, or provisioned with public keys before they start writing thoughts.
At the repository root:
MENTISDB_MCP.mdMENTISDB_REST.mdmentisdb/WHITEPAPER.mdmentisdb/changelog.txt
