Skip to main content
Glama

MemStack

Persistent memory for AI agents. Claude Code and Codex share one memory per project, and your own agents get the same pipeline: store, retrieve, summarize, and prune.

Website and install guides: memstack.stalewell.com

npm version skills.sh MCP Registry memstack MCP server – quality and maintenance score on Glama CI License: MIT MCP Reference

Tell one agent how your project works. The other already knows. Claude Code learns a convention; a new Codex session recalls it and gets the task right:

Claude Code saves a project convention to MemStack; a new Codex session recalls it and writes pnpm install and fly deploy

npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0   # MemStack never installs storage drivers
memstack init                  # LLM provider + store
memstack connect claude-code
memstack connect codex

Every session then starts with the project's key memories, and both agents save what you ask them to remember. How harness memory works.

Building your own agent?

# Use MemStack in your application
npm install @memstack/core

# Give your coding agents the MemStack skill (-g: every project; omit it for this project only)
npx skills add isiomaC/memstack -g

@memstack/core is the runtime SDK; the Agent Skill teaches compatible coding agents how to integrate and operate MemStack correctly. Without -g, the skill installs into the current project (.agents/skills/, plus a .claude/skills/ link for Claude Code). Update it later with npx skills update.

The problem: AI agents forget. Every interaction starts from zero. You either stuff everything into the context window (expensive, slow, degrades output quality) or the agent has no memory of past conversations.

What MemStack does: A persistent memory pipeline that lives between your agent and the LLM. It stores every interaction, retrieves only what's relevant, summarizes old memories to save tokens, and prunes stale ones automatically. One method call, no infrastructure required.

Think of it as the open-source alternative to Mem0 — pluggable storage, bring your own LLM, zero vendor lock-in.

memstack MCP server – quality and maintenance score on Glama


Table of Contents


Related MCP server: recall-mcp

Why MemStack

LLMs have context windows, not memory. The difference matters.

Approach

Problem

Stuff everything in context

Cost is O(n²). 100 conversations = thousands of tokens = dollars per call. Quality degrades from "lost in the middle" effect.

Use a vector DB directly

You get similarity search. You don't get summarization, pruning, recency weighting, deduplication, or token budget management. You're building the pipeline yourself.

Use Mem0

Proprietary, cloud-only with their hosted API. You don't control where your data lives.

Use MemStack

Full pipeline. Pluggable everything. Your data, your infrastructure. Open source.

What MemStack handles that raw vector DBs don't:

  • Summarization — compress 100 old interactions into one paragraph, keep meaning, save tokens

  • Recency weighting — recent memories matter more; MemStack sorts them higher

  • Importance scoring — not all memories are equal; high-importance ones survive pruning

  • Deduplication — identical or near-identical memories are collapsed in context assembly

  • Token budget — compileContext() tells you how many tokens you're spending before the LLM call

  • Memory-type routing — interactions, summaries, observations treated differently at retrieval time

  • Auto-pruning — old, low-importance memories clean themselves up


Quick Start

Claude Code and Codex

Persistent memory across agent harnesses: what you tell Claude Code, Codex recalls in the same project, and the reverse.

npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0  # MemStack never installs storage drivers for you
memstack init                  # choose an LLM provider and a store
memstack connect claude-code
memstack connect codex

Then, in Claude Code: "Remember that this project uses Hono." In Codex, in the same repository: "What framework does this project use?" Codex answers Hono. See Harness Memory for how it works.

As a library

npm install @memstack/core

OpenAI

import { MemStack, OpenAILLMAdapter, OpenAIEmbeddingAdapter, InMemoryStorageAdapter } from "@memstack/core";

const llm = new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! });

const memstack = new MemStack({
  llm,
  embedding: new OpenAIEmbeddingAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
});

DeepSeek (no embeddings)

DeepSeek provides chat completions but has no embedding API. Use the OpenAI-compatible LLM adapter with baseURL and omit the embedding adapter — retrieval falls back to keyword + recency + importance ranking. You still get the full pipeline: store, summarize, prune, and compileContext.

import { MemStack, OpenAILLMAdapter, InMemoryStorageAdapter } from "@memstack/core";

const llm = new OpenAILLMAdapter({
  apiKey: process.env.DEEPSEEK_API_KEY!,
  baseURL: "https://api.deepseek.com/v1",
  defaultModel: "deepseek-flash",
});

const memstack = new MemStack({
  llm,
  storage: new InMemoryStorageAdapter(),
  // No embedding adapter — retrieval uses keyword matching
});

OpenRouter / Together AI / any OpenAI-compatible API

Same pattern — change baseURL and defaultModel:

// OpenRouter
const llm = new OpenAILLMAdapter({
  apiKey: process.env.OPENROUTER_API_KEY!,
  baseURL: "https://openrouter.ai/api/v1",
  defaultModel: "openai/gpt-4o-mini",
});

// Together AI
const llm = new OpenAILLMAdapter({
  apiKey: process.env.TOGETHER_API_KEY!,
  baseURL: "https://api.together.xyz/v1",
  defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
});

// Gemini (OpenAI-compatible endpoint)
const llm = new OpenAILLMAdapter({
  apiKey: process.env.GEMINI_API_KEY!,
  baseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
  defaultModel: "gemini-2.0-flash",
});

Store and retrieve

// 1. Store what happened
await memstack.memory.store({
  actorId: "support-bot-42",
  content: "User reports login failing with error 503 on Chrome 125.",
  tags: ["login", "bug", "chrome"],
  importance: 0.8,
});

// 2. Later, retrieve relevant context
const memories = await memstack.memory.retrieve({
  actorId: "support-bot-42",
  query: "login error",
  strategy: "hybrid",
});

// 3. Assemble an LLM-ready context
const ctx = await memstack.memory.compileContext({
  actorId: "support-bot-42",
  maxTokens: 2000,
});

const response = await llm.complete({
  system: `You are a support bot. Here is what you remember:\n${ctx.systemPrompt}`,
  user: "The user is back and still can't log in. What do you do?",
});

console.log(response.text);
// "Based on our history, the user has been experiencing 503 errors on Chrome 125..."

// 4. Every 100 interactions, summarization triggers automatically.
// Old interactions are compressed into a paragraph. Token costs stay flat.

Harness Memory (Claude Code & Codex)

Coding agents forget everything between sessions, and they don't share what they learn with each other. MemStack gives Claude Code and Codex one memory per project: a decision made with one agent is known to the other, and to every later session.

How it works

  • Saving. Each agent gets five MemStack tools (memory_store, memory_retrieve, memory_get, memory_delete, memory_stats) through the MCP harness profile. When you say "remember that…", or the agent learns a durable fact, decision, preference, or rule, it calls memory_store. MemStack asks your LLM for a few topic tags, so "uses Hono" can later be found by "which framework?". If tagging fails, the memory is still saved.

  • Recalling. A session-start hook loads the project's most important memories into every new session, so the agent starts out knowing them. During a session the agent calls memory_retrieve with plain questions. Recall runs locally with keyword ranking (BM25 with stemming) and never calls the LLM, so it is fast and works offline.

  • Scope. Memories belong to the current project. Preferences that apply everywhere can be saved as global (scope: "global") and are recalled in every project. One project can never read or delete another's memories.

  • Safety. Agents don't get bulk or destructive tools, secrets are never to be stored (the agents are told so), and the LLM key stays in ~/.memstack/config.json (readable only by you), never in agent configs.

Commands

Command

What it does

memstack init

Choose an LLM provider and a store. Verifies the key with a real request and writes ~/.memstack/config.json. Non-interactive: --provider, --base-url, --model, --api-key-env <VAR>, --store, --path, --url, --yes.

memstack connect <claude-code|codex>

Registers MemStack with the agent. Checks the server works first, and undoes everything if a step fails. --dry-run shows the changes; --no-hooks and --no-agents-md skip those parts.

memstack disconnect <claude-code|codex>

Removes everything connect added. Your memories are kept.

memstack status

Config, storage, the current project, and what each agent has connected.

memstack doctor

Diagnoses setup problems and prints the fix for each. --live also tests the LLM key.

memstack memories [query]

Lists or searches this project's memories. --global for global ones, --delete <id> to remove one.

memstack project

Shows this repository's project ID. project pin <id> fixes it in .memstack.json; project merge <old-id> moves memories from an old ID.

What connect changes

connect changes only these entries, backs up each file first, and disconnect restores each file exactly:

Agent

File

Change

Claude Code

~/.claude.json

A user-scope memstack MCP server, added with claude mcp add-json.

