← Blog
April 21, 2026

MentisDB × OpenClaw — Memory Integration Guide and Protocol Comparison

Last week we analyzed Hermes' memory architecture and proposed its MemoryProvider ABC as the basis for a community agent-memory-protocol standard. This week we applied the same analysis to OpenClaw — a TypeScript multi-channel AI gateway with 17,000+ commits, active daily development, and sponsors including OpenAI, GitHub, and NVIDIA.

The results are revealing. OpenClaw and Hermes both solve the pluggable memory problem — but with different philosophies, different languages, and meaningfully different tradeoffs. And one key prediction from the Hermes post turned out to be wrong in an instructive way.

Contents

  1. What OpenClaw is — and how fast it moves
  2. OpenClaw's memory architecture
  3. Zero-code tutorial: MentisDB as an MCP tool in OpenClaw
  4. Native TypeScript backend implementation
  5. Comparing the two: Hermes vs OpenClaw vs our proposed standard
  6. Reassessing the protocol proposal

What OpenClaw Is — and How Fast It Moves

OpenClaw is a personal AI gateway. Where Hermes is a self-improving agent primarily accessed through a terminal, OpenClaw is a multi-channel daemon: it runs as a background service and routes messages between Claude and every messaging platform you already use — WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Matrix, Zalo, WeChat, QQ, and more. You talk to your AI from your phone; OpenClaw handles the protocol translation, tool dispatch, memory, and delivery.

The version tag as of this writing is 2026.4.20. Its git history has over 17,000 commits. The last 12 commits are all from the past few days: CI performance tuning, test refactoring, shared fixture helpers. This is a codebase under continuous professional maintenance, not a research prototype.

The relevance for memory integration: OpenClaw must work reliably across sessions measured in weeks or months, across multiple messaging platforms, often for multiple users simultaneously via its gateway mode. Memory isn't optional — it's load-bearing infrastructure.


OpenClaw's Memory Architecture

The capability registration pattern

Where Hermes defines memory via an ABC that providers inherit, OpenClaw uses a capability registration pattern. A memory plugin registers a bundle of callbacks rather than subclassing an interface:

// src/plugins/memory-state.ts
export type MemoryPluginCapability = {
  promptBuilder?:    MemoryPromptSectionBuilder;
  flushPlanResolver?: MemoryFlushPlanResolver;
  runtime?:          MemoryPluginRuntime;
  publicArtifacts?:  MemoryPluginPublicArtifactsProvider;
};

export function registerMemoryCapability(
  pluginId: string,
  capability: MemoryPluginCapability,
): void {
  memoryPluginState.capability = { pluginId, capability: { ...capability } };
}

Each slot is independently replaceable:

The MemorySearchManager interface

The runtime slot expects a MemorySearchManager, defined in the SDK. The three existing backends all implement it:

// extensions/memory-core/src/memory/manager.ts
export class MemoryIndexManager
  extends MemoryManagerEmbeddingOps
  implements MemorySearchManager
{
  async search(
    query: string,
    opts?: {
      maxResults?: number;
      minScore?: number;
      sessionKey?: string;
      onDebug?: (debug: MemorySearchRuntimeDebug) => void;
    },
  ): Promise<MemorySearchResult[]>

  async warmSession(sessionKey?: string): Promise<void>

  status(): MemoryProviderStatus

  async close(): Promise<void>
}

Search results are a typed union:

// src/plugins/memory-state.ts
type MemoryCorpusSearchResult = {
  corpus:   string;
  path:     string;
  title?:   string;
  kind?:    string;
  score:    number;
  snippet:  string;
  lines?:   { start: number; end: number };
};

Three backends today

BackendStorageSearchWhen to use
builtin SQLite per agent
~/.openclaw/state/memory/{agentId}.sqlite
FTS5 + optional vector Default. Works offline, zero config.
qmd Quantum Memory Daemon (local sidecar) Vector + reranking + query expansion Higher recall quality, local-first.
honcho Honcho cloud service AI-native, cross-session user modeling Multi-user, cross-device memory.

Memory in the agent loop

Five integration points, each corresponding to a different phase of a conversation turn:

  1. System prompt assemblypromptBuilder() injects recall guidance. The model is told to call memory_search before answering questions about prior context.
  2. Tool registrationmemory_search and memory_get are registered from extensions/memory-core/src/tools.ts and appear in the agent's tool catalog.
  3. Session warmupwarmSession(sessionKey) is called at session start. Implementations use this to trigger background index sync.
  4. Search-triggered sync — when the model calls memory_search, the runtime can optionally kick off an async re-sync for subsequent turns.
  5. Context compression — before messages are discarded, flushPlanResolver decides what to durably write. A silent agent turn runs the plan and appends to MEMORY.md, append-only, guardian-protected.

