Developer Documentation

Overview

MentisDB is a standalone Rust crate available on crates.io. Add it to your project:

mentisdb = "0.9.1"

The crate provides the full memory engine, skill registry, storage adapter interface, agent registry, and optional HTTP server stack (MCP + REST + dashboard) behind the server feature flag.

Full API Reference

The complete, generated API documentation is hosted on docs.rs:

docs.rs/mentisdb

Full rustdoc for all public types, traits, functions, and modules. Includes MentisDb, Thought, ThoughtInput, ThoughtQuery, StorageAdapter, SkillRegistry, BinaryStorageAdapter, and the HTTP server.

View on docs.rs →

MCP Server

MentisDB ships a built-in MCP (Model Context Protocol) HTTP server. Enable it with the server feature. All MentisDB operations are exposed as MCP tools over JSON-RPC 2.0 via HTTP, making it compatible with any MCP-capable AI tool. The canonical onboarding path is not a separate URL: it happens during the MCP initialize call via startup instructions plus the embedded resource mentisdb://skill/core exposed through resources/list and resources/read.

Default HTTP endpoint: http://127.0.0.1:9471 — HTTPS endpoint: https://127.0.0.1:9473.

All 37 MCP Tools

ToolDescriptionKey Parameters
mentisdb_bootstrapCreate a chain if needed and write one bootstrap checkpoint when it is emptychain_key,content,concepts,importance
mentisdb_appendAppend a durable semantic thought with optional tags, concepts, refs, scope, and signature metadatachain_key,thought_type,content,agent_id,tags,concepts,scope,importance,confidence
mentisdb_append_retrospectiveAppend a guided retrospective memory to prevent future agents from repeating a hard failurechain_key,content,concepts,refs,importance
mentisdb_searchSearch thoughts by semantic filters, identity filters, time bounds, and scoring thresholdschain_key,text,thought_types,roles,agent_ids,since,until,min_importance,min_confidence
mentisdb_lexical_searchReturn flat ranked lexical matches with explainable term and field provenancechain_key,text,thought_types,limit,offset
mentisdb_ranked_searchReturn flat ranked results with lexical, graph-aware, or heuristic score breakdowns. Supports point-in-time queries and memory scope filteringchain_key,text,as_of,scope,enable_reranking,rerank_k,concepts_any,roles,limit
mentisdb_context_bundlesReturn seed-anchored grouped support context beneath the best lexical seedschain_key,text,as_of,scope,graph,limit
mentisdb_list_chainsList known chains with version, storage adapter, counts, and storage locationchain_key
mentisdb_merge_chainsMerge all thoughts from a source chain into a target chain, then permanently delete the sourcesource_chain_key,target_chain_key
mentisdb_branch_fromCreate a new chain diverging from a thought on an existing chain. Searches on the branch transparently include ancestor resultssource_chain_key,branch_chain_key,branch_thought_id
mentisdb_list_agentsList the distinct agent identities participating in one chainchain_key
mentisdb_get_agentReturn one full agent registry record including status, aliases, description, keys, and per-chain activity metadataagent_id,chain_key
mentisdb_list_agent_registryReturn the full per-chain agent registrychain_key
mentisdb_upsert_agentCreate or update a registry record before or after an agent writes thoughtsagent_id,display_name,agent_owner,description,status,chain_key
mentisdb_set_agent_descriptionSet or clear the description stored for one registered agentagent_id,description,chain_key
mentisdb_add_agent_aliasAdd a historical or alternate alias to a registered agentagent_id,alias,chain_key
mentisdb_add_agent_keyAdd or replace one public verification key on a registered agentagent_id,key_id,algorithm,public_key_bytes,chain_key
mentisdb_revoke_agent_keyRevoke one previously registered public keyagent_id,key_id,chain_key
mentisdb_disable_agentDisable one agent by marking its registry status as revokedagent_id,chain_key
mentisdb_recent_contextRender recent thoughts into a prompt snippet for session resumptionchain_key,last_n
mentisdb_memory_markdownExport a MEMORY.md-style Markdown view of the full chain or a filtered subsetchain_key,agent_ids,thought_types,roles,since,until,limit
mentisdb_import_memory_markdownImport a MEMORY.md-formatted Markdown document into a target chainmarkdown,default_agent_id,chain_key
mentisdb_get_thoughtReturn one stored thought by stable id, chain index, or content hashthought_id,thought_index,thought_hash,chain_key
mentisdb_get_genesis_thoughtReturn the first thought ever recorded in the chain, if anychain_key
mentisdb_traverse_thoughtsTraverse the chain forward or backward in append order from a chosen anchor, in chunks, with optional filterschain_key,anchor_id,anchor_index,anchor_hash,direction,chunk_size,thought_types,roles,since,until
mentisdb_skill_mdReturn the official embedded MENTISDB_SKILL.md Markdown file(none)
mentisdb_list_skillsList versioned skill summaries from the skill registrychain_key
mentisdb_skill_manifestReturn the versioned skill-registry manifest including searchable fields and supported formats(none)
mentisdb_upload_skillUpload a new immutable skill version from Markdown or JSONagent_id,skill_id,content,format,signing_key_id,skill_signature,chain_key
mentisdb_search_skillSearch skills by indexed metadata such as ids, names, tags, triggers, uploader identity, status, format, schema version, and time windowchain_key,text,skill_ids,names,statuses,since,until,limit
mentisdb_read_skillRead one stored skill as Markdown or JSON. Responses include trust warnings for untrusted or malicious skill contentskill_id,version_id,format,chain_key
mentisdb_skill_versionsList immutable uploaded versions for one skillskill_id,chain_key
mentisdb_deprecate_skillMark a skill as deprecated while preserving all prior versionsskill_id,reason,chain_key
mentisdb_revoke_skillMark a skill as revoked while preserving audit historyskill_id,reason,chain_key
mentisdb_headReturn head metadata, the latest thought at the current chain tip, and integrity statechain_key
mentisdb_register_webhookRegister a webhook to receive HTTP POST notifications when thoughts are appended. Delivery is fire-and-forget with exponential backoff retries (up to 5 attempts)chain_key,url,event_types
mentisdb_list_webhooksList all registered webhookschain_key
mentisdb_delete_webhookRemove a webhook registration by its UUIDwebhook_id,chain_key