Claude Code

~/.claude/settings.json

A SessionStart hook running memstack-mcp hook session-start.

Codex

~/.codex/config.toml

A memstack MCP server, added with codex mcp add.

Codex

~/.codex/hooks.json

A SessionStart hook. Codex runs it after you approve it once with /hooks.

Codex

~/.codex/AGENTS.md

A short block between memstack:begin/memstack:end markers telling Codex to save memories with memory_store.

The registered command is the memstack-mcp you installed, run by absolute path, so it works even when the agent starts without your shell's PATH. Restart Claude Code, or start a new Codex session, to load it.

Projects

The project ID comes from the repository itself, with nothing stored, so it stays the same across clones, worktrees, renamed remotes, moved folders, new machines, and storage switches:

  1. a pinned ID in .memstack.json at the repository root (memstack project pin <id>; commit it to share it with your team);

  2. otherwise, the repository's first commit;

  3. for shallow clones, the origin remote;

  4. for repositories without commits, the git directory, and outside git, the folder. Memories saved before a repository's first commit move to the new ID automatically.

A fork shares its first commit with the original repository, so on one store the two share memories until one of them runs memstack project pin.

Storage

Any supported store works; pick it in memstack init. MemStack never installs storage drivers: install the one you need alongside @memstack/mcp.

Store

Driver to install

Notes

sqlite

better-sqlite3@^11.10.0

Default; one local file, safe for both agents at once.

postgres

postgres@^3.4.9

Shared across machines.

redis

ioredis@^5.11.1

disk, markdown

none

Single process only; doctor warns when both agents use them.

Switching stores keeps project IDs; move memories with memstack export and memstack import.

Troubleshooting

Run memstack doctor first; it checks the config file and its permissions, the LLM key, the storage driver, each agent's registration, hook, and guidance, and starts the server to confirm it answers.

  • "memstack-mcp is not installed" or a missing driver: run the npm install -g … command it prints.

  • Codex doesn't load memories at session start: approve the MemStack hook in Codex with /hooks.

  • Codex doesn't save when asked: check memstack status shows the AGENTS.md guidance as present; reconnect if not.

  • Memories seem missing: memstack project shows which project you are in. Use memstack project merge <old-id> to bring memories from an old ID.

Details on the MCP server itself: docs/MCP_SETUP.md.


The Memory Pipeline

MemStack's core is a five-stage pipeline. Each stage can be used independently.

1. Store

Every agent interaction becomes a Memory with metadata that controls how it's retrieved, summarized, and pruned later.

interface Memory {
  id: string;
  actorId: string;               // Who this memory belongs to (user ID, agent ID, session ID)
  memoryType: MemoryType;        // "interaction" | "summary" | "observation" | "fact" | "reflection" | "preference" | "decision" | "instruction"
  content: string;               // The actual text
  importance: number;            // 0-1 — higher = survives pruning, ranks higher in retrieval
  emotionalValence: number;      // -1 to 1 — for tone-aware retrieval
  tags: string[];                // Filter by tag: "bug", "billing", "urgent", etc.
  embedding?: number[];          // Computed automatically if embedding adapter is configured
  metadata?: Record<string, unknown>;  // Your custom fields
  expiresAt?: Date;              // Auto-pruned after this date
  sourceId?: string;             // Link back to the originating event
  createdAt: Date;
}
// Simple store
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
  tags: ["billing", "refund"],
});

// Batch store — embeddings are batched into one API call for efficiency
await ms.memory.storeBatch([
  { actorId: "agent-7", content: "First interaction" },
  { actorId: "agent-7", content: "Second interaction" },
  { actorId: "agent-7", content: "Third interaction" },
]);

2. Retrieve

Pull back what's relevant — by keyword, by meaning (semantic), by recency, or by importance.

const memories = await ms.memory.retrieve({
  actorId: "agent-7",              // Scope to one actor
  query: "refund policy",          // What to search for
  strategy: "hybrid",              // How to rank: "recent" | "important" | "semantic" | "hybrid"
  limit: 10,                       // Max results
  memoryTypes: ["interaction"],    // Only certain types
  tags: ["billing"],               // Only certain tags
});

Strategy behavior:

Strategy

Sorts by

Requires embeddings

Best for

recent

Newest first

No

Knowing what just happened

important

Highest importance first

No

Filtering noise, keeping signal

semantic

Cosine similarity to query

Yes

"Find memories about X"

hybrid

Semantic + importance blend

Yes

Best of both worlds

No embedding adapter? semantic and hybrid fall back to keyword matching + importance sort. No API costs, just less precise.

3. Compile Context

compileContext() takes retrieval results and assembles an LLM-ready system prompt — deduplicated, sorted by recency and importance, with a token estimate so you know the cost before calling the LLM.

const ctx = await ms.memory.compileContext({
  actorId: "agent-7",
  maxTokens: 2000,               // Budget — assembler stops when it hits this
  memoryTypes: ["interaction", "summary"],
});

// ctx.systemPrompt:
// ## Important Memories
// - The customer has been attempting login for 3 days. (importance: 0.85)
// - Refund was processed for order #4521 on Jan 12. (importance: 0.72)
// 
// ## Recent Interactions
// - Customer asked about refund policy for Q2 purchases.
// - Customer reported login error 503 on Chrome 125.

console.log(ctx.tokenEstimate);  // ~280

// Inject into your LLM call
const currentMessage = "The user is asking about their refund status.";
const response = await llm.complete({
  system: ctx.systemPrompt,
  user: currentMessage,
});

console.log(response.text);

compileContext() handles deduplication, token budgeting, and splits context into important-vs-recent sections. Without it, you'd be concatenating raw retrieval results and risking context-window overflow.

4. Summarize

When an actor has hundreds of interactions, retrieval gets expensive and context gets bloated. Summarization compresses old interactions into a single paragraph using the configured LLM.

const { summary, deletedCount } = await ms.memory.summarize({
  actorId: "agent-7",
  olderThan: new Date(Date.now() - 7 * 86400000),  // Older than 7 days
  skipMostRecent: 10,        // Never touch the 10 most recent
  targetCount: 50,           // Summarize at most 50 memories
  memoryTypes: ["interaction"],
  keepOriginals: false,      // Delete originals after summary
});

// summary.content:
// "Over the past week, the customer reported recurring login failures (error 503)
//  on Chrome 125. Multiple troubleshooting attempts including cache clearing and 
//  password reset were unsuccessful. A refund was processed for order #4521."

console.log(deletedCount);   // 47 — 47 interactions compressed into 1 summary memory

Auto-summarization: Set summarizationThreshold in config (default: 100). Every 100th interaction for an actor triggers summarization automatically.

Warning: keepOriginals: false deletes the summarized memories. Set keepOriginals: true to preserve them alongside the summary.

Custom summarization prompt:

const ms = new MemStack({
  llm,
  defaults: {
    summarizationPrompt:
      "You are an enterprise support memory compressor. Highlight: customer name,
       product, severity, resolution status, and any open issues.",
  },
});

5. Prune

Not all memories deserve to live forever. Pruning removes low-value memories to keep storage and retrieval fast.

// Remove memories older than 30 days
await ms.memory.prune({ type: "byAge", maxAge: 30 * 86400000 });

// Keep only memories above importance 0.3
await ms.memory.prune({ type: "byImportance", minImportance: 0.3 });

// Keep at most 500 memories per actor
await ms.memory.prune({ type: "byCount", maxPerActor: 500 });

// Remove specific types
await ms.memory.prune({ type: "byType", memoryTypes: ["observation"] });

// Custom logic
await ms.memory.prune({
  type: "custom",
  shouldRemove: (memory) => memory.content.includes("[RESOLVED]"),
});

// Dry run first — see what would be removed
const { wouldPrune, count } = await ms.memory.dryRunPrune({
  type: "byAge",
  maxAge: 86400000,
});
console.log(`Would remove ${count} memories:`, wouldPrune);

Auto-prune on every process() call by setting pruneStrategy in config:

const ms = new MemStack({
  llm,
  defaults: {
    pruneStrategy: { type: "byImportance", minImportance: 0.05 },
  },
});

Real-World Use Cases

Support Agent

// detectUrgency and classifyIntent are your own business logic.
// They could be simple keyword matchers, regex, or an LLM call.
function detectUrgency(msg: string): number {
  if (msg.match(/urgent|asap|immediately/i)) return 0.9;
  if (msg.match(/error|fail|broken/i)) return 0.7;
  return 0.5;
}