MCP is a first-class citizen

Unlike Hermes — where MCP is one of several tool integration paths — OpenClaw treats MCP as a primary tool registration mechanism. The configuration type (src/config/types.mcp.ts) supports both stdio and HTTP transports. MCP tools are materialized into OpenClaw's native tool catalog at startup via pi-bundle-mcp-materialize.ts, subject to the same tool policy and allow/deny logic as built-in tools.

// src/config/types.mcp.ts
export type McpServerConfig = {
  command?: string;           // stdio transport
  args?: string[];
  url?: string;               // HTTP transport
  transport?: "sse" | "streamable-http";
  headers?: Record<string, string | number | boolean>;
  connectionTimeoutMs?: number;
};

Zero-Code Tutorial: MentisDB as an MCP Tool in OpenClaw

Time to working: under two minutes. No code, no compilation, no plugin installation — just four lines of YAML and a daemon restart.

Step 1 — Start the MentisDB daemon

# Install once
cargo install mentisdb

# Start the daemon (MCP endpoint on port 9471)
mentisdbd &

# Verify
curl -s http://localhost:9471/health
# → {"status":"ok"}

Step 2 — Register MentisDB in openclaw.config.yml

mcp:
  servers:
    mentisdb:
      url: "http://localhost:9471"
      transport: "streamable-http"
      connectionTimeoutMs: 5000

Save and restart OpenClaw:

openclaw gateway restart

Step 3 — Confirm tool discovery

openclaw tools list | grep mentisdb
# mcp_mentisdb_mentisdb_search
# mcp_mentisdb_mentisdb_append_thought
# mcp_mentisdb_mentisdb_recent_context
# mcp_mentisdb_mentisdb_bootstrap
# … all MentisDB MCP tools

MentisDB tools are now available in every agent conversation, just like any other built-in tool. The model can call them directly:

# From any channel (Telegram, WhatsApp, Slack, …)
You: Search my memory for anything about the API rate limiting fix from last month.

# OpenClaw routes to: mcp_mentisdb_mentisdb_search({"query": "API rate limiting fix"})
# Returns ranked results from MentisDB's hybrid retrieval pipeline

Optional — restrict exposed tools

mcp:
  servers:
    mentisdb:
      url: "http://localhost:9471"
      transport: "streamable-http"
      tools:
        - mentisdb_search
        - mentisdb_append_thought
        - mentisdb_recent_context

Optional — prime the agent to use MentisDB first

Add to your agent's system prompt or MEMORY.md:

At the start of each conversation, call mentisdb_bootstrap to load context
from the chain before answering any questions.

Optional — HTTPS

mcp:
  servers:
    mentisdb:
      url: "https://my.mentisdb.com:9473"
      transport: "streamable-http"

Native TypeScript Backend Implementation

For full lifecycle integration — automatic turn sync, session warmup, flush plan participation — you can implement MentisDB as a native OpenClaw memory backend. This is a TypeScript extension plugin, not a Python ABC subclass.

Create the plugin directory:

~/.openclaw/plugins/
└── memory-mentisdb/
    ├── package.json
    ├── openclaw.plugin.json
    └── src/
        ├── index.ts        ← entry point, registers the capability
        └── manager.ts      ← MemorySearchManager implementation

openclaw.plugin.json

{
  "id": "memory-mentisdb",
  "name": "MentisDB Memory",
  "description": "Durable, hash-chained semantic memory via MentisDB",
  "version": "1.0.0",
  "entrypoint": "./src/index.ts",
  "capabilities": ["memory"]
}

src/manager.ts — the search backend:

import type { MemorySearchManager, MemorySearchResult, MemoryProviderStatus }
  from "@openclaw/sdk";

export class MentisDBManager implements MemorySearchManager {
  private baseUrl: string;
  private chainKey: string;
  private abortController: AbortController | null = null;

  constructor(opts: { url: string; chainKey: string }) {
    this.baseUrl  = opts.url;
    this.chainKey = opts.chainKey;
  }

  async search(
    query: string,
    opts?: { maxResults?: number; minScore?: number; sessionKey?: string },
  ): Promise<MemorySearchResult[]> {
    const resp = await fetch(`${this.baseUrl}/v1/search`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        query,
        limit:     opts?.maxResults ?? 10,
        chain_key: this.chainKey,
      }),
    });
    if (!resp.ok) return [];

    const { thoughts } = await resp.json();
    return (thoughts ?? []).map((t: any) => ({
      corpus:  "mentisdb",
      path:    `mentisdb://${t.id}`,
      title:   t.thought_type,
      kind:    t.thought_type?.toLowerCase(),
      score:   t.score ?? 0.5,
      snippet: t.content,
    }));
  }

  async warmSession(sessionKey?: string): Promise<void> {
    // Optional: trigger background sync, verify daemon reachability
    await fetch(`${this.baseUrl}/health`).catch(() => {});
  }

  status(): MemoryProviderStatus {
    return { available: true, label: "MentisDB" };
  }

  async close(): Promise<void> {
    this.abortController?.abort();
  }
}