JSON-RPC Request/Response Example

MCP uses JSON-RPC 2.0 over HTTP. Send a POST to the MCP endpoint:

POST / HTTP/1.1
Host: 127.0.0.1:9471
Content-Type: application/json

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "mentisdb_append",
"arguments": {
"chain_key": "default",
"agent_id": "assistant",
"thought_type": "Insight",
"content": "Memory deduplication triggers when Jaccard similarity exceeds threshold",
"importance": 0.7,
"confidence": 0.9,
"tags": ["feature:dedup"],
"concepts": ["memory-dedup", "jaccard-similarity"]
}
}
}

// Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "Thought appended: a1b2c3... (index 42)"
}
]
}
}

MCP HTTP Ports

The MCP server listens on two ports:

  • http://127.0.0.1:9471 — HTTP (unencrypted), suitable for local development
  • https://127.0.0.1:9473 — HTTPS (TLS), recommended for production

The daemon binary (mentisdb) starts the MCP server automatically. For embedding in your own Axum app, see the docs.rs API reference for server module details.

REST API

The same daemon also serves a REST API alongside MCP. Key endpoints:

MethodPathDescription
POST/v1/thoughtsAppend a new thought to a chain. Supports scope parameter for memory scope tagging
POST/v1/searchSearch/query thoughts. Supports as_of for point-in-time queries and scope for scope filtering
POST/v1/lexical-searchRanked lexical search with scores and matched-term diagnostics
POST/v1/ranked-searchCanonical flat ranked retrieval with lexical + vector + graph-aware score breakdowns (hybrid when managed sidecars are available). Supports as_of, scope, enable_reranking, and rerank_k parameters
POST/v1/context-bundlesSeed-anchored grouped support context for agent reasoning and dashboard inspection. Supports as_of and scope parameters
GET/v1/chainsList available chain keys
POST/v1/chains/branchCreate a branch chain diverging from a thought on an existing chain
POST/v1/agentsList agents in a chain or inspect the registry
GET/mentisdb_skill_mdCompatibility fallback for clients that cannot use MCP resources; MCP-native clients should read mentisdb://skill/core after initialize
POST/v1/skills/uploadUpload a skill version
POST/v1/skills/readRead a skill (latest or specific version)
POST/v1/recent-contextRender a compact resumption prompt for recent, important context
POST/v1/memory-markdownExport a chain as MentisDB-flavored MEMORY.md markdown
POST/v1/headReturn current chain head metadata and the latest thought
GET/healthLiveness health check
POST/v1/bootstrapBootstrap a chain with an initial memory
POST/v1/retrospectivesAppend a guided retrospective thought
POST/v1/thoughtRetrieve a single thought by ID, hash, or index
POST/v1/thoughts/genesisRetrieve the first thought in a chain
POST/v1/thoughts/traverseTraverse thoughts in append order with filtering and pagination
POST/v1/import-markdownImport MEMORY.md-formatted markdown into a chain
GET/v1/skillsList uploaded skill summaries
GET/v1/skills/manifestReturn the skill registry manifest with searchable fields
POST/v1/skills/searchSearch the versioned skill registry by indexed fields
POST/v1/skills/versionsList immutable versions for a stored skill
POST/v1/skills/deprecateMark a stored skill as deprecated
POST/v1/skills/revokeMark a stored skill as revoked
POST/v1/agentGet a single agent record by ID
POST/v1/agent-registryList the full agent registry for a chain
POST/v1/agents/upsertCreate or update an agent record
POST/v1/agents/descriptionSet or clear an agent's description
POST/v1/agents/aliasesAdd an alias to an agent
POST/v1/agents/keysAdd a verification key to an agent
POST/v1/agents/keys/revokeRevoke a verification key from an agent
POST/v1/agents/disableDisable (revoke) an agent
POST/v1/vectors/rebuildRebuild vector sidecar indexes for a chain
POST/v1/chains/mergeMerge all thoughts from a source chain into a target, then delete the source