function classifyIntent(msg: string): string[] {
  const tags: string[] = [];
  if (msg.match(/bill|refund|charge|payment/i)) tags.push("billing");
  if (msg.match(/error|bug|fail|crash/i)) tags.push("bug");
  if (msg.match(/login|password|account/i)) tags.push("account");
  return tags;
}

// Every customer message becomes a memory
async function handleMessage(customerId: string, message: string) {
  await ms.memory.store({
    actorId: `customer:${customerId}`,
    content: message,
    importance: detectUrgency(message),
    tags: classifyIntent(message),
  });

  // Retrieve everything relevant to this customer's history
  const ctx = await ms.memory.compileContext({
    actorId: `customer:${customerId}`,
    maxTokens: 1500,
  });

  const response = await llm.complete({
    system: `You are a support agent. Customer history:\n${ctx.systemPrompt}`,
    user: message,
  });

  return response.text;
}

// Every 100th interaction, old history auto-compresses.
// A customer with 10,000 messages still fits in a $0.02 LLM call.

RAG Pipeline

// Suppose you have documents from your knowledge base
const documents = [
  { text: "Authentication uses JWT tokens with 15-minute expiry.", url: "/docs/auth", section: "security" },
  { text: "Refunds are processed within 5-10 business days.", url: "/docs/billing", section: "billing" },
];

// Index documents as observation memories
for (const doc of documents) {
  await ms.memory.store({
    actorId: "knowledge-base",
    content: doc.text,
    memoryType: "observation",
    metadata: { source: doc.url, section: doc.section },
  });
}

// Query with semantic search
const relevantDocs = await ms.memory.retrieve({
  actorId: "knowledge-base",
  query: "How does authentication work?",
  strategy: "semantic",
  limit: 5,
});

const ctx = await ms.memory.compileContext({
  actorId: "knowledge-base",
  memoryTypes: ["observation"],
});

// Prompt the LLM with retrieved context
const answer = await llm.complete({
  system: `Answer using only these documents:\n${ctx.systemPrompt}`,
  user: "How does authentication work?",
});

Multi-User Chatbot

// Each user gets their own memory space
async function chat(userId: string, message: string) {
  await ms.memory.store({
    actorId: userId,
    content: message,
  });

  const ctx = await ms.memory.compileContext({
    actorId: userId,
    maxTokens: 1000,
  });

  return llm.complete({
    system: `You are a friendly assistant. Conversation history with this user:\n${ctx.systemPrompt}`,
    user: message,
  });
}

// Get stats
const total = await ms.memory.count();
const userCount = await ms.memory.count({ actorId: "user-42" });

Memory Type Reference

Type

Purpose

Example

interaction

Default. Direct exchanges between agent and user/other agent.

"User asked about billing."

summary

Compressed collection of old interactions. Created by summarize().

"Over 3 weeks, user reported 5 login failures..."

observation

Passive knowledge — facts, documents, things the agent knows but didn't interact with.

"Company refund policy is 30 days from purchase."

fact

Verified knowledge — discrete truths the agent has confirmed.

"The user's subscription tier is Enterprise."

reflection

Self-generated insight — the agent thinking about its own experiences.

"I tend to over-explain billing policies — should be more concise."

preference

How the user likes things done.

"Prefer small pull requests with one concern each."

decision

A choice made, ideally with its reason.

"Chose Hono over Express for edge runtime support."

instruction

A standing rule to follow.

"Never commit directly to main."

Types control retrieval behavior — compileContext() treats interaction and summary differently from observation. Use types to separate "what happened" from "what I know."


Retrieval Strategies

Four strategies, each with a purpose:

// "What just happened?" — most recent first
await ms.memory.retrieve({ actorId: "x", strategy: "recent", limit: 3 });

// "What matters most?" — highest importance, ignoring age
await ms.memory.retrieve({ actorId: "x", strategy: "important" });

// "What relates to this query?" — cosine similarity search (needs embeddings)
await ms.memory.retrieve({ actorId: "x", query: "login bug", strategy: "semantic" });

// "Balance relevance and importance" — semantic + importance blend
await ms.memory.retrieve({ actorId: "x", query: "login bug", strategy: "hybrid" });

Choosing a strategy:

  • Use recent for chatbots, ongoing conversations, anything time-sensitive

  • Use important for long-running agents where signal-to-noise matters

  • Use semantic for RAG, document search, knowledge base queries

  • Use hybrid for most agent memory — it balances meaning with significance

Keyword recall on any storage adapter

LexicalRetriever answers natural questions without embeddings or an LLM call, and behaves the same on every storage adapter. It loads the memories in the given actors through retrieve(), then ranks them in MemStack with BM25 over content and tags, with stemming and prefix matching. When nothing matches, it returns the most important memories instead.

import { LexicalRetriever } from "@memstack/core";

const retriever = new LexicalRetriever(storage);
const { hits, fallback } = await retriever.recall({
  actorIds: ["project:abc", "global"], // Searched together
  query: "What framework does this project use?",
  limit: 10,                           // Max results
  maxChars: 8000,                      // Max total content; the top hit is always returned
});

Up to 2,000 memories per actor are ranked (candidateLimit). Only returned memories are marked as accessed.


Embeddings

Embeddings power semantic search. They're optional — without them, retrieval uses keyword matching.

With embeddings vs Without embeddings

With embeddings (embedding adapter configured):

import { MemStack, OpenAILLMAdapter, OpenAIEmbeddingAdapter, InMemoryStorageAdapter } from "@memstack/core";

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  embedding: new OpenAIEmbeddingAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
});

// store() computes a 1536-dim vector automatically
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
});

// retrieve() with "semantic" or "hybrid" uses cosine similarity
// Query: "refund" finds the refund policy memory even though the word "refund"
// appears differently across stored memories.
const results = await ms.memory.retrieve({
  actorId: "agent-7",
  query: "how do I get my money back",
  strategy: "semantic",
});
// Matches "Customer asked about refund policy" — semantic match, not keyword match.

Without embeddings (no embedding adapter):

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: process.env.OPENAI_API_KEY! }),
  storage: new InMemoryStorageAdapter(),
  // no embedding adapter
});

// store() works identically, just no vector computed
await ms.memory.store({
  actorId: "agent-7",
  content: "Customer asked about refund policy for Q2 purchases.",
});

// retrieve() with "semantic" or "hybrid" falls back to keyword matching
// plus importance/recency sorting. No API costs, no setup required.
const results = await ms.memory.retrieve({
  actorId: "agent-7",
  query: "refund",
  strategy: "hybrid", // falls back to keyword + importance
});
// Still works — finds "refund" via substring match. Less precise for
// paraphrased queries ("money back" won't match "refund").

Batch embedding: storeBatch() sends all texts in one embedding API call, reducing cost and latency.

// Disable auto-embedding if you only need keyword search
const ms = new MemStack({
  llm,
  embedding: new OpenAIEmbeddingAdapter({ apiKey }),
  defaults: { embedOnStore: false },
});

Vector dimensions and model compatibility

Different embedding models produce vectors of different lengths. Cosine similarity only works between vectors of the same dimension. If you change embedding models, existing vectors become incompatible — they can't be compared to new ones.

Adapter

Default model

Dimensions

OpenAIEmbeddingAdapter

text-embedding-3-small

1536

OpenAIEmbeddingAdapter

text-embedding-3-large

3072

CohereEmbeddingAdapter

embed-english-v3.0

1024

CohereEmbeddingAdapter

embed-english-light-v3.0

384

CohereEmbeddingAdapter

embed-english-v2.0

4096

CohereEmbeddingAdapter

embed-multilingual-v3.0

1024

What happens if dimensions don't match: If you store memories with one model (e.g., 1536 dims) then switch to another model (e.g., 1024 dims), the storage adapter receives query vectors and stored vectors of different lengths. Cosine similarity between vectors of different dimensions is undefined — results depend on the storage backend's behavior. Most will either error, return empty results, or produce meaningless scores.

Recommendation: Pick one embedding model per storage instance and stick with it. If you need to switch models, create a new storage instance and re-embed from scratch.

DeepSeek users: DeepSeek has no embeddings API. If you use DeepSeek as your LLM, you must either:

  1. Omit the embedding adapter and use "recent" or "important" retrieval strategies (no API costs, less precise)

  2. Pair DeepSeek with a separate embedding provider (e.g., OpenAI for embeddings, DeepSeek for chat)


Adapters

MemStack is provider-agnostic. Every boundary is an interface — bring your own LLM, embedding model, and storage backend.

LLM Adapters