/** Append a thought to MentisDB (called from flush plan + sync_turn). */
export async function appendThought(opts: {
  baseUrl:     string;
  chainKey:    string;
  content:     string;
  thoughtType: string;
  tags?:       string[];
}): Promise<void> {
  await fetch(`${opts.baseUrl}/v1/thoughts`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      content:      opts.content,
      thought_type: opts.thoughtType,
      chain_key:    opts.chainKey,
      tags:         opts.tags ?? [],
    }),
  }).catch(console.error);
}

src/index.ts — register the capability:

import type { OpenClawPluginAPI } from "@openclaw/sdk";
import { MentisDBManager, appendThought } from "./manager.js";

export function register(api: OpenClawPluginAPI): void {
  const config = api.getConfig();
  const baseUrl  = config.env?.MENTISDB_URL  ?? "http://localhost:9471";
  const chainKey = config.env?.MENTISDB_CHAIN ?? "openclaw";

  const manager = new MentisDBManager({ url: baseUrl, chainKey });

  api.registerMemoryCapability("memory-mentisdb", {
    // ── Prompt block ──────────────────────────────────────────────────────
    promptBuilder: (_tools) =>
      "Your long-term memory is stored in MentisDB. Before answering questions\n"
      + "about past decisions, prior work, or user preferences, call memory_search.",

    // ── Flush plan: what to persist before context compression ────────────
    flushPlanResolver: async (messages) => {
      const userMsgs = messages
        .filter(m => m.role === "user" && m.content)
        .slice(-5);
      return userMsgs.map(m => ({
        content: String(m.content).slice(0, 2000),
        write: async () =>
          appendThought({
            baseUrl, chainKey,
            content:     String(m.content).slice(0, 2000),
            thoughtType: "Observation",
            tags:        ["openclaw", "pre-compress"],
          }),
      }));
    },

    // ── Runtime: the actual search engine ────────────────────────────────
    runtime: {
      manager,
      // sync_turn: called after every completed turn
      onTurnComplete: async (userContent, assistantContent) => {
        await appendThought({
          baseUrl, chainKey,
          content:     `User: ${userContent}\nAssistant: ${assistantContent}`.slice(0, 3000),
          thoughtType: "Observation",
          tags:        ["openclaw", "turn"],
        });
      },
    },

    // ── Public artifacts: list memories in gateway dashboard ──────────────
    publicArtifacts: {
      listArtifacts: async () => [{
        id:    "mentisdb-chain",
        label: `MentisDB chain: ${chainKey}`,
        kind:  "memory-index",
      }],
    },
  });
}

Activate via config:

memory:
  provider: memory-mentisdb
  env:
    MENTISDB_URL:   "http://localhost:9471"
    MENTISDB_CHAIN: "openclaw"

Comparing the Two: Hermes vs OpenClaw vs Our Proposed Standard

Feature Hermes MemoryProvider OpenClaw MemoryPlugin Proposed Standard
Language Python TypeScript Language-agnostic spec
Pattern ABC inheritance Capability registration Either (composable preferred)
Recall / persist ✓ prefetch + sync_turn ✓ search + onTurnComplete ✓ required
Prompt injection ✓ system_prompt_block() ✓ promptBuilder() ✓ required
Tool exposure to model ✓ get_tool_schemas() ✓ via memory_search / memory_get built-ins ✓ required
Async prefetch / caching ✓ queue_prefetch() ✓ warmSession() + async sync ✓ required
Pre-compression hook ✓ on_pre_compress() ✓ flushPlanResolver() ✓ required
Built-in write bridging ✓ on_memory_write() ~ guardian-protected MEMORY.md append ✓ recommended
Delegation observability ✓ on_delegation() — not exposed ✓ recommended
Session boundary hook ✓ on_session_end() ~ implicit via close() ✓ required
Public artifact listing — not in Hermes ✓ publicArtifacts ~ nice to have
Multi-tenant scoping ✓ user_id, agent_identity in initialize() ✓ sessionKey in search() / warmSession() ✓ required
MCP as first class ~ one integration path among several ✓ primary tool registration mechanism ✓ required
Failure isolation ✓ catches & logs, agent continues ✓ same pattern ✓ required
Config / setup wizard ✓ get_config_schema() ~ via plugin.json + env keys ✓ recommended