For MCP-native agents, prefer the streamable HTTP root at POST / and let the agent bootstrap itself from the initialize instructions and resource catalog.

For direct crate users, 0.8.0 keeps vector sidecars additive and rebuildable, but ranked search now blends lexical, graph, and managed-sidecar vector signals automatically when sidecars are enabled. The append-only chain remains canonical; sidecars are derived acceleration state.

The daemon now applies persisted managed-vector settings whenever it opens a chain. By default each chain gets the built-in local-text provider (local-text-v1), and ranked search across REST, MCP, and dashboard surfaces upgrades to hybrid scoring when that sidecar is available.

REST Example: POST /v1/thoughts (Append Thought)

POST /v1/thoughts HTTP/1.1
Host: 127.0.0.1:9471
Content-Type: application/json

{
"chain_key": "default",
"agent_id": "assistant",
"thought_type": "Insight",
"content": "Memory deduplication triggers when Jaccard similarity exceeds threshold",
"importance": 0.7,
"confidence": 0.9,
"tags": ["feature:dedup"],
"concepts": ["memory-dedup", "jaccard-similarity"],
"scope": "user",
"relations": [
{ "kind": "ContinuesFrom", "target_id": "<uuid-of-prior-turn>" }
]
}

// Response:
{
"thought": {
"id": "a1b2c3d4-...",
"index": 42,
"hash": "9f14...",
"thought_type": "Insight",
"content": "Memory deduplication triggers when Jaccard similarity exceeds threshold",
"agent_id": "assistant",
"importance": 0.7,
"confidence": 0.9,
"tags": ["feature:dedup"],
"concepts": ["memory-dedup", "jaccard-similarity"],
"created_at": "2026-04-14T12:00:00Z"
}
}

REST Example: POST /v1/ranked-search

POST /v1/ranked-search HTTP/1.1
Host: 127.0.0.1:9471
Content-Type: application/json

{
"chain_key": "default",
"text": "how does memory deduplication work",
"scope": "user",
"limit": 10,
"enable_reranking": true,
"rerank_k": 50
}

// Response:
{
"results": [
{
"thought": {
"id": "a1b2c3d4-...",
"thought_type": "Insight",
"content": "Memory deduplication triggers when Jaccard similarity exceeds threshold",
"importance": 0.7
},
"score": 4.82,
"lexical_score": 1.2,
"vector_score": 3.1,
"session_cohesion": 0.4,
"rank": 1
}
],
"total": 1,
"query_time_ms": 12
}

REST Example: POST /v1/context-bundles

POST /v1/context-bundles HTTP/1.1
Host: 127.0.0.1:9471
Content-Type: application/json

{
"chain_key": "default",
"text": "memory deduplication decision",
"scope": "user",
"limit": 5
}

// Response:
{
"bundles": [
{
"seed": {
"id": "a1b2c3d4-...",
"thought_type": "Decision",
"content": "Enable dedup at 0.85 Jaccard threshold",
"score": 5.1
},
"supporting": [
{
"id": "b2c3d4e5-...",
"thought_type": "LessonLearned",
"content": "Previous attempts at 0.95 threshold produced too many false negatives",
"chain_key": "default"
}
],
"children": []
}
]
}