Used by summarize() and compileContext(). Ships with OpenAI, Anthropic, Ollama, and Groq built-in — and via baseURL, the OpenAI adapter works with any OpenAI-compatible API (DeepSeek, Mistral, Gemini, Together AI, Perplexity, Fireworks, xAI, and dozens more).

// OpenAI
import { OpenAILLMAdapter } from "@memstack/core";
const llm = new OpenAILLMAdapter({ apiKey: "..." });

// Any OpenAI-compatible API — just change baseURL
const deepseek = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.deepseek.com/v1" });
const mistral = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.mistral.ai/v1" });
const together = new OpenAILLMAdapter({ apiKey: "...", baseURL: "https://api.together.xyz/v1" });

// Anthropic
import { AnthropicLLMAdapter } from "@memstack/core";
const llm = new AnthropicLLMAdapter({
  apiKey: process.env.ANTHROPIC_API_KEY!,
  defaultModel: "claude-sonnet-4-5-20250929",
});

// Ollama (built-in)
import { OllamaLLMAdapter } from "@memstack/core";
const llm = new OllamaLLMAdapter({
  baseURL: "http://localhost:11434",
  defaultModel: "llama3.2",
});

Embedding Adapters

Used by semantic retrieval. Ships with OpenAI and Cohere built-in — and via baseURL, the OpenAI adapter works with any OpenAI-compatible embedding API (Together AI, Voyage AI, Jina, Nomic, and more).

import { OpenAIEmbeddingAdapter, CohereEmbeddingAdapter } from "@memstack/core";

// OpenAI
new OpenAIEmbeddingAdapter({ apiKey: "...", model: "text-embedding-3-small" }); // 1536 dims

// Cohere
new CohereEmbeddingAdapter({ apiKey: "..." }); // embed-english-v3.0, 1024 dims

// Any OpenAI-compatible embedding API
new OpenAIEmbeddingAdapter({ apiKey: "...", baseURL: "https://api.voyageai.com/v1", model: "voyage-3" });

Storage Adapters

MemStack contains 18 storage-adapter implementations. Twelve are exported from @memstack/core; six remain experimental source implementations. Core has no runtime dependencies, and database clients are injected by callers.

Support levels:

  • Production-ready means exported from the public package, covered by unit tests, and supported as part of the public API.

  • Real-service E2E verified means the adapter also passes against its actual database implementation in pnpm test:e2e.

  • Mock-tested means unit coverage uses an injected fake client rather than a live cloud service.

  • Experimental means implemented in source but not exported from the published package.

Public package exports

Built-in (zero external deps):

Adapter

Backend

Use case

InMemoryStorageAdapter

In-memory Map

Testing, prototyping

DiskStorageAdapter

Local JSON files

Simple local persistence

MarkdownStorageAdapter

Append-only .md files

Human-readable, git-diffable, debug-friendly

HybridStorageAdapter

Compose any two StorageProviders

Cache + durable, edge + durable

Relational / SQL:

Adapter

Backend

Vector search

PostgresStorageAdapter

PostgreSQL + pgvector

HNSW native

SQLiteStorageAdapter

SQLite (better-sqlite3)

Cosine in-memory

Vector databases:

Adapter

Backend

QdrantStorageAdapter

Qdrant

WeaviateStorageAdapter

Weaviate

LanceDBStorageAdapter

LanceDB

MongoDBStorageAdapter

MongoDB Atlas Vector Search

Cache / KV:

Adapter

Backend

RedisStorageAdapter

Redis (ioredis)

Graph:

Adapter

Backend

Neo4jStorageAdapter

Neo4j

Experimental (mock-tested or missing an optional E2E capability)

These implementations are available to source contributors but are not part of the published package API.

Adapter

Backend

Blocker

TursoStorageAdapter

Turso (libsql)

Cloud-only (needs Turso account)

ChromaStorageAdapter

ChromaDB

Embedding function dependency

PineconeStorageAdapter

Pinecone

Cloud-only (needs API key)

UpstashStorageAdapter

Upstash Redis + Vector

Cloud-only (needs API key)

Mem0StorageAdapter

Mem0 OSS or Cloud

Cloud-only (needs API key)

ZepStorageAdapter

Zep Cloud or CE

Cloud-only (needs API key)

Live cloud compatibility remains unverified for Pinecone, Upstash, Mem0, Zep, and Turso. Chroma's real-client E2E suite is skipped when its optional default embedding function is unavailable. LLM and embedding-provider tests use mocks; live-provider testing is opt-in and is not part of CI.

Quick-start per backend:

// Postgres
import { PostgresStorageAdapter } from "@memstack/core";
const storage = new PostgresStorageAdapter({ connectionString: "postgres://..." });

// Redis
import Redis from "ioredis";
import { RedisStorageAdapter } from "@memstack/core";
const storage = new RedisStorageAdapter({ redis: new Redis() });

// Markdown (append-only, human-readable)
import { MarkdownStorageAdapter } from "@memstack/core";
const storage = new MarkdownStorageAdapter({ dir: "./memories" });

// Hybrid (Redis cache + Postgres durable)
import { HybridStorageAdapter } from "@memstack/core";
const storage = new HybridStorageAdapter({
  cache: new RedisStorageAdapter({ redis: new Redis() }),
  durable: new PostgresStorageAdapter({ connectionString: "postgres://..." }),
});

Custom storage:

import type { StorageProvider, MemoryStoreInput } from "@memstack/core";

class MyStorage implements StorageProvider {
  async store(input: MemoryStoreInput): Promise<Memory> { /* ... */ }
  async get(id: string): Promise<Memory | null> { /* ... */ }
  async retrieve(query: MemoryRetrieveQuery, embedding?: number[]): Promise<Memory[]> { /* ... */ }
  async count(filter?: MemoryCountFilter): Promise<number> { /* ... */ }
  async delete(id: string): Promise<void> { /* ... */ }
  async deleteMany(ids: string[]): Promise<number> { /* ... */ }
  async storeBatch(inputs: MemoryStoreInput[]): Promise<Memory[]> { /* ... */ }
  async initialize(): Promise<void> { /* ... */ }
  async close(): Promise<void> { /* ... */ }
}

Backend Comparison

Backend

Vector search

Touch

Status

InMemory

Cosine in-memory

Yes

✅ Production

Disk (JSON)

Keyword + importance

Yes

✅ Production

Markdown

Keyword + importance

No

✅ Production

Postgres

pgvector HNSW

Yes

✅ Production

Redis

RediSearch KNN (auto-detect)

Yes

✅ Production

Qdrant

ANN native

No

✅ Production

Weaviate

BM25 + vector hybrid

No

✅ Production

LanceDB

DiskANN native

No

✅ Production

MongoDB

Atlas Vector Search

No

✅ Production

Neo4j

Neo4j vector index

No

✅ Production

Hybrid

Delegates to cache/durable

If durable supports

✅ Production

SQLite

Cosine in-memory

Yes

✅ Production


Full API Reference

MemStack Client

import { MemStack } from "@memstack/core";

const ms = new MemStack({
  llm: LLMProvider,                    // Required — for summarization
  embedding?: EmbeddingProvider,       // Optional — for semantic search
  storage?: StorageProvider,           // Optional — defaults to InMemoryStorageAdapter
  defaults?: {
    summarizationThreshold?: number,   // Auto-summarize every N process() calls. Default: 100
    embedOnStore?: boolean,            // Auto-embed on store(). Default: true
    pruneStrategy?: PruneStrategy,     // Auto-prune during process() (throttled). Default: disabled
    pruneInterval?: number,            // Run auto-prune every N process() calls. Default: 100
    autoImportance?: boolean,          // LLM-score importance in process() when not provided. Default: false
    autoTags?: boolean,                // LLM-extract tags in process() when not provided. Default: false
    summarizationPrompt?: string,      // Custom prompt for the summarizer
  },
  hooks?: {
    onMemoryStored?: (memory: Memory) => void;
    onMemoryPruned?: (ids: string[]) => void;
    onSummaryCreated?: (summary: Memory, deletedCount: number) => void;
    onError?: (error: Error, context: string) => void;
  },
});

Auto-behaviors run inside process(), not store(). process() tracks a per-actor call count: summarization fires every summarizationThreshold calls, and pruning fires every pruneInterval calls (when pruneStrategy is set). store() is the low-level write and never triggers these.

Memory Subsystem

All methods accessible via ms.memory.*:

// Store
ms.memory.store(input: MemoryStoreInput): Promise<Memory>
ms.memory.storeBatch(inputs: MemoryStoreInput[]): Promise<Memory[]>