Both frameworks cover the core protocol. OpenClaw adds publicArtifacts (no Hermes equivalent) and makes MCP a first-class transport. Hermes adds on_delegation() (no OpenClaw equivalent) and a richer setup wizard abstraction. Neither is strictly a superset of the other.


Reassessing the Protocol Proposal

In the Hermes post we proposed six design principles for an agent-memory-protocol standard. OpenClaw is an independent implementation built by a different team — it's an honest test of whether those principles hold.

What the proposal got right

Frozen snapshot injection. ✓ Both frameworks freeze the memory snapshot into the system prompt at session start. OpenClaw's MEMORY.md is loaded once; Hermes' MEMORY.md is loaded once. Mid-session writes go to disk, not the active prompt. This isn't a coincidence — it's the only approach that preserves the LLM's prefix cache. Any standard must mandate it.

Pre-compression extraction. ✓ Both frameworks hook before context is compressed. Hermes calls on_pre_compress(). OpenClaw calls flushPlanResolver(). The mechanism differs — Hermes is imperative (extract now), OpenClaw is declarative (return a plan, system executes it) — but the intent is identical. Any standard must define this hook.

Async prefetch at turn boundary. ✓ Both frameworks warm the cache for the next turn while the current one is processing. Hermes uses Python threads; OpenClaw uses async warmSession calls. Same pattern, different runtime.

Failure isolation. ✓ Both frameworks catch exceptions in memory provider calls and continue. This is non-negotiable for production agents.

Where the proposal needs revision

ABC inheritance vs. capability registration. The Hermes post assumed that a standard would look like an abstract base class — a single interface you subclass. OpenClaw proves otherwise. Its capability registration pattern is equally expressive and arguably more composable: you register only the callbacks you implement, and each can be replaced independently without touching the others. A language-agnostic standard should define the protocol (which events fire, in what order, with what arguments) rather than mandating an implementation pattern. ABC or capability registration are both conforming implementations.

MCP must be part of the standard, not optional. The Hermes post treated MCP as one integration path. OpenClaw treats it as the primary tool registration mechanism. Given that MCP is rapidly becoming the universal tool protocol — backed by Anthropic, adopted by GitHub Copilot, Cursor, Claude Desktop, and now OpenClaw — any memory protocol that doesn't specify how MCP interacts with memory tools will be incomplete within twelve months. The standard should define: how a memory backend exposes tools via MCP, and how an agent framework routes those MCP tools through the memory lifecycle hooks.

Delegation observability is real, but niche. Hermes' on_delegation() hook — called when a subagent completes — has no OpenClaw equivalent. OpenClaw runs in a gateway context where multi-agent delegation isn't the primary model. This suggests on_delegation() should be optional in the standard, not required. It matters for orchestration frameworks; it's irrelevant for single-user gateways.

"One provider" is a Hermes constraint, not a universal principle. Hermes enforces one external provider at a time. OpenClaw does the same implicitly (via the single capability slot). But this is an implementation choice that both frameworks made for practical reasons, not a protocol requirement. A standard should not mandate single-provider — some use cases legitimately need two backends (e.g., local fast recall + remote long-term storage with different latency profiles).

The revised protocol requirements

Based on both implementations, here is what a conforming memory protocol must define:

  1. Availability check — synchronous, no network calls, called at startup
  2. Session initialization — receives session_id, platform, user scoping context
  3. System prompt block — static guidance for the model, injected once at session start
  4. Tool schemas — OpenAI-compatible schemas for model-callable memory tools
  5. Tool dispatch — handle model-initiated calls to registered tools
  6. Async prefetch — warm cache for next turn while current turn processes
  7. Turn sync — persist completed (user, assistant) turn to backend
  8. Pre-compression extraction — hook fires before messages are discarded; either imperative or declarative plan
  9. Session boundary — called at session end with full message history
  10. MCP interop — memory tools must be exposable via MCP; the framework must route MCP tool calls through the memory lifecycle
  11. Failure isolation — all provider calls must be caught; errors logged but agent continues

Optional (implement for richer integrations):

These eleven required points and four optional extensions are where both Hermes and OpenClaw converge — independently, built by different teams, in different languages, for different deployment models. That convergence is the evidence that they belong in a standard.

Next steps

The native TypeScript backend in Part 3 is a working starting point. We plan to publish it as a proper @mentisdb/openclaw-plugin npm package and submit it as a pull request to the OpenClaw plugin registry alongside the Python MemoryProvider plugin for Hermes.

For the protocol itself: we'll open a GitHub discussion to collect feedback from both the Hermes and OpenClaw communities before drafting a formal spec. If you're building an agent framework and want to participate: join the discussion.