REST Example: POST /v1/webhooks/register

POST /v1/webhooks/register HTTP/1.1
Host: 127.0.0.1:9471
Content-Type: application/json

{
"chain_key": "default",
"url": "https://myapp.example.com/mentisdb-webhook",
"event_types": ["thought.append", "thought.relation.added"]
}

// Response:
{
"webhook": {
"id": "wh_abc123",
"chain_key": "default",
"url": "https://myapp.example.com/mentisdb-webhook",
"event_types": ["thought.append", "thought.relation.added"],
"created_at": "2026-04-14T12:00:00Z",
"status": "active"
}
}

0.8.0 Search &amp; Storage Improvements

MentisDB 0.8.0 introduces five major improvements to the search and storage pipeline: Porter stemming, tiered vector-lexical fusion, importance weighting, bincode hashing, and managed sidecar entries with auto_sync.

Porter Stemming (Normalizer v2)

The lexical tokenizer now applies English Porter stemming. Words like preferencesprefer and runningrun now match in lexical search. Existing chains auto-reindex on first open — no manual migration step required.

Tiered Vector-Lexical Fusion

Ranked search scoring replaces the flat addition model with a tiered boost that respects the relationship between lexical and vector signals:

  • Lexical = 0 and vector &gt; 0: vector × 60 — full boost for semantic-only hits
  • 0 &lt; lexical &lt; 1.0 and vector &gt; 0: vector × (1 + 20 × fraction) — partial boost proportional to lexical overlap
  • Lexical ≥ 1.0: vector as-is — no boost, lexical dominates

This ensures that pure semantic matches surface prominently, while thoughts already matched lexically are not artificially inflated by an additional vector boost.

Importance Weight 3.0×

User-originated thoughts carry higher importance. At the default 3.0× weight multiplier, a user thought (importance 0.8) contributes +2.4 to the final score versus an assistant thought (importance 0.2) at +0.6. This makes user-stated preferences and decisions rank above routine assistant summaries in retrieval results.

Bincode Hashing

compute_thought_hash() now uses bincode instead of serde_json for thought serialization, eliminating full JSON serialization per append. This reduces hash computation overhead and produces smaller, deterministic binary encodings. Existing chains auto-reindex on first open.

ManagedSidecarEntry with auto_sync

Vector sidecar management uses ManagedSidecarEntry with an auto_sync flag that controls whether the sidecar rebuilds on startup. The new register_vector_sidecar_for_search() method registers a sidecar for search-only without triggering a rebuild, allowing faster daemon startup while keeping the sidecar available for ranked search fusion.

0.8.1 Search Improvements

MentisDB 0.8.1 refines the scoring pipeline with session cohesion, smooth exponential fusion, and a tighter BM25 document-frequency cutoff. LongMemEval R@5 climbs from 65.0% to 67.6%; LoCoMo 2-persona R@10 reaches 88.7%.

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. The session_cohesion field is now included in ranked search score responses.

Smooth Exponential Vector-Lexical Fusion

Replaces the 0.8.0 step-function tiers with a continuous decay: 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 is purely additive. This eliminates discontinuities between tiers.

BM25 DF Cutoff 30%

Terms appearing in >30% of documents (corpus ≥ 20 docs) are skipped during scoring. This filters non-discriminative entity names without blanket stopword removal.

NaN/Infinity Guard

with_confidence() and with_importance() now reject non-finite floats. f32::NAN.clamp(0.0, 1.0) returns NaN in Rust, which previously crashed serde_json serialization when the dashboard tried to render affected thoughts.

0.8.2 Features

MentisDB 0.8.2 introduces four major features: temporal edge validity, memory deduplication, multi-level memory scopes, and CLI subcommands.

Temporal Edge Validity

ThoughtRelation now carries optional valid_at: Option&lt;DateTime&lt;Utc&gt;&gt; and invalid_at: Option&lt;DateTime&lt;Utc&gt;&gt; fields. A relation is considered active when the current time falls between these bounds. If neither is set the relation is always active (backward compatible with V2 chains). Schema V3 migration adds these fields transparently on first open.

The as_of query parameter (RFC 3339 timestamp) is supported on ranked search, context bundles, and traversal endpoints. When provided, only thoughts appended at or before the timestamp are included in results. This enables point-in-time auditing and decision reproduction.

When a relation's invalid_at has passed, the target thought is added to invalidated_thought_ids in the search response, allowing clients to filter or highlight stale edges.

Memory Dedup