// Retrieve
ms.memory.retrieve(query: MemoryRetrieveQuery): Promise<Memory[]>
ms.memory.get(id: string): Promise<Memory | null>

// Context assembly
ms.memory.compileContext(options: ContextOptions): Promise<CompiledContext>

// Lifecycle
ms.memory.summarize(options: SummarizeOptions): Promise<{ summary: Memory; deletedCount: number }>
ms.memory.prune(strategy: PruneStrategy): Promise<{ pruned: string[]; count: number }>
ms.memory.dryRunPrune(strategy: PruneStrategy): Promise<{ wouldPrune: string[]; count: number }>

// Management
ms.memory.count(filter?: MemoryCountFilter): Promise<number>
ms.memory.delete(id: string): Promise<void>
ms.memory.deleteMany(ids: string[]): Promise<number>
ms.memory.touch(id: string): Promise<void>
ms.memory.purgeActor(actorId: string): Promise<number>
ms.memory.merge(ids: string[]): Promise<Memory>
ms.memory.stats(actorId?: string): Promise<MemoryStats>
ms.memory.summarizeStream(options: SummarizeOptions): AsyncIterable<{ chunk: string; text: string }>

Export / Import

Snapshot and restore full state for persistence, backups, or migration:

import * as fs from "node:fs";

// Save
const snapshot = await ms.export();
fs.writeFileSync("state.json", JSON.stringify(snapshot, null, 2));

// Restore
const data = JSON.parse(fs.readFileSync("state.json", "utf-8"));
await ms2.import(data);

Each memory's original createdAt is preserved on import, so export → import is a lossless round-trip — safe for backups and cross-backend migration (e.g. disk → Postgres). All storage adapters honor a createdAt supplied on store()/storeBatch(); when omitted, they default to the current time.

Health & Close

const status = await ms.health();
// { storage: true, llm: true, embedding: true }

await ms.close(); // graceful shutdown

Harness Memory API

The harness features are built on HarnessMemory, which works with any storage adapter. Use it to build your own agent integration:

import { HarnessMemory, defaultRecallNamespaces, projectNamespace } from "@memstack/core";

const memory = new HarnessMemory({ storage, llm });

await memory.remember({
  namespace: projectNamespace("acme-api"),   // or "global"
  content: "This project uses Hono",
  kind: "decision",                           // fact | preference | decision | instruction | observation | ...
  source: { harness: "my-agent", project: "acme-api" },
});

const { hits, fallback } = await memory.recall({
  namespaces: defaultRecallNamespaces("acme-api"), // project, then global
  query: "Which framework do we use?",
  limit: 10,
  maxChars: 8000,
});

await memory.get(id, namespaces);       // null outside namespaces
await memory.forget(id, namespaces);    // refuses ids outside namespaces
await memory.stats(namespaces);         // { "project:acme-api": 3, global: 1 }
await memory.moveNamespace(from, to);   // e.g. when a project's ID changes

remember asks the LLM for topic tags (disable with autoTags: false); every other method makes no LLM call. recall uses LexicalRetriever.


Configuration

const ms = new MemStack({
  llm: new OpenAILLMAdapter({ apiKey: "..." }),

  // Defaults control auto-behavior (all applied during process())
  defaults: {
    summarizationThreshold: 50,      // Summarize every 50 process() calls (default: 100)
    embedOnStore: false,             // Don't auto-embed — saves API costs
    pruneStrategy: {                 // Auto-clean during process(), throttled by pruneInterval
      type: "byAge",
      maxAge: 90 * 86400000,         // 90 days
    },
    pruneInterval: 100,              // Run the prune check every 100 process() calls (default: 100)
    autoImportance: true,            // Let the LLM score importance when you don't pass one
    autoTags: true,                  // Let the LLM extract tags when you don't pass any
  },

  // Hooks for observability
  hooks: {
    onMemoryStored: (m) => logger.debug("memory:stored", { id: m.id, actor: m.actorId }),
    onMemoryPruned: (ids) => logger.info("memory:pruned", { count: ids.length }),
    onSummaryCreated: (summary, n) => logger.info("memory:summarized", { count: n }),
    onError: (err, context) => logger.error("memory:error", { context, message: err.message }),
  },
});

Harness configuration file

The CLI and the MCP harness profile read ~/.memstack/config.json (or $MEMSTACK_HOME/config.json), which memstack init writes with permissions readable only by you:

{
  "version": 1,
  "llm": { "provider": "openai-compatible", "apiKey": "…", "baseURL": "https://api.deepseek.com", "model": "deepseek-flash" },
  "storage": { "type": "sqlite", "path": "/Users/you/.memstack/memstack.db" }
}

Environment variables override the file one section at a time: any LLM variable (OPENAI_API_KEY, ANTHROPIC_API_KEY, MEMSTACK_OPENAI_BASE_URL, MEMSTACK_LLM_MODEL) replaces the whole llm section, and MEMSTACK_STORAGE replaces the whole storage section, so a key is never sent to another provider's URL.


Advanced Usage

Custom Storage

Implement StorageProvider for any database. The interface is 9 methods. See the reference section above for the full contract.

Optional members, none of them required:

  • capabilities: { multiProcess?, textSearch? } declares whether several processes can share the store safely and whether search() is native.

  • search(query) provides native full-text search. LexicalRetriever uses it when textSearch is declared and ranks memories itself otherwise.

  • retrieve() should honor touch: false by returning memories without marking them as accessed.

SQLiteStorageAdapter enables WAL and a 5-second busy timeout so several processes can share one database file. Set walMode: false or busyTimeoutMs to change this.

Custom LLM / Embedding

Implement LLMProvider or EmbeddingProvider for any service:

import type { LLMProvider } from "@memstack/core";

class TogetherAIAdapter implements LLMProvider {
  async complete(req: { system: string; user: string; model?: string }) {
    const res = await fetch("https://api.together.xyz/v1/chat/completions", {
      headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
      body: JSON.stringify({ model: req.model, messages: [{ role: "system", content: req.system }, { role: "user", content: req.user }] }),
    });
    const data = await res.json() as any;
    return { text: data.choices[0].message.content, tokens: { prompt: data.usage.prompt_tokens, completion: data.usage.completion_tokens, total: data.usage.total_tokens } };
  }
}

Event Hooks

Monitor memory operations without modifying code:

const ms = new MemStack({
  llm,
  hooks: {
    onMemoryStored: (m) => metrics.increment("memory.stored"),
    onSummaryCreated: (_, n) => metrics.gauge("memory.summarized_count", n),
    onMemoryPruned: (ids) => metrics.increment("memory.pruned", ids.length),
  },
});


Development

Setup & Tests

git clone https://github.com/isiomaC/memstack.git
cd memstack
pnpm install

pnpm test             # 407 core tests, no external services needed
pnpm test:packages    # 78 package tests after dependency-ordered builds
pnpm test:e2e         # 80 pass, 1 optional Chroma skip (requires Docker services)
pnpm test:e2e:run     # Start services, run E2E once, preserve failure logs, clean up
pnpm smoke:artifacts  # Built core, CLI, MCP, and server black-box checks
pnpm smoke:packages   # Pack and install publishable tarballs in a clean project
pnpm smoke:docker     # Build and exercise the server image
pnpm verify           # Complete local verification pipeline
pnpm test:watch       # Watch core tests
pnpm build:all        # Build core and all workspace packages
pnpm check:all        # Type-check core and all workspace packages

CI exposes a stable verification job. Configure that job as a required status check in GitHub branch protection for main.

Debugging

Use hooks for observability — MemStack has no built-in logging:

const ms = new MemStack({
  llm,
  hooks: {
    onMemoryStored: (m) => console.debug("[memstack] stored:", m.id, m.content.slice(0, 80)),
    onMemoryPruned: (ids) => console.debug("[memstack] pruned:", ids.length),
  },
});

Common issues:

Symptom

Cause

Fix

CONFIG_ERROR: LLM provider is required

No LLM adapter

Pass any LLMProvider to config

Empty retrieval results

Wrong actorId or no memories stored

Check await ms.memory.count({ actorId })

Semantic search not working

No embedding adapter or embedOnStore: false

Add embedding adapter or use strategy: "recent"

High memory usage in production

Using InMemoryStorageAdapter

Implement StorageProvider for Postgres/Redis/etc

Poor summarization quality

Default prompt doesn't match your domain

Use summarizationPrompt in defaults config

Inspecting state at runtime:

// How much data do we have?
const total = await ms.memory.count();
const perActor = await ms.memory.count({ actorId: "user-42" });