Automatic deduplication on append, controlled by two environment variables:

  • MENTISDB_DEDUP_THRESHOLD — Jaccard similarity threshold (0.0–1.0). When set, each append compares the new thought's content against the last N thoughts. If similarity exceeds the threshold, the new thought receives an auto-Supersedes relation pointing at the most similar existing thought instead of being appended as a duplicate. Disabled when unset.
  • MENTISDB_DEDUP_SCAN_WINDOW — how many recent thoughts to scan (default: 64). Only used when MENTISDB_DEDUP_THRESHOLD is set.

The builder API exposes .with_dedup_threshold(f64) and .with_dedup_scan_window(usize) for programmatic control. When dedup fires, the append still succeeds but the resulting thought carries a Supersedes relation and the content is not duplicated in search results.

Multi-Level Memory Scopes

The MemoryScope enum introduces three scope levels for thoughts within a single chain:

  • MemoryScope::User — globally visible across sessions (default, backward compatible)
  • MemoryScope::Session — ephemeral working memory scoped to a single conversation
  • MemoryScope::Agent — private to the authoring agent, not visible to other fleet members

Scopes are stored as tags (scope:user, scope:session, scope:agent) on each thought. Set scope via .with_scope(MemoryScope::Session) on the builder, or pass scope in MCP/REST append calls. Filter in search with the scope parameter.

CLI Subcommands

The mentisdb binary now supports inline subcommands for quick operations without an MCP client:

  • mentisdb add "content" — append a thought directly from the command line (uses ureq to POST to the local daemon)
  • mentisdb search "query" --limit 5 — ranked search from the terminal
  • mentisdb agents — list registered agents across all chains

These subcommands communicate with the running daemon over HTTP (via ureq), so the daemon must already be started. They are convenience shortcuts — all functionality remains available via MCP, REST, and the dashboard.

Schema Version

MentisDB 0.8.2 uses schema version 3 (MENTISDB_SCHEMA_V3 = 3). V3 adds valid_at and invalid_at fields to ThoughtRelation for temporal edge validity. All new chains are created at V3 automatically. Legacy V2 chains (created before 0.8.2) are migrated transparently on first open — no manual migration step and no data loss.

Bulk Import API

Import an existing MEMORY.md (or any MentisDB-style Markdown export) into a chain in one call:

let count = db
.import_from_memory_markdown(markdown_str, "orion")
.await?;
println!("Imported {} thoughts", count);

The method parses the Markdown document, creates one thought per heading/section, and appends them all to the chain under default_agent_id. Returns the count of thoughts imported.

The same operation is also available via REST and MCP:

  • REST: POST /v1/import-markdown — body: { "markdown": "...", "default_agent_id": "orion", "chain_key": "..." }
  • Dashboard: POST /dashboard/api/chains/{chain_key}/import-markdown
  • MCP: mentisdb_import_memory_markdown(markdown, default_agent_id, chain_key)

Thought Taxonomy

The canonical enum names below are accepted by the crate API, MCP tools, REST filters, and MEMORY.md import/export. ThoughtType carries semantic meaning; ThoughtRole carries operational meaning.

ThoughtType (30): PreferenceUpdate, UserTrait, RelationshipUpdate, Finding, Insight, FactLearned, PatternDetected, Hypothesis, Mistake, Correction, LessonLearned, AssumptionInvalidated, Constraint, Goal, Plan, Subgoal, Decision, StrategyShift, Wonder, Question, Idea, Experiment, ActionTaken, TaskComplete, Checkpoint, StateSnapshot, Handoff, Summary, Surprise, and Reframe.

ThoughtRole (8): Memory, WorkingMemory, Summary, Compression, Checkpoint, Handoff, Audit, and Retrospective.

The Agent Guide contains the human-facing explanation of when to use each ThoughtType and ThoughtRole; docs.rs is the authoritative source for the Rust enum definitions and builder APIs.

MemoryScope

0.8.2 introduces MemoryScope — a visibility partition within a single chain. Scopes are stored as tags on each thought:

  • MemoryScope::User — globally visible (default). Tag: scope:user
  • MemoryScope::Session — ephemeral, scoped to one conversation. Tag: scope:session
  • MemoryScope::Agent — private to the authoring agent. Tag: scope:agent

Set scope on the builder with .with_scope(MemoryScope::Session) or pass scope in MCP/REST append calls. Filter in search with the scope parameter. Existing thoughts without a scope tag are treated as User-scoped — no migration required.

Thought Relations & Cross-chain References

A ThoughtRelation is a typed semantic edge between two thoughts. Attach relations to a ThoughtInput using the builder API:

// Intra-chain relation:
let input = ThoughtInput::new(ThoughtType::Reframe, "We now frame this as Y.")
.with_relation(ThoughtRelation {
kind: ThoughtRelationKind::Supersedes,
target_id: old_thought_uuid,
chain_key: None,
valid_at: None,
invalid_at: None,
});

// Relation with temporal bounds:
let input = ThoughtInput::new(ThoughtType::Decision, "Adopted the caching strategy.")
.with_relation(ThoughtRelation {
kind: ThoughtRelationKind::Supersedes,
target_id: old_thought_uuid,
chain_key: None,
valid_at: Some("2025-12-01T00:00:00Z".parse().unwrap()),
invalid_at: None,
});

// Cross-chain relation — use the convenience builder:
let input = ThoughtInput::new(ThoughtType::Decision, "Adopted the shared convention.")
.with_cross_chain_relation(
ThoughtRelationKind::Supersedes,
old_thought_uuid,
"platform-conventions",
);

The chain_key: Option&lt;String&gt; field on ThoughtRelation makes cross-chain references first-class. Intra-chain relations (chain_key: None) remain fully backward-compatible with all existing code.

ThoughtRelationKind::Supersedes

Use Supersedes to declare that a new thought replaces an older one that was correct but is now outdated or reframed. It is the canonical replacement edge — distinct from a Correction (which signals a factual error). Pair it with the Reframe ThoughtType for perspective or framing shifts.

All relation kinds

ThoughtRelationKind variants accepted by both the Rust API and POST /v1/thoughts:

  • References — generic pointer to another thought
  • Summarizes — this thought condenses the target
  • Corrects — fixes a factual error in the target
  • Invalidates — target is no longer applicable
  • CausedBy — this thought resulted from the target
  • Supports — this thought provides evidence for the target
  • Contradicts — this thought conflicts with the target
  • DerivedFrom — this thought is derived from the target
  • ContinuesFrom — sequential continuation; used to chain consecutive session turns
  • RelatedTo — weak associative link
  • Supersedes — replaces the target's framing or approach

REST — relations in POST /v1/thoughts

Pass relations as a JSON array in the request body. Each element has a kind string, a target_id (UUID), and an optional chain_key for cross-chain edges:

POST /v1/thoughts
{
"agent_id":     "planner",
"thought_type": "Observation",
"content":      "Retry logic resolved the timeout — root cause was DNS.",
"refs":         [41],
"relations": [
{ "kind": "ContinuesFrom", "target_id": "<uuid-of-prior-turn>" },
{ "kind": "CausedBy",      "target_id": "<uuid>", "chain_key": "infra-ops" }
]
}

Storage Adapters

MentisDB separates the memory model from the storage backend via the StorageAdapter trait:

  • BinaryStorageAdapter — Default (and only supported adapter for new chains). Compact binary format with write buffering. Best for production.

Implement the StorageAdapter trait to plug in your own backend (S3, SQLite, etc.). See docs.rs for the trait definition.

0.8.6 Features

MentisDB 0.8.6 adds three retrieval features and a chain branching primitive:

RRF Reranking

Reciprocal Rank Fusion (RRF) is an opt-in reranking pass on ranked search. When enable_reranking is set, the engine produces separate lexical-only, vector-only, and graph-only rankings over the top rerank_k candidates (default 50), merges them via 1/(k + rank) with k=60, and replaces the additive total with the RRF total. Non-rankable signals (importance, confidence, recency, session cohesion) are added back as small adjustments. Use RRF when lexical and vector signals disagree on top candidates.

Irregular Verb Lemma Expansion

The query tokenizer now expands irregular English verbs to their base form (e.g. "went" → "go", "gave" → "give", "ran" → "run"). About 170 mappings are included. Indexed content is not modified — expansion is query-time only.

Memory Chain Branching

New ThoughtRelationKind::BranchesFrom enables cross-chain divergence. MentisDb::branch_from() creates a new chain with a genesis thought pointing back to the branch-point on the source chain. When searching a branch chain, the server transparently searches ancestor chains and merges results, annotated with chain_key. REST: POST /v1/chains/branch. MCP: mentisdb_branch_from.

Per-Field BM25 DF Cutoffs

The Bm25DfCutoffs struct on LexicalQuery allows configuring separate document-frequency cutoff ratios per field (content, tags, concepts, agent_id, agent_registry). Terms whose global DF exceeds the cutoff for a given field are skipped for that field only. Default cutoffs: content=0.30, tags=0.30, concepts=0.30, agent_id=0.70, agent_registry=0.60.

0.9.x Features

MentisDB 0.9.x adds four major features: LLM extraction, federated cross-chain search, an official Python client, and an improved setup experience.

Opt-in LLM Extraction (0.9.1)

The extract_memories_from_text function uses GPT-4o (or any OpenAI-compatible endpoint) to convert raw agent text, conversation logs, or reasoning traces into structured ThoughtInput records. Extraction is opt-in behind the llm-extraction feature (enabled by default). Uses openai-rust2 with 3x retry on 429/5xx, 60s timeout, temperature 0.1. Configure via OPENAI_API_KEY, LLM_BASE_URL, and LLM_MODEL env vars. The pipeline returns raw ExtractionResult — callers must review and validate thoughts before appending to protect the hash chain from untrusted LLM output.

Federated Cross-Chain Search (0.9.1)

The BranchesFrom primitive enables fleet memory hierarchies. A project chain branches from a company-wide knowledge chain at a checkpoint. Ranked search on the branch transparently queries both the local chain and ancestor chains, annotated with chain_key so you know where each result originated. REST: POST /v1/chains/branch. MCP: mentisdb_branch_from.

Python Client — pymentisdb (0.9.1)

pymentisdb is on PyPI. Full MentisDbClient with typed enums, LangChain MentisDbMemory, webhook management, skill registry, and memory import/export. Install:

pip install pymentisdb

Or with LangChain:

pip install pymentisdb[langchain]

Webhook Callbacks (0.9.1)

Register HTTP POST callbacks that fire when thoughts are appended. Useful for reactive agents, cross-system sync, or audit logging. REST: POST /v1/webhooks. MCP: mentisdb_register_webhook, mentisdb_list_webhooks, mentisdb_delete_webhook. Delivery is fire-and-forget with exponential backoff retries.

Smart Stdio Mode — Auto-Detect Daemon (0.9.2)

The stdio MCP mode now detects if a daemon is already running on the configured MCP port via the health endpoint. If running, the stdio process acts as a lightweight proxy forwarding requests to the daemon's HTTP MCP endpoints. If not running, it launches the daemon in the background (nohup on Unix, start /B on Windows) in headless HTTP mode and proxies. This means Claude Desktop users get shared live state with zero configuration — just set the command to mentisdb with args --mode stdio.

Dashboard Skill Editing (0.9.7)

The web dashboard can now edit skills from both the Skills table and a skill detail page. Saving creates a new immutable skill version through the existing upload path, preserving audit history instead of mutating prior content.

Wizard Brew-First Setup (0.9.1)

The interactive setup wizard mentisdb setup now tries brew install mcp-remote before npm. It detects Homebrew-installed mcp-remote (which has a proper shebang pointing to the correct Node version) and writes the minimal Claude Desktop config automatically.

Custom Ontology — entity_type (0.8.7)

Thoughts can carry an optional entity_type label (e.g. "bug_report", "architecture_decision"). The ThoughtQuery accepts entity_type as a filter. Entity types are auto-observed per chain and persisted in a chain_key-entity-types.json sidecar.

Episode Provenance — source_episode (0.8.8)

Thoughts can carry a source_episode field pointing to the original thought, with a new DerivedFrom relation kind. The dashboard shows full provenance graphs.

LLM-Based Reranking (0.8.8)

In addition to arithmetic RRF, RankedSearchQuery accepts an optional llm_rerank block that delegates the final rerank of the top-rerank_k candidates to an OpenAI-compatible model. The server sends the candidate thoughts and the original query, parses the model's ranked IDs, and blends the returned ordinal score (under the llm_score field on each hit) with the arithmetic signals as an additive tie-breaker. LLM reranking is strictly opt-in and disabled by default; the core pipeline remains LLM-free.

Operations & Admin

For the full list of environment variables that tune storage, behaviour, networking, TLS, the dashboard, updates, and audio cues, see the Environment Variables Reference in the user manual — every MENTISDB_* knob is documented there with defaults, purpose, and a concrete example.

POST /v1/admin/flush

REST endpoint that walks every open chain and calls flush() on its storage adapter. Useful in buffered-write mode (MENTISDB_AUTO_FLUSH=false) where up to FLUSH_THRESHOLD − 1 records can sit in the background-writer queue. The backup CLI calls this automatically when it detects a running daemon; consistent on-disk state is a precondition for a reliable .mentis archive.

Backup & Restore (.mentis archive)