// What does one actor's memory look like?
const snapshot = await ms.export();
const actorMemories = snapshot.memories.filter(m => m.actorId === "user-42");
console.log(`User-42: ${actorMemories.length} memories`);
actorMemories.forEach(m => console.log(`  [${m.memoryType}] ${m.content.slice(0, 60)} (imp: ${m.importance})`));

Publishing to npm

# Bump version, then:
pnpm build && pnpm check && pnpm test
npm login
npm publish --access public

The @memstack scope requires --access public.


Contributing

Most needed contributions:

  • LLM adapters: Google Gemini (native), Amazon Bedrock, Vertex AI

  • Embedding adapters: local inference (transformers.js, ONNX)

  • Benchmarks: retrieval quality, latency, cost comparisons

  • Python port: pip install memstack

Open an issue or PR at github.com/isiomaC/memstack.


License

MIT © MemStack

Available Tools

18 tools
memory_compile_contextA

Compile memories into an LLM-ready system prompt (markdown formatted, token-budgeted, split into important and recent sections).

ParametersJSON Schema
NameRequiredDescriptionDefault
actorIdNoActor ID. Defaults to the current session actor.
maxTokensNoMaximum tokens for the compiled context. Default: 2000.
memoryTypesNoFilter by memory types

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full responsibility. It mentions the output formatting and structure, suggesting a read-only aggregation operation. It does not explicitly state side effects, but the name and description imply no state changes, providing reasonable transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that efficiently conveys the tool's purpose and output characteristics without redundancy. It is well-structured and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description provides sufficient context for an agent to understand what the tool does and what it returns, given the sibling list. It lacks an explicit comparison to alternatives, but the purpose is clear enough for typical use cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All three parameters have clear descriptions: actorId defaults to current session, maxTokens sets a limit with default, and memoryTypes filters by type. The schema fully covers parameter meaning and defaults, leaving no ambiguity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: compiling memories into an LLM-ready system prompt. It specifies the resource (memories), the action (compile), and the output format (markdown, token-budgeted, split into sections), distinguishing it from sibling memory tools that retrieve, summarize, or store.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for generating a compiled context prompt for LLM interactions, which differentiates it from sibling tools like retrieve or summarize. However, it does not explicitly state when to use this tool over alternatives, leaving some inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_deleteA

Delete a single memory by ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesMemory ID to delete

TDQS

A3.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, and the description omits any note on irreversibility, permissions, or side effects, which are critical for a destructive operation like delete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence with no redundant words, effectively conveying the tool's purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple deletion function with one parameter and no output schema, the description is adequate, though it could benefit from noting the exclusion of batch operations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter 'id' is clearly documented as 'Memory ID to delete', fully covering the schema and providing necessary context.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Delete') and the target ('a single memory') with the identifier ('ID'), making it distinct from bulk operations like delete_many.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

While it describes what it does, it does not explicitly contrast with sibling tools like memory_delete_many or memory_prune, leaving room for ambiguity about optimal usage scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_delete_manyA

Delete multiple memories by ID in one call. Returns the number actually deleted.

ParametersJSON Schema
NameRequiredDescriptionDefault
idsYesMemory IDs to delete

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the burden. It mentions 'Returns the number actually deleted', hinting at partial deletion behavior, but does not disclose irreversibility, error handling, or authentication requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, with only two sentences, and clearly conveys the action and return value without unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple delete operation, the description sufficiently covers the purpose, input, and output (number deleted). No output schema is present, but the return value is explicitly stated, making it complete for this context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already describes the 'ids' parameter as 'Memory IDs to delete', and the description adds no additional semantic detail. Since schema coverage is 100%, a baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool deletes memories by ID in a single call, which is specific and distinguishable from other memory-related tools like memory_prune (which likely deletes by criteria) and memory_purge_actor (which deletes by actor).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when specific memory IDs are known, but it does not explicitly contrast with sibling tools like memory_prune or memory_purge_actor. No guidance on when not to use this tool is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_dry_run_pruneA

Preview what would be pruned by a given strategy WITHOUT actually deleting. Scoped to a single actor — defaults to the current session actor. Returns the memory IDs that would be removed.

ParametersJSON Schema
NameRequiredDescriptionDefault
typeYesPrune strategy type to preview. Note: 'custom' is not available via MCP because it requires a JS function.
maxAgeNoMax age in seconds (for byAge)
actorIdNoActor ID to scope pruning to. Defaults to the current session actor.
maxPerActorNoMaximum memories per actor (for byCount)
memoryTypesNoMemory types to evaluate (for byType)
minImportanceNoMinimum importance (for byImportance)

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description explicitly states that no deletion occurs, which is the primary side-effect concern. It also mentions the return value (memory IDs). Since annotations are absent, this is the sole source of transparency, and it covers the key behavioral aspect.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, consisting of two sentences that efficiently convey the purpose, scope, and output. No unnecessary information or repetition is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description provides sufficient context for a preview tool: it states the output (memory IDs) and the scope (single actor, default). While it does not detail what the agent should do with the returned IDs, that is implied by the preview nature. The lack of an output schema is mitigated by the clear mention of the output type.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All 6 parameters are described with brief but sufficient explanations. The note about 'custom' not being available via MCP adds useful context. The descriptions are clear enough for an agent to understand what each parameter does.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: previewing what would be pruned without actually deleting. It specifies the scope (single actor, defaulting to current session) and the output (memory IDs).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implicitly distinguishes this from the actual prune tool by emphasizing 'WITHOUT actually deleting,' which guides the agent to use this when a dry-run is needed. However, it does not explicitly name the alternative or provide a direct when-to-use statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_exportA

Export a snapshot of memories for backup or migration. Returns { version, memories, exportedAt }.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorIdNoActor ID to export. Defaults to the current session actor.

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description mentions the return fields (version, memories, exportedAt) and implies a non-destructive snapshot operation, but it does not explicitly state that no state is modified. In the absence of annotations, this level of detail is somewhat lacking for full transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, containing only one sentence with no redundant information. It efficiently conveys the operation, purpose, and return structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity (one optional parameter, no output schema), the description is sufficiently complete. It states the operation, its purpose, and the expected return fields, enabling an agent to use it correctly without further context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter actorId is fully described with its meaning and default behavior. Schema coverage is 100%, and the description adds complete semantic clarity beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool exports a snapshot of memories for backup or migration, with a specific verb and resource. It distinguishes itself from sibling tools like memory_import and memory_get by focusing on exporting a full snapshot rather than individual retrieval or ingestion.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides the intended use cases (backup or migration) and notes the optional actorId parameter with a default behavior. It does not explicitly mention when not to use it, but the purpose is sufficiently unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_getB

Get a single memory by ID. Returns null if not found.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesMemory ID

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses one behavioral detail ('Returns null if not found'), which is useful for expected outcomes. However, since annotations are absent, it does not mention side effects, permissions, or error behaviors, leaving some transparency gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, using two short sentences to convey the core functionality and a key return behavior. No redundant or extraneous information is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description lacks an output schema and does not detail the return format beyond the null case. It also omits any context about when to use this tool relative to siblings, leaving some ambiguity for an agent selecting among many memory tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'id' is described as 'Memory ID', which matches the schema's coverage but adds only minimal semantic clarity. With 100% schema coverage, this meets the baseline without adding extra meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Get') and the target resource ('a single memory by ID'). It is specific enough to distinguish from batch operations like memory_get_all, though it does not explicitly name alternative tools for comparison.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool versus others. The description only states what it does, without contextual clues about when it is appropriate or when a sibling like memory_retrieve might be preferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_healthA

Check the health of storage, LLM, and embedding connections.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It implies read-only behavior but does not explicitly state side effects, network calls, or potential latency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single concise sentence with no repetition or extraneous words. Information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Simple tool with no output schema; the description covers its purpose adequately. Lacks any mention of output format or typical usage context, but not critical for a health check.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist, so schema coverage is trivially complete. The description adds nothing about parameters but none are needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb 'Check' and specific resources (storage, LLM, embedding connections). Easily distinguished from sibling tools that operate on memory data rather than health.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to invoke this tool versus others, nor any preconditions or typical use cases mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_importA

Import memories from a snapshot previously produced by memory_export. Restores each memory as-is, including its original ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
memoriesYesMemories to import, as produced by memory_export

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the transparency burden. It discloses that memories are restored as-is including original IDs, which is useful, but does not mention behavior on ID conflicts, overwrites, or failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no fluff, and the core purpose is front-loaded. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple single-parameter tool with no output schema, the description covers the essential behavior. Minor gaps like conflict handling or error semantics are not covered but are not critical for basic usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the parameter is well-documented. The description adds value beyond the schema by specifying that restoration preserves original IDs and is 'as-is', which the schema does not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (import) and the resource (memories from a snapshot produced by memory_export). It distinguishes itself from siblings like memory_store or memory_store_batch by specifying the source format and the 'as-is' restoration with original IDs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is used after memory_export to restore a snapshot. It provides clear context but does not explicitly mention when not to use it or alternative tools for individual memory operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_mergeA

Merge multiple memories into one. Uses the highest-importance memory as the base and appends content from the rest.

ParametersJSON Schema
NameRequiredDescriptionDefault
idsYesMemory IDs to merge (at least 2)

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses the merge behavior (highest-importance base, appending content) which adds meaningful behavioral context. However, it does not disclose whether the source memories are deleted, modified, or preserved, nor does it mention side effects, error conditions, or return value. With no annotations, these omissions are significant, but the core behavior is at least transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no wasted words. The purpose is front-loaded ('Merge multiple memories into one') and the algorithmic detail is given concisely in the second sentence. This is an appropriately sized and well-structured description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is missing critical context for an agent to fully understand the consequences: it does not state what happens to the original memories, what the return value is, or what 'importance' refers to. With no annotations and no output schema, these gaps are significant, making the description incomplete for a merge operation that likely has side effects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents the 'ids' parameter with a description indicating at least 2 IDs, so schema coverage is 100%. The tool description does not add extra semantic detail about the parameter (e.g., order, validity, duplication), so it remains at the baseline expected when the schema covers the parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Merge multiple memories into one') and the resource, and it distinguishes itself from other memory tools by mentioning the specific merge algorithm (highest-importance base, appending content). It leaves no ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when combining memories but does not explicitly contrast it with alternatives like memory_summarize or memory_compile_context, nor does it state when not to use it. This leaves the agent to infer the appropriate context, which is a moderate gap given the many sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_processA

Store a memory with auto-enrichment (importance scoring, tag extraction, auto-summarization, auto-pruning). This is the primary method for recording interactions.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoTags for categorization. Auto-extracted if omitted.
actorIdNoActor ID. Defaults to the current session actor.
contentYesThe memory content text
metadataNoAdditional metadata key-value pairs
importanceNoImportance score (0.0-1.0). Auto-scored if omitted.
memoryTypeNoMemory type (interaction, summary, observation, fact, reflection, preference, decision, instruction). Default: interaction.

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it does disclose genuinely useful behavior: auto-enrichment steps and, notably, auto-pruning as a side effect of a store operation. However, it does not say what auto-pruning deletes, whether the call is synchronous, whether enrichment can fail, or what permissions are needed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with the action front-loaded and the enrichment behaviors itemized in a parenthetical. No filler, and the 'primary method' claim is placed where it reinforces the purpose statement.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with six parameters of nested/loose typing and no annotations or output schema, the description covers what the tool does but leaves key gaps: no return-value guidance, no guidance on the destructive auto-prune side effect's scope, and no resolution of the overlap with memory_store and memory_store_batch.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents every parameter including the auto-extraction and auto-scoring defaults. The description's mentions of 'importance scoring' and 'tag extraction' merely restate what the schema says, adding no syntax, range, or format detail beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Store a memory') and adds the differentiator of auto-enrichment (importance scoring, tag extraction, summarization, pruning). However, it never distinguishes itself from the sibling memory_store or memory_store_batch, which appear to do overlapping work, so an agent cannot tell which to pick from the description alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'This is the primary method for recording interactions' implies a default choice, which is some guidance. But it names no alternatives, no when-not conditions, and never explains why an agent would use this over memory_store or memory_store_batch despite those siblings existing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_pruneA

Prune (delete) memories matching the given strategy. Scoped to a single actor — defaults to the current session actor. Returns the pruned memory IDs and count. Handle with care.

ParametersJSON Schema
NameRequiredDescriptionDefault
typeYesPrune strategy type. Note: 'custom' is not available via MCP because it requires a JS function.
maxAgeNoMax age in seconds (for byAge). Memories older than this are removed.
actorIdNoActor ID to scope pruning to. Defaults to the current session actor.
maxPerActorNoMaximum memories per actor (for byCount). Excess memories are removed.
memoryTypesNoMemory types to prune (for byType).
minImportanceNoMinimum importance (for byImportance). Memories below this are removed.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of disclosing side effects. It explicitly states that memories are deleted, scoping is per actor, and the return includes IDs and count. It also cautions with 'Handle with care,' signaling destructiveness. However, it does not mention irreversibility, permissions, or potential cascading effects, so a perfect score is not warranted.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, tightly packed with necessary information. It front-loads the primary action, then details scope and return value. No redundant or filler content exists, making it highly efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the absence of an output schema, the description adequately explains the return value (pruned memory IDs and count). It also clarifies scoping and default behavior. Together with the schema, this provides sufficient context for an agent to invoke the tool correctly without ambiguity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema descriptions already cover all parameters at 100% coverage, establishing a baseline of 3. The tool description adds no further clarification about parameter interactions (e.g., how strategy types map to fields) beyond what the schema already provides, so the baseline holds.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: pruning (deleting) memories based on a strategy. It names the resource (memories), the action (prune/delete), and the scope (single actor, defaulting to session). It also distinguishes itself from sibling tools like memory_purge_actor (which purges all for an actor) and memory_delete (specific deletion) by focusing on strategy-based pruning.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use this tool (for strategy-based pruning) but does not explicitly contrast it with alternatives. It lacks guidance on when to prefer memory_delete for individual items or memory_purge_actor for clearing an actor entirely, leaving the selection to inference rather than explicit direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_purge_actorB

Delete ALL memories belonging to a specific actor. Irreversible.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorIdNoActor ID to purge. Defaults to the current session actor.

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full responsibility for behavioral disclosure. It explicitly states the operation is irreversible and deletes all memories for an actor, which is important destructive behavior. However, it does not mention other side effects such as confirmation, cascading behavior, or impact on related data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, using two short sentences with no filler. The critical information—destructive scope and irreversibility—is front-loaded and clearly stated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simple action and the absence of an output schema, the description provides enough contextual information to understand the core behavior. It could be slightly more complete by mentioning what happens to the actor after purging or any return value, but the essential context is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides 100% coverage for the single parameter, including its default behavior ('Defaults to the current session actor'). The tool description adds no additional parameter semantics beyond the schema, so the baseline score applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Delete'), the target ('ALL memories'), and the scope ('belonging to a specific actor'). The word 'ALL' and 'Irreversible' make the destructive scope unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus related sibling tools such as memory_delete, memory_delete_many, or memory_prune. The description does not mention conditions, alternatives, or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_retrieveB

Retrieve memories for an actor with optional filtering, semantic search, and date range constraints.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoFilter by tags (any match)
limitNoMaximum number of memories to return
queryNoSemantic search query (used with semantic/hybrid strategies)
actorIdNoActor ID. Defaults to the current session actor.
strategyNoRetrieval strategy. Default: recent.
memoryTypesNoFilter by memory types
createdAfterNoISO-8601 date. Only memories created after this time.
createdBeforeNoISO-8601 date. Only memories created before this time.

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations provided, so the description carries the full burden of behavioral transparency. It states the action but does not disclose potential side effects (e.g., whether it is read-only), performance implications, or any other behavioral constraints or requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that effectively summarizes the tool's functionality without any unnecessary words or repetition. It is well-structured and directly to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete enough for a basic understanding, but it lacks some context. It does not describe the output format or structure (though no output schema is provided), nor does it explain the relationship to sibling tools or the meaning of 'actor' and 'memories' in this domain. This missing context may lead to ambiguity in edge cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers all 8 parameters with individual descriptions, so schema coverage is 100%. The tool description does not add additional meaning beyond the schema, but per the rubric, the baseline for high schema coverage is 3. The parameter names and descriptions are clear enough.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Retrieve') and the resource ('memories for an actor'), and mentions optional filtering, semantic search, and date range constraints. However, it does not explicitly differentiate from sibling tools like memory_get, which might also retrieve a specific memory, so it is not perfectly distinct.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide any guidance on when to use this tool versus alternatives such as memory_compile_context, memory_summarize, or memory_get. No explicit use cases or conditions are mentioned, leaving the agent to infer the appropriate context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_statsB