mentisdb backup produces a .mentis ZIP archive covering every chain data file (*.tcbin, *.agents.json, *.entity-types.json, *.vectors.*.json), the global chain registry, the skill registry, and optionally TLS certificates. When a daemon is running the CLI first POSTs to /v1/admin/flush so pending writes are on disk before the archive is assembled.

mentisdb restore reverses the operation. Path traversal is rejected server-side — manifest entries containing ../ or absolute paths fail with InvalidData before any file is written outside the target directory. Existing files are preserved by default; pass --overwrite to replace them, or answer yes to the interactive confirmation.

Dashboard PIN

Set MENTISDB_DASHBOARD_PIN to require a shared PIN before the web dashboard accepts any request. An empty string is treated as absent. Failed logins are rate-limited per-IP to prevent brute-force enumeration; the dashboard is always served over TLS so the PIN is never transmitted in the clear on a local network.

Benchmarking

MentisDB ships with two benchmark styles:

  • Criterion microbenchmarks for in-process append, query, traversal, import, and skill-registry hot paths
  • HTTP concurrency benchmarks for live mentisdb write/read waves under concurrent client load

Run the full benchmark suite with:

make bench

This currently runs cargo bench and saves stdout to /tmp/mentisdb_bench_results.txt.

Durable vs Buffered Write Mode

Benchmark write-heavy changes in both persistence modes. They now behave differently enough that one run is not representative of the other:

  • MENTISDB_BENCH_AUTO_FLUSH=true — durable mode. Appends are queued to the bounded background writer and the request only returns after the writer flushes them. Concurrent appends may share a short group-commit window.
  • MENTISDB_BENCH_AUTO_FLUSH=false — buffered mode. Appends are queued and acknowledged before the worker flushes them, trading durability for higher write throughput.
MENTISDB_BENCH_AUTO_FLUSH=true  cargo bench --bench http_concurrency
MENTISDB_BENCH_AUTO_FLUSH=false cargo bench --bench http_concurrency

Criterion Baselines

Criterion benchmarks such as thought_chain and skill_registry automatically compare the current run against the saved baseline in target/criterion/. That is why the output includes messages like Performance has improved, Performance has regressed, or No change in performance detected.

  • time: [low mid high] — lower is better
  • thrpt: [low mid high] — higher is better
  • change — percentage delta versus the saved baseline
  • p &lt; 0.05 — the change is statistically meaningful

If you have changed the benchmark harness itself or want a fresh comparison, clear the old Criterion baseline before trusting the regression/improvement messages.

HTTP Concurrency Baselines

The custom http_concurrency benchmark now persists its own baselines under target/http_concurrency/ and prints delta tables on later runs. This means you no longer need to manually compare the Markdown output tables by eye.

MENTISDB_BENCH_AUTO_FLUSH=true  MENTISDB_BENCH_BASELINE=durable  cargo bench --bench http_concurrency
MENTISDB_BENCH_AUTO_FLUSH=false MENTISDB_BENCH_BASELINE=buffered cargo bench --bench http_concurrency

Useful environment variables:

  • MENTISDB_BENCH_CONCURRENCY — comma-separated client counts such as 100,1000,10000
  • MENTISDB_BENCH_AUTO_FLUSHtrue for durable group commit, false for buffered throughput mode
  • MENTISDB_BENCH_BASELINE — names the saved baseline file so you can keep separate durable, buffered, nightly, or branch-specific histories

How to Read the HTTP Table

  • wall_ms — total time for the whole wave
  • req/s — throughput
  • p50_ms, p95_ms, p99_ms — median and tail latency
  • errors — non-2xx responses or transport failures

For write-path work, focus on the Write — POST /v1/thoughts table at high concurrency. Read-path changes should primarily move the Read — POST /v1/head table and the query/traversal Criterion benches.

What to Benchmark After Storage Changes

  • cargo bench --bench thought_chain append_single -- --noplot — strict append latency
  • cargo bench --bench thought_chain query_by_tag -- --noplot — indexed query path
  • cargo bench --bench thought_chain traverse_filtered_miss_10 -- --noplot — full-scan traversal miss path
  • cargo bench --bench http_concurrency — live daemon concurrency behavior

A useful workflow is to measure a clean baseline on master, apply the storage change, rerun the same focused benches, and only then trust the wider make bench output.

Contributing

MentisDB is open source under the MIT license.

git clone https://github.com/cloudllm-ai/mentisdb

Run the test suite:

cargo test

Run benchmarks:

make bench
View on GitHub →