Get memory statistics: total count, by type, by actor, average importance, and more.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, and the description only uses the word 'get' to imply read-only behavior. It does not disclose potential side effects, permissions, or whether the operation is safe to call repeatedly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, concise sentence that conveys the tool's purpose without any filler or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter statistics tool, the description adequately covers what the user can expect (counts, breakdowns, average importance). The phrase 'and more' is slightly vague but does not undermine overall completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool accepts zero parameters, so the baseline score of 4 applies. The description provides sufficient context for what the tool does without needing parameter explanations.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool retrieves memory statistics and enumerates specific stats (total count, by type, by actor, average importance). It is distinct from any sibling tool, none of which appear to provide aggregate statistics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives. It is implied that one would use it to obtain statistics, but no comparison to sibling tools or conditions for selection are provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_storeA

Store a memory directly without enrichment. Use this when you already have importance/tags or want precise control.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNoTags for categorization
actorIdNoActor ID. Defaults to the current session actor.
contentYesThe memory content text
metadataNoAdditional metadata key-value pairs
importanceNoImportance score 0.0-1.0. Default: 0.5.
memoryTypeNoMemory type (interaction, summary, observation, fact, reflection, preference, decision, instruction). Default: interaction.

TDQS

A3.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. "Without enrichment" hints that no automatic processing occurs, but it says nothing about persistence guarantees, deduplication, side effects, permission needs, or whether the call is repeatable — thin for a write-side tool with zero annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero waste, with the core action front-loaded and the usage condition immediately following.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With six parameters, a nested metadata object, no output schema, and no annotations, the definition should carry more behavioral weight than it does. It adequately explains when to use the tool but leaves the write semantics and error/idempotency behavior unexplained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with defaults and an enumerated memoryType documented inline, so the schema does the heavy lifting. The description's mention of importance/tags echoes existing field docs without adding format or interaction detail, making 3 the correct baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ("Store a memory") and adds a scope qualifier ("directly without enrichment") that implicitly separates it from the enrichment-oriented sibling memory_process. It does not name the alternative tool outright, so an agent must infer the contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger condition: "Use this when you already have importance/tags or want precise control", which maps directly to the schema's importance and tags parameters. No when-not guidance or named alternatives, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_store_batchA

Store multiple memories in one call, directly without enrichment. Embeddings (if configured) are computed in a single batched call for efficiency.

ParametersJSON Schema
NameRequiredDescriptionDefault
memoriesYesMemories to store (at least 1).

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It usefully discloses two traits beyond the schema: no enrichment is performed and embeddings are computed in a single batched call. But it omits anything about write permissions, batch-size limits, partial-failure behavior, or whether storage is reversible.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with zero filler, and the primary purpose is front-loaded ahead of the embedding-efficiency note. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema and no annotations, so the description is the only source of behavioral context. It covers the batching model and enrichment bypass, which is adequate, but says nothing about return values, batch limits, or failure semantics for a multi-item write tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the nested item fields (content, tags, actorId, importance, memoryType, metadata) are already fully documented in the schema. The description adds no syntax or format detail beyond that, which is the correct baseline when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ("Store multiple memories in one call") and adds scope clarification ("directly without enrichment"), which implicitly distinguishes it from the enriched path (e.g. memory_process) and the single-item memory_store. However, it never names an alternative sibling explicitly, so differentiation requires inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Store multiple memories in one call" implies the batch use case and "without enrichment" hints at when the direct path is preferable, but there is no explicit statement of when to choose this over memory_store or memory_process, and no exclusions or prerequisites. Usage is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_summarizeA

Summarize old memories into a single summary memory. Compresses N interactions into 1 summary via LLM.

ParametersJSON Schema
NameRequiredDescriptionDefault
actorIdNoActor ID. Defaults to the current session actor.
olderThanNoISO-8601 date. Only summarize memories older than this.
targetCountNoMaximum number of memories to summarize
skipMostRecentNoSkip the N most recent memories to preserve conversational freshness.

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that it uses an LLM and creates a single summary memory, but it does not explain whether the original memories are deleted, kept, or modified. Since no annotations are present, the description carries the full burden, and this missing side-effect information reduces transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two concise sentences with no superfluous content. It is well-structured and immediately communicates the core functionality, making it easy for an agent to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description sufficiently covers the main action and its parameters. No output schema exists, so no return value details are needed. It lacks a mention of error conditions or edge cases, but for the given complexity, it is reasonably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already provides descriptions for all four parameters, achieving 100% coverage. The description does not add any additional meaning or context beyond what the schema already states, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: summarizing old memories into a single summary memory, and compressing N interactions into 1 summary via LLM. This clearly differentiates it from siblings like memory_merge or memory_compile_context by focusing on summarization of old memories.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide explicit guidance on when to use this tool versus alternatives. It does not mention conditions, exclusions, or comparisons with sibling tools such as memory_prune or memory_merge, leaving the agent to infer appropriate usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

memory_touchA

Bump a memory's recency (last-accessed timestamp) without changing its content, id, or createdAt.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesMemory ID to touch

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Describes side effects (bump timestamp) and non-effects (no changes to content, id, or createdAt). Lacks details on error handling or authentication, but acceptable for a simple tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise, one-sentence description with clear structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequate for basic use, but lacks information on return value or error behavior if ID not found.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema describes 'id' as 'Memory ID to touch', which is clear. The description reinforces that it's the identifier.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the function: updates last-accessed timestamp, and explicitly excludes changes to content, id, or createdAt.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Does not provide guidance on when to use this tool versus alternatives; only states what it does.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 3 tool updates
    • Changedmemory_process1 field changed
      • changedInput schema / properties / memoryType / description
        Previous value: -"Memory type (interaction, summary, observation, fact, reflection). Default: interaction."New value: +"Memory type (interaction, summary, observation, fact, reflection, preference, decision, instruction). Default: interaction."
    • Changedmemory_store1 field changed
      • changedInput schema / properties / memoryType / description
        Previous value: -"Memory type (interaction, summary, observation, fact, reflection). Default: interaction."New value: +"Memory type (interaction, summary, observation, fact, reflection, preference, decision, instruction). Default: interaction."
    • Changedmemory_store_batch1 field changed
      • changedInput schema / properties / memories / items / properties / memoryType / description
        Previous value: -"Memory type (interaction, summary, observation, fact, reflection). Default: interaction."New value: +"Memory type (interaction, summary, observation, fact, reflection, preference, decision, instruction). Default: interaction."
  2. 18 tool updates
    • First observedmemory_compile_context
    • First observedmemory_delete
    • First observedmemory_delete_many
    • First observedmemory_dry_run_prune
    • First observedmemory_export
    • First observedmemory_get
    • First observedmemory_health
    • First observedmemory_import
    • First observedmemory_merge
    • First observedmemory_process
    • First observedmemory_prune
    • First observedmemory_purge_actor
    • First observedmemory_retrieve
    • First observedmemory_stats
    • First observedmemory_store
    • First observedmemory_store_batch
    • First observedmemory_summarize
    • First observedmemory_touch

TDQS

A3.7/5.0

Scored across 18 tools

Disambiguation4/5

Most tools have clearly distinct purposes: store vs process (raw vs enriched), get vs retrieve (by ID vs filtered search), and prune vs purge_actor vs delete. The main potential confusion is between memory_prune, memory_purge_actor, memory_delete, and memory_delete_many, though descriptions clarify scope and irreversibility.

Naming Consistency5/5

All 18 tools use a uniform memory_ prefix with snake_case verb_noun naming (memory_get, memory_store, memory_prune). Highly predictable and consistent throughout.

Tool Count4/5

18 tools is slightly heavy but each earns its place across store, retrieve, delete, maintenize, export, and health operations. No obvious redundant filler, though a couple of maintenance tools could be consolidated.

Completeness4/5

Broad lifecycle coverage: store/get/retrieve, delete/purge/prune, export/import, summarize/merge, stats, and health. The one notable gap is a dedicated single-memory content update tool, though merge and touch partially cover edit-like needs.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Long-term and multimodal memory for AI agents. Store facts and conversations with add_memory, recall them with search_memories — 8 tools over stdio/SSE/HTTP. Per-character memory isolation, LLM-based semantic deduplication, FAISS + JSON storage, and a fully local option (Ollama, no API key). Drop-in compatible with Mem0.
    8
    37 PyPI
    493
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Persistent, searchable memory for AI agents over the Model Context Protocol, enabling memory storage, full-text search with BM25 ranking, and retrieval across sessions.
    9 npm
    MIT