ai-memory v0.8.0

User Guide

BLUF (Bottom Line Up Front): ai-memory gives any AI assistant persistent memory across sessions. It works with any MCP-compatible AI client – including Claude AI, OpenAI ChatGPT, xAI Grok, META Llama, and others. Configure the MCP server once, and your AI automatically stores and recalls knowledge – your project architecture, preferences, past decisions, and hard-won lessons.

What Is This and Why Do I Need It?

ai-memory gives any AI assistant persistent memory across sessions. Without it, every conversation starts from zero. With it, your AI can:

Think of it as a brain for your AI assistant – short-term for what you’re doing right now, mid-term for this week’s work, and long-term for things that should never be forgotten.

The easiest way to use ai-memory is as an MCP tool server. MCP (Model Context Protocol) is an open standard supported by multiple AI platforms. ai-memory works with Claude Code, Codex, Gemini, Cursor, Windsurf, Continue.dev, Grok, Llama, and any other MCP-compatible client. Once configured, your AI client can store and recall memories natively without any manual CLI usage.

Setup

Each AI platform has its own MCP configuration path and format. See the Installation Guide for platform-specific setup instructions.

Below is an example for Claude Code (user scope: merge mcpServers into ~/.claude.json; or project scope: .mcp.json in project root) — one of many supported platforms:

{
  "mcpServers": {
    "memory": {
      "command": "ai-memory",
      "args": ["--db", "~/.claude/ai-memory.db", "mcp", "--tier", "semantic"]
    }
  }
}

Claude Code note: MCP server configuration does not go in settings.json or settings.local.json – those files do not support mcpServers.

Tier flag: The --tier flag must be passed in the args: keyword, semantic (default), smart, or autonomous. The config.toml tier setting is not used when launched by an AI client. Smart/autonomous tiers require an LLM backend — post-#1067 (v0.7.0) that is local Ollama, LMStudio, vLLM, llama.cpp server, OR any OpenAI-compatible cloud vendor (xAI / OpenAI / Anthropic / Gemini / DeepSeek / Kimi / Qwen / Mistral / Groq / Together / Cerebras / OpenRouter / Fireworks), selected via AI_MEMORY_LLM_BACKEND.

Other platforms (Codex, Gemini, Cursor, Windsurf, Continue.dev, etc.): config paths vary by platform. The command and args are the same – only the config file location differs. Refer to the Installation Guide for exact paths.

Grok note: Grok connects via remote MCP over HTTPS only (no stdio). Run ai-memory serve and expose it behind an HTTPS reverse proxy. server_label is required. See the Installation Guide for details.

Llama note: Llama Stack connects over HTTP rather than stdio MCP. Run ai-memory serve to start the HTTP daemon, then point your client at http://localhost:9077. See the Installation Guide for details.

How It Works

With MCP configured, your AI client gains 101 memory tools at --profile full (100 callable tools + the always-on memory_capabilities bootstrap) (highlights below; see API_REFERENCE.md for the full reference):

Your AI assistant uses these tools automatically during conversations. You can also ask directly: “Remember that we use PostgreSQL 15” or “What do you remember about our auth system?”

MCP Tool Reference

This section documents the MCP tools with their exact parameter schemas, example requests, and response formats. The surface advertises 101 entries at --profile full (100 callable “memory tools” + the always-on memory_capabilities bootstrap — both numbers are intentional; see issue #862 for the disambiguation). Default --profile core exposes 7 (the original 5 + memory_load_family + memory_smart_load) plus the always-on memory_capabilities. Canonical counts on the evidence page and asserted by Profile::full().expected_tool_count() in src/profile.rs. All tools are invoked via JSON-RPC 2.0 using method tools/call with the tool name in params.name and tool parameters in params.arguments.

All responses are wrapped in the MCP content envelope:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "<tool output>" }]
  }
}

On error, the envelope includes "isError": true and the text contains the error message.


memory_store

Store a new memory. Deduplicates by title+namespace – if a memory with the same title and namespace already exists, it updates the existing memory instead of creating a duplicate.

Parameters:

Name Type Required Default Description
title string Yes Short descriptive title
content string Yes Full memory content (max 64 KB)
tier string No "mid" Memory tier: "short", "mid", or "long"
namespace string No "global" Project/topic namespace
tags array of strings No [] Tags for filtering
priority integer (1-10) No 5 Priority ranking
confidence number (0.0-1.0) No 1.0 Certainty level
source string No "nhi" (v0.7.x vendor-neutral default, #1175) Origin — one of VALID_SOURCES: "user", "nhi", "claude" (deprecated), "hook", "api", "cli", "import", "consolidation", "system", "chaos", "notify"
kind string No "observation" Memory kind. Omit for the observation default; a supplied value MUST be one of the canonical variants (observation, reflection, persona, concept, entity, claim, relation, event, conversation, decision) or the write is rejected (#1467). See docs/memory-kind-vocab.md.

Example request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "memory_store",
    "arguments": {
      "title": "Project uses PostgreSQL 15",
      "content": "The main database is PostgreSQL 15 with pgvector for embeddings.",
      "tier": "long",
      "namespace": "my-app",
      "tags": ["database", "infrastructure"],
      "priority": 8,
      "source": "user"
    }
  }
}

Example response (new memory):

{
  "id": "a1b2c3d4-...",
  "tier": "long",
  "title": "Project uses PostgreSQL 15",
  "namespace": "my-app",
  "potential_contradictions": ["e5f6g7h8-..."]
}

Example response (duplicate – updated existing):

{
  "id": "existing-id-...",
  "tier": "long",
  "title": "Project uses PostgreSQL 15",
  "namespace": "my-app",
  "duplicate": true,
  "action": "updated existing memory"
}

memory_recall

Recall memories relevant to a context. Uses fuzzy OR matching, ranked by a composite score of relevance + priority + access frequency + confidence + tier boost + recency decay. At semantic tier and above, uses hybrid scoring (semantic + keyword blending).

Parameters:

Name Type Required Default Description
context string Yes What you are trying to remember
namespace string No Filter by namespace
limit integer (max 50) No 10 Maximum results to return
tags string No Filter by tag
since string No Only memories created after this RFC 3339 timestamp
until string No Only memories created before this RFC 3339 timestamp
format string No "toon_compact" Response format: "json", "toon", or "toon_compact" (default saves 79% tokens vs JSON)

Example request:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "memory_recall",
    "arguments": {
      "context": "database setup and configuration",
      "namespace": "my-app",
      "limit": 5
    }
  }
}

Example response (JSON format):

{
  "memories": [
    {
      "id": "a1b2c3d4-...",
      "title": "Project uses PostgreSQL 15",
      "content": "The main database is PostgreSQL 15 with pgvector.",
      "tier": "long",
      "namespace": "my-app",
      "priority": 8,
      "tags": ["database"],
      "score": 0.763,
      "confidence": 1.0,
      "access_count": 3,
      "created_at": "2026-04-01T12:00:00Z",
      "updated_at": "2026-04-10T08:00:00Z"
    }
  ],
  "count": 1,
  "mode": "hybrid"
}

Example response (TOON compact, the default):

count:1|mode:hybrid
memories[id|title|tier|namespace|priority|score|tags]:
a1b2c3d4-...|Project uses PostgreSQL 15|long|my-app|8|0.763|database

Search memories by exact keyword match with AND semantics (all terms must match).

Parameters:

Name Type Required Default Description
query string Yes Search keywords
namespace string No Filter by namespace
tier string No Filter by tier: "short", "mid", or "long"
limit integer (max 200) No 20 Maximum results
agent_id string No Filter by metadata.agent_id (exact match)
as_agent string No Querying agent for scope-based visibility filtering (Task 1.5)
format string No "toon_compact" Response format: "json", "toon", or "toon_compact"

Example request:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "memory_search",
    "arguments": {
      "query": "PostgreSQL",
      "namespace": "my-app"
    }
  }
}

Example response (JSON format):

{
  "results": [ { "id": "a1b2c3d4-...", "title": "Project uses PostgreSQL 15", "..." : "..." } ],
  "count": 1
}

memory_list

List memories, optionally filtered by namespace or tier.

Parameters:

Name Type Required Default Description
namespace string No Filter by namespace
tier string No Filter by tier: "short", "mid", or "long"
limit integer (max 200) No 20 Maximum results
format string No "toon_compact" Response format: "json", "toon", or "toon_compact"

Example request:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "memory_list",
    "arguments": {
      "namespace": "my-app",
      "tier": "long",
      "limit": 10
    }
  }
}

Example response (JSON format):

{
  "memories": [ { "id": "...", "title": "...", "tier": "long", "..." : "..." } ],
  "count": 1
}

memory_delete

Delete a memory by ID.

Parameters:

Name Type Required Default Description
id string Yes Memory ID to delete

Example request:

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "memory_delete",
    "arguments": { "id": "a1b2c3d4-..." }
  }
}

Example response:

{ "deleted": true }

memory_promote

Promote a memory to long-term (permanent). Clears the expiry timestamp.

Parameters:

Name Type Required Default Description
id string Yes Memory ID to promote
target_tier string No Stop at an intermediate tier ("mid" or "long"); omit for the historical highest-reachable-tier (long) behavior
to_namespace string No Clone to ancestor namespace (Task 1.7; preserves original)

Example request:

{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "tools/call",
  "params": {
    "name": "memory_promote",
    "arguments": { "id": "a1b2c3d4-..." }
  }
}

Example response:

{ "promoted": true, "id": "a1b2c3d4-...", "tier": "long" }

memory_forget

Bulk delete memories matching a pattern, namespace, or tier. At least one filter should be provided.

Parameters:

Name Type Required Default Description
namespace string No Filter by namespace
pattern string No Delete memories matching this pattern
tier string No Filter by tier: "short", "mid", or "long"

Example request:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "memory_forget",
    "arguments": {
      "namespace": "my-app",
      "tier": "short"
    }
  }
}

Example response:

{ "deleted": 12, "archived": true }

memory_stats

Get memory store statistics (counts, tiers, namespaces, links, database size).

Parameters: None.

Example request:

{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "tools/call",
  "params": {
    "name": "memory_stats",
    "arguments": {}
  }
}

Example response:

{
  "total": 142,
  "by_tier": [{"tier":"short","count":5},{"tier":"mid","count":37},{"tier":"long","count":100}],
  "by_namespace": [{"namespace":"my-app","count":80},{"namespace":"global","count":62}],
  "expiring_soon": 5,
  "links_count": 23,
  "db_size_bytes": 524288
}

memory_update

Update an existing memory by ID. Only provided fields are changed – omitted fields remain unchanged.

Parameters:

Name Type Required Default Description
id string Yes Memory ID to update
title string No New title
content string No New content
tier string No New tier: "short", "mid", or "long"
namespace string No New namespace
tags array of strings No New tags (replaces existing)
priority integer (1-10) No New priority
confidence number (0.0-1.0) No New confidence
expires_at string No New expiry timestamp (RFC 3339); a JSON null is treated as absent (no change)
metadata object No Arbitrary JSON metadata (replaces existing; the original agent_id is preserved)

Example request:

{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "tools/call",
  "params": {
    "name": "memory_update",
    "arguments": {
      "id": "a1b2c3d4-...",
      "priority": 9,
      "tags": ["database", "critical"]
    }
  }
}

Example response:

{
  "updated": true,
  "memory": {
    "id": "a1b2c3d4-...",
    "title": "Project uses PostgreSQL 15",
    "tier": "long",
    "priority": 9,
    "tags": ["database", "critical"],
    "...": "..."
  }
}

memory_get

Get a specific memory by ID, including its links.

Parameters:

Name Type Required Default Description
id string Yes Memory ID to retrieve

Example request:

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "tools/call",
  "params": {
    "name": "memory_get",
    "arguments": { "id": "a1b2c3d4-..." }
  }
}

Example response:

{
  "id": "a1b2c3d4-...",
  "title": "Project uses PostgreSQL 15",
  "content": "The main database is PostgreSQL 15 with pgvector.",
  "tier": "long",
  "namespace": "my-app",
  "priority": 8,
  "confidence": 1.0,
  "tags": ["database"],
  "source": "user",
  "access_count": 5,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-10T08:00:00Z",
  "last_accessed_at": "2026-04-12T09:00:00Z",
  "expires_at": null,
  "links": [
    { "source_id": "a1b2c3d4-...", "target_id": "e5f6g7h8-...", "relation": "related_to" }
  ]
}

Create a link between two memories.

Parameters:

Name Type Required Default Description
source_id string Yes Source memory ID
target_id string Yes Target memory ID
relation string No "related_to" Relation type (nine at v0.8.0): "related_to", "supersedes", "contradicts", "derived_from", "reflects_on", "derives_from", "decomposes_into", "depends_on", "advances"

Example request:

{
  "jsonrpc": "2.0",
  "id": 11,
  "method": "tools/call",
  "params": {
    "name": "memory_link",
    "arguments": {
      "source_id": "a1b2c3d4-...",
      "target_id": "e5f6g7h8-...",
      "relation": "supersedes"
    }
  }
}

Example response:

{
  "linked": true,
  "source_id": "a1b2c3d4-...",
  "target_id": "e5f6g7h8-...",
  "relation": "supersedes"
}

Get all links for a memory (both directions – where the memory is source or target).

Parameters:

Name Type Required Default Description
id string Yes Memory ID to get links for

Example request:

{
  "jsonrpc": "2.0",
  "id": 12,
  "method": "tools/call",
  "params": {
    "name": "memory_get_links",
    "arguments": { "id": "a1b2c3d4-..." }
  }
}

Example response (v0.7.0 — memory_get_links returns the full envelope per issue #860; temporal + attestation columns surfaced):

{
  "links": [
    {
      "source_id": "a1b2c3d4-...",
      "target_id": "e5f6g7h8-...",
      "relation": "related_to",
      "created_at": "2026-05-18T12:00:00Z",
      "valid_from": "2026-05-18T12:00:00Z",
      "valid_until": null,
      "observed_by": "ai:claude-opus-4.7@host:pid-3812",
      "signature": null,
      "attest_level": "unsigned",
      "signed_at": null
    }
  ],
  "count": 1
}

Relations accepted by memory_link and surfaced by memory_get_links (nine at v0.8.0; six at v0.7.0; was four at v0.6.x): related_to, supersedes, contradicts, derived_from, reflects_on (recursive-learning Task 1/8), derives_from (WT-1-A atomisation), decomposes_into, depends_on, advances.


memory_consolidate

Consolidate multiple memories into one long-term summary. Deletes source memories and creates derived_from links. If summary is omitted and LLM is available (smart/autonomous tier), auto-generates a summary.

Parameters:

Name Type Required Default Description
ids array of strings (2-100 items) Yes Memory IDs to consolidate
title string Yes Title for the consolidated memory
summary string No Summary content. Auto-generated via LLM if omitted at smart/autonomous tier
namespace string No "global" Namespace for the consolidated memory

Example request:

{
  "jsonrpc": "2.0",
  "id": 13,
  "method": "tools/call",
  "params": {
    "name": "memory_consolidate",
    "arguments": {
      "ids": ["id-1", "id-2", "id-3"],
      "title": "Auth system architecture",
      "summary": "JWT tokens with refresh rotation, RBAC via middleware, sessions in Redis."
    }
  }
}

Example response:

{ "id": "new-consolidated-id-...", "consolidated": 3 }

Example response (auto-generated summary):

{
  "id": "new-consolidated-id-...",
  "consolidated": 3,
  "auto_summary": true,
  "summary_preview": "JWT tokens with refresh rotation, RBAC via middleware..."
}

memory_capabilities

Report the active feature tier, loaded models, and available capabilities of the memory system. Call once per session to discover what features are available.

Parameters: all optional — accept (envelope version negotiation: "1" / "2" / "3"), family (drill into one tool family), include_schema (bool), verbose (bool; also forced on by AI_MEMORY_TOOLS_VERBOSE=1).

Example request:

{
  "jsonrpc": "2.0",
  "id": 14,
  "method": "tools/call",
  "params": {
    "name": "memory_capabilities",
    "arguments": {}
  }
}

Example response (abridged — the v0.7.0 envelope is schema_version: "3" and additionally carries summary, describe_to_user, tools, governance, and family/profile metadata; v1/v2 remain negotiable via the accept param):

{
  "tier": "semantic",
  "features": {
    "embedding_recall": true,
    "hybrid_scoring": true,
    "query_expansion": false,
    "auto_tagging": false,
    "contradiction_detection": false,
    "cross_encoder_reranking": false
  },
  "models": {
    "embedding": "nomic-embed-text-v1.5",
    "embedding_dim": 384,
    "llm": "none",
    "cross_encoder": "none"
  }
}

models.* reflects the operator-resolved configuration (issue #1168, 2026-05-24). The values mirror the boot banner and the live LLM / embedder / reranker the daemon was wired to. When you set [llm] backend = "xai", model = "grok-4.3" in ~/.config/ai-memory/config.toml, models.llm reports "xai:grok-4.3"; an Ollama backend reports the bare model id ("gemma3:4b"); models.embedding reports the configured [embeddings].model string verbatim. An unconfigured deployment reports the resolver’s defaults ("nomic-embed-text-v1.5" + "none" for the LLM). See the v0.7.x #1146 enterprise-config rollout for the full precedence ladder.

features.embedder_loaded reports the LIVE posture (#1594 / #1598): a remote (API-backend) embedder whose endpoint is failing at request time reports false, and recall_mode_active follows truthfully ("degraded" instead of "hybrid"). Embedder construction failures at boot fail closed to keyword recall (#1593) — the chat LLM client is never reused for embeddings.


memory_expand_query

Use LLM to expand a search query into additional semantically related terms. Requires smart or autonomous tier.

Parameters:

Name Type Required Default Description
query string Yes The search query to expand

Example request:

{
  "jsonrpc": "2.0",
  "id": 15,
  "method": "tools/call",
  "params": {
    "name": "memory_expand_query",
    "arguments": { "query": "database migration" }
  }
}

Example response:

{
  "original": "database migration",
  "expanded_terms": ["schema change", "data migration", "SQL migration", "alembic", "flyway", "db upgrade"]
}

memory_auto_tag

Use LLM to auto-generate tags for a memory. Merges new tags with existing ones. Requires smart or autonomous tier.

Parameters:

Name Type Required Default Description
id string Yes Memory ID to auto-tag

Example request:

{
  "jsonrpc": "2.0",
  "id": 16,
  "method": "tools/call",
  "params": {
    "name": "memory_auto_tag",
    "arguments": { "id": "a1b2c3d4-..." }
  }
}

Example response:

{
  "id": "a1b2c3d4-...",
  "new_tags": ["postgresql", "infrastructure", "database"],
  "all_tags": ["existing-tag", "postgresql", "infrastructure", "database"]
}

memory_detect_contradiction

Use LLM to check if two memories contradict each other. Requires smart or autonomous tier.

Parameters:

Name Type Required Default Description
id_a string Yes First memory ID
id_b string Yes Second memory ID

Example request:

{
  "jsonrpc": "2.0",
  "id": 17,
  "method": "tools/call",
  "params": {
    "name": "memory_detect_contradiction",
    "arguments": {
      "id_a": "a1b2c3d4-...",
      "id_b": "e5f6g7h8-..."
    }
  }
}

Example response:

{
  "contradicts": true,
  "memory_a": { "id": "a1b2c3d4-...", "title": "Use PostgreSQL 15" },
  "memory_b": { "id": "e5f6g7h8-...", "title": "Use MySQL 8" }
}

memory_archive_list

List archived (expired) memories. Archived memories are preserved by garbage collection when archive_on_gc = true in config.

Parameters:

Name Type Required Default Description
namespace string No Filter by namespace
limit integer (max 1000) No 50 Maximum results
offset integer No 0 Pagination offset

Example request:

{
  "jsonrpc": "2.0",
  "id": 18,
  "method": "tools/call",
  "params": {
    "name": "memory_archive_list",
    "arguments": { "limit": 10 }
  }
}

Example response:

{
  "archived": [ { "id": "...", "title": "...", "..." : "..." } ],
  "count": 3
}

memory_archive_restore

Restore an archived memory back to the active memory store. The row’s original_tier and original_expires_at are re-applied where present (legacy archive rows without them restore as long with no expiry).

Parameters:

Name Type Required Default Description
id string Yes ID of the archived memory to restore

Example request:

{
  "jsonrpc": "2.0",
  "id": 19,
  "method": "tools/call",
  "params": {
    "name": "memory_archive_restore",
    "arguments": { "id": "archived-id-..." }
  }
}

Example response:

{ "restored": true, "id": "archived-id-..." }

memory_archive_purge

Permanently delete archived memories. Optionally only those older than N days. This cannot be undone.

Parameters:

Name Type Required Default Description
older_than_days integer No Only purge entries archived more than N days ago. Omit to purge all

Example request:

{
  "jsonrpc": "2.0",
  "id": 20,
  "method": "tools/call",
  "params": {
    "name": "memory_archive_purge",
    "arguments": { "older_than_days": 30 }
  }
}

Example response:

{ "purged": 7 }

memory_archive_stats

Show archive statistics: total count and breakdown by namespace.

Parameters: None.

Example request:

{
  "jsonrpc": "2.0",
  "id": 21,
  "method": "tools/call",
  "params": {
    "name": "memory_archive_stats",
    "arguments": {}
  }
}

Example response:

{
  "archived_total": 15,
  "by_namespace": [{"namespace":"my-app","count":10},{"namespace":"global","count":5}]
}

Knowledge Graph & Hierarchy (v0.6.3)

The v0.6.3 release introduces hierarchical namespace taxonomy (Pillar 1 / Stream A) and a temporal-validity knowledge graph (Pillar 2 / Streams B–D). The tools below operate on memory_links and the entity_aliases side table; existing memory CRUD is unaffected.

memory_get_taxonomy

Walk live (non-expired) memories grouped by namespace and fold them into a TaxonomyNode tree. Splits on / so a namespace like alphaone/engineering/platform becomes a 3-level subtree. Each node carries count (memories at exactly this namespace) and subtree_count (count plus every descendant within the depth limit).

Parameters:

Name Type Required Default Description
namespace_prefix string No Restrict the walk to namespaces under this prefix (e.g. "alphaone/engineering")
depth integer (0-8) No 8 Maximum tree depth from the root or prefix
limit integer (1-10000) No 1000 Maximum nodes to return (response includes truncated: true if exceeded)

Example response envelope:

{
  "tree": [
    {
      "namespace": "alphaone",
      "count": 0,
      "subtree_count": 47,
      "children": [
        { "namespace": "alphaone/engineering", "count": 3, "subtree_count": 47, "children": [ ... ] }
      ]
    }
  ],
  "total_count": 47,
  "truncated": false
}

total_count is computed as an independent aggregation that stays honest even when limit drops rows from the walk.


memory_check_duplicate

Embedding cosine-similarity duplicate detection. Embeds title + content, scans live memories with non-NULL embeddings, and returns the nearest match with an is_duplicate flag.

Parameters:

Name Type Required Default Description
title string Yes Candidate title
content string Yes Candidate content
namespace string No Restrict scan to a single namespace
threshold number (0.5-1.0) No 0.85 Cosine similarity at or above this is is_duplicate: true (clamped to 0.5 floor)

Example response:

{
  "is_duplicate": true,
  "threshold": 0.85,
  "nearest": {
    "id": "a1b2c3d4-...",
    "title": "Project uses PostgreSQL 15",
    "namespace": "my-app",
    "similarity": 0.92
  },
  "suggested_merge": "a1b2c3d4-...",
  "candidates_scanned": 412
}

suggested_merge is non-null when is_duplicate == true. Requires the semantic feature tier or higher (embeddings must be available).


memory_entity_register

Register an entity as a typed memory (no separate entities table — entities live in memories with metadata.kind = "entity"). Idempotent: calling twice with the same canonical_name + namespace returns the same entity ID and merges any new aliases via INSERT OR IGNORE.

Parameters:

Name Type Required Default Description
canonical_name string Yes Primary name for the entity
namespace string Yes Namespace to scope the entity to
aliases array of strings No [] Alternate names; blanks skipped, dupes deduped
metadata object No {} Additional metadata (kind is forced to "entity")
agent_id string No (caller NHI) Override the recorded agent_id

Example response (newly created):

{
  "entity_id": "ent-9876...",
  "canonical_name": "PostgreSQL",
  "namespace": "my-app",
  "aliases": ["pg", "postgres", "PostgreSQL"],
  "created": true
}

If a non-entity memory already exists with the same (title, namespace), the call returns an error rather than overwriting it.


memory_entity_get_by_alias

Resolve an alias to its canonical entity. Trims whitespace, matches case-sensitively, and discriminates against non-entity memories that happen to share the alias.

Parameters:

Name Type Required Default Description
alias string Yes Alias to look up
namespace string No Restrict lookup to this namespace; if omitted, picks the most-recently-created match across namespaces

Example response:

{
  "found": true,
  "entity_id": "ent-9876...",
  "canonical_name": "PostgreSQL",
  "namespace": "my-app",
  "aliases": ["pg", "postgres", "PostgreSQL"]
}

Returns {"found": false, ...} (with null fields) if no entity matches the alias.


memory_kg_query

Recursive-CTE traversal of the temporal knowledge graph rooted at a source memory. Walks outbound links up to max_depth hops, applies per-hop temporal and agent filters, and prunes cycles via accumulated path matching.

Parameters:

Name Type Required Default Description
source_id string Yes Memory ID to start the walk from
max_depth integer (1-5) No 1 Maximum hops; ceiling enforced (depth=0 errors, depth>5 errors)
valid_at string (RFC 3339) No Apply per-hop: valid_from <= ? AND (valid_until IS NULL OR valid_until > ?); rows with NULL valid_from are excluded when set
allowed_agents array of strings No Apply per-hop: observed_by IN (...); an empty array ([]) returns zero rows (vs. omitting which skips the filter)
limit integer (1-1000) No 200 Maximum rows returned

Example response:

{
  "source_id": "a1b2c3d4-...",
  "max_depth": 3,
  "memories": [
    {
      "target_id": "e5f6g7h8-...",
      "title": "API uses Axum 0.8",
      "target_namespace": "my-app",
      "relation": "depends_on",
      "valid_from": "2026-04-25T19:25:00Z",
      "valid_until": null,
      "observed_by": "ai:claude-code@host:pid-12345",
      "depth": 1,
      "path": "a1b2c3d4->e5f6g7h8"
    }
  ],
  "paths": ["a1b2c3d4->e5f6g7h8->..."],
  "count": 1
}

Ordering: depth ASC, COALESCE(valid_from, link_created_at) ASC, link_created_at ASC.


memory_kg_timeline

Ordered fact timeline for an entity. Returns every link with the given source_id, ordered by valid_from ascending. Skips links with NULL valid_from (legacy un-anchored links from before the v15 migration backfill).

Parameters:

Name Type Required Default Description
source_id string Yes Entity / memory ID anchoring the timeline
since string (RFC 3339) No Only events with valid_from >= since
until string (RFC 3339) No Only events with valid_from <= until
limit integer (1-1000) No 200 Maximum events

Example response:

{
  "source_id": "a1b2c3d4-...",
  "events": [
    {
      "target_id": "e5f6g7h8-...",
      "relation": "depends_on",
      "valid_from": "2026-04-25T19:25:00Z",
      "valid_until": null,
      "observed_by": "ai:claude-code@host:pid-12345"
    }
  ],
  "count": 1
}

memory_kg_invalidate

Mark a knowledge-graph link as superseded by setting its valid_until column. The link is NOT deleted — historical queries that pin valid_at to a time before the invalidation still see it. Idempotent: repeated calls overwrite valid_until; the prior value is returned so callers can detect overwrites.

Federation note: invalidations are NOT quorum-broadcast. They apply locally and propagate to peers asynchronously via the sync-daemon. See docs/MIGRATION-v0.6.2-to-v0.6.3.md for details.

Parameters:

Name Type Required Default Description
source_id string Yes Source memory ID of the link
target_id string Yes Target memory ID of the link
relation string Yes Relation type (the third part of the link triple)
valid_until string (RFC 3339) No (now) When to mark the link superseded

Example response:

{
  "found": true,
  "valid_until": "2026-04-26T03:00:00Z",
  "previous_valid_until": null
}

Returns {"found": false, ...} if no link matches the (source_id, target_id, relation) triple.


Distributed Coordination Tools (v0.8.0)

v0.8.0 turns the memory substrate into a coordination substrate. The Pillar-1 surface (#1709) adds 26 coordination tools across five families — all in the power profile, all advertised at --profile full. They let multiple agents coordinate work over the shared store: a typed action DAG + state machine, single-holder TTL leases, Ed25519-signed signals, attested checkpoints, and replayable routines. Full parameter schemas, examples, and the federation-fanout semantics live in coordination.md; the summary below names every tool so you know the surface exists.

Family Tools What it does
Actions (8) memory_action_create, memory_action_get, memory_action_transition, memory_action_list, memory_action_add_edge, memory_action_edges, memory_action_frontier, memory_action_next Typed action DAG + state machine. Create/get/list actions, transition state (CAS), wire typed dependency edges, then query the ready frontier (ranked unblocked actions) and pick the single next action.
Leases (4) memory_lease_acquire, memory_lease_renew, memory_lease_release, memory_lease_get Single-holder TTL leases over the action substrate — acquire/renew/release a lease and read its current holder, for at-most-one-worker handoff.
Signals (5) memory_signal_send, memory_signal_read, memory_signal_inbox, memory_signal_thread, memory_signal_ack Typed, Ed25519-signed inter-agent messages — send, read, list your inbox, walk a thread, and acknowledge.
Checkpoints (4) memory_checkpoint_create, memory_checkpoint_resolve, memory_checkpoint_query, memory_checkpoint_verify Conditional coordination gates with Ed25519-attested resolution — create a gate, resolve it, query state, and verify the attestation.
Routines (5) memory_routine_create, memory_routine_freeze, memory_routine_run, memory_routine_status, memory_routine_list Replayable scheduled-coordination routines — create, freeze (pin a version), run, check status, and list.

Pillar-2 layers typed cognition (Goal / Plan / Step via lifecycle_state


Agent Identity (NHI) — metadata.agent_id

Every stored memory carries a metadata.agent_id tag that records who (or what) stored it. You’ll see it in recall, list, search, and get responses. You can filter by it too.

Quick examples

# Default: a host-qualified id is auto-generated
ai-memory store -T "note" -c "content"
# stored memory's metadata.agent_id = "host:your-hostname:pid-12345-abcdef01"

# Use an explicit identity
ai-memory --agent-id alice store -T "note" -c "content"

# Or via environment variable
AI_MEMORY_AGENT_ID=alice ai-memory store -T "note" -c "content"

# Filter by agent
ai-memory list --agent-id alice
ai-memory search "some query" --agent-id alice

Resolution precedence

The identity that ends up in metadata.agent_id is resolved in order:

  1. The explicit value you passed (flag, env var, MCP param, or HTTP body field)
  2. AI_MEMORY_AGENT_ID environment variable
  3. (MCP server only) the MCP client’s initialize.clientInfo.nameai:<client>@<hostname> (durable, pid-free since #1720)
  4. host:<hostname> — a durable host-qualified default (pid-free since #1720)
  5. anonymous:pid-<pid>-<uuid8> — last-resort fallback if hostname lookup fails

For the HTTP API, the precedence within a single request is:

  1. agent_id field in the POST/PUT JSON body
  2. X-Agent-Id request header
  3. Per-request synthesized anonymous:req-<uuid8> (logged at WARN)

Read-path private-row visibility (#1468 / #1469)

The precedence above resolves the write identity stamped into metadata.agent_id. Reads that enforce per-row scope=private ownership work differently. The MCP tools memory_session_start, memory_list, memory_search, and memory_recall resolve a visibility caller from AI_MEMORY_AGENT_ID only (or None when it is unset) — never from the pid-synthesized clientInfo id, because that id embeds the live PID and could never match the metadata.agent_id an earlier process wrote.

When AI_MEMORY_AGENT_ID is set, these tools drop rows owned by a different agent and marked scope=private (or with no scope key, which defaults to private) before returning results; rows you own and rows marked scope=collective always pass. When it is unset, the read path is trust-all (single-tenant) and returns every matching row. Set AI_MEMORY_AGENT_ID per agent to keep another agent’s private memories out of your list / search / recall results.

Format rules

Valid agent_id values match ^[A-Za-z0-9_\-:@./]{1,128}$. Prefixed forms (ai:, host:, anonymous:, and future human: / system:) are reserved for specific roles but not enforced. Whitespace, null bytes, control chars, and shell metacharacters are rejected at validation time.

Immutability

Once a memory carries an agent_id, that value is preserved across update, consolidate, import, sync, and upsert dedup. You can update a memory’s content as a different agent, but the original author’s agent_id stays recorded. import restamps with the current caller’s id by default — pass --trust-source to import the embedded ids as-is (use only for legitimate backup restoration).

Special metadata fields produced by the system

These extra keys may appear alongside agent_id — they’re informational:

Trust model

By default agent_id is a claimed identity: anyone who can invoke ai-memory can set any agent_id they want (subject to the format regex), so an unsigned write’s agent_id is provenance/filtering metadata, not a security assertion. Such writes land metadata.attest_level = "claimed".

#626 Layer-3 — cryptographic attestation (v0.7.0). A caller that holds the agent’s keypair can upgrade a write from claimed to attested by presenting a detached Ed25519 signature over the canonical SignableWrite envelope (agent_id + namespace + title + kind + created_at + sha256(content)). On every store surface — CLI (ai-memory store --sign), MCP (memory_store), and HTTP (POST /api/v1/memories) — the daemon verifies the signature against the agent’s bound public key (registered via memory_agent_register + bind-key) and, on success, stamps metadata.attest_level = "agent_attested" and adopts the signed created_at verbatim (±300 s freshness window). A forged signature is rejected with 403 ATTESTATION_FAILED.

Operators who want to require attestation can set AI_MEMORY_REQUIRE_AGENT_ATTESTATION (truthy): unsigned writes are then rejected rather than landing claimed. The default is permissive (unsigned → claimed) to preserve the v0.6.x posture. Two edges stay claimed-by-design at v0.7.0 — the federation receive path (mTLS + the peer allowlist is the trust boundary, not a per-write agent signature) and this permissive default posture — both tracked for v0.8 hardening under #1464.

Default exposes the hostname

The auto-generated host:<hostname> default exposes your hostname (it is durable + pid-free since #1720; only the anonymous:pid-… fallback still carries a process id). When exporting / syncing / sharing a DB externally, pass --agent-id or AI_MEMORY_AGENT_ID to scrub that exposure. Tracking issue #198 covers an opt-out config flag.

Zero Token Cost

Unlike built-in memory systems (Claude Code auto-memory, ChatGPT memory) that load your entire memory into every conversation, ai-memory uses zero context tokens until recalled. Only relevant memories come back, ranked by a 6-factor scoring algorithm. For Claude Code users: disable auto-memory ("autoMemoryEnabled": false in settings.json) to stop paying for 200+ lines of idle context.

TOON Format (Token-Oriented Object Notation)

All recall, search, and list responses default to TOON compact format – 79% smaller than JSON. Field names are declared once as a header, then values as pipe-delimited rows. This saves tokens on every recall.

MCP Prompts

The server provides 2 MCP prompts via prompts/list that teach AI clients to use memory proactively:

Feature Tiers

ai-memory supports 4 feature tiers, controlled by the --tier flag when starting the MCP server (e.g., ai-memory mcp --tier semantic). Each tier builds on the previous one:

Tier Recall Method Extra Features Requirements
keyword FTS5 only None None (lightest)
semantic (default) Hybrid: semantic + keyword blending Embedding-based recall HuggingFace embedding model (~256 MB RAM), or zero local RAM with an API embedding backend (#1598 — [embeddings].backend)
smart Hybrid Query expansion, auto-tagging, contradiction detection An LLM backend (#1067 — local Ollama ~1 GB RAM, or any OpenAI-compatible cloud vendor via AI_MEMORY_LLM_BACKEND ~256 MB)
autonomous Hybrid Full autonomous memory management + cross-encoder reranking LLM backend + local cross-encoder (~4 GB local / ~3 GB with a remote LLM)

Semantic tier first-run note: The first run at the semantic tier (or above) downloads a ~100 MB embedding model from HuggingFace, which takes 30-60 seconds. Subsequent starts load from cache (~2 seconds). If the download fails, retry or use --tier keyword temporarily.

Hybrid Recall (semantic tier and above)

At the semantic tier and above, recall uses hybrid scoring that blends two signals:

  1. Semantic similarity – the query and each memory are converted to embeddings (dense vectors), and cosine similarity measures how close they are in meaning. This catches relevant results even when exact keywords differ.
  2. Keyword matching – the existing FTS5 full-text search with the 6-factor composite score.

The final ranking blends both signals, so you get the precision of keyword matching plus the flexibility of semantic understanding.

Query Expansion, Auto-Tagging, and Contradiction Detection (smart+ tier)

At the smart and autonomous tiers, three additional capabilities are available via LLM inference (any backend via AI_MEMORY_LLM_BACKEND, #1067):

These tools are available to your AI assistant automatically at the smart+ tier. At lower tiers, calling them returns a tier-requirement notice.

Getting Started (CLI)

Store Your First Memory

ai-memory store \
  -T "Project uses PostgreSQL 15" \
  -c "The main database is PostgreSQL 15 with pgvector for embeddings." \
  --tier long \
  --priority 7

That’s it. The memory is now stored permanently (long tier) with priority 7/10.

Custom Expiration

You can set a custom expiration on any memory:

# Set an explicit expiration timestamp
ai-memory store \
  -T "Sprint 42 goals" \
  -c "Finish migration, deploy v2 API." \
  --tier mid \
  --expires-at "2026-04-15T00:00:00Z"

# Or set a TTL in seconds (e.g., 2 hours)
ai-memory store \
  -T "Current debugging session" \
  -c "Investigating auth timeout in login.rs" \
  --tier short \
  --ttl-secs 7200

Recall Memories

ai-memory recall "database setup"

This performs a fuzzy OR search across all your memories and returns the most relevant ones, ranked by a 6-factor composite score:

  1. FTS relevance – how well the text matches (via SQLite FTS5)
  2. Priority – higher priority memories rank higher (weight: 0.5)
  3. Access frequency – frequently recalled memories rank higher (weight: 0.1)
  4. Confidence – higher certainty memories rank higher (weight: 2.0)
  5. Tier boost – long-term gets +3.0, mid gets +1.0, short gets +0.0
  6. Recency decay1/(1 + days_old * 0.1) so recent memories rank higher

Recall also automatically:

Search for Exact Matches

ai-memory search "PostgreSQL"

Search uses AND semantics – all terms must match. Use this when you know exactly what you’re looking for. Search uses the same 6-factor ranking but without the tier boost.

Search vs Recall

Use recall when you vaguely remember something – it uses fuzzy OR matching and returns ranked results. Any term can match, so it casts a wide net.

Use search when you know exact keywords – it requires all terms to match (AND semantics), so results are precise but narrower.

CLI Command Reference

All commands accept the following global flags (before the subcommand):

Flag Short Type Default Description
--db   path ai-memory.db (env: AI_MEMORY_DB) Path to the SQLite database file
--json   bool false Output as JSON (machine-parseable)

store

Store a new memory. Deduplicates by title+namespace (upserts on conflict).

Flag Short Type Default Description
--title -T string required Short descriptive title
--content -c string required Memory content (use - to read from stdin)
--tier -t string mid Memory tier: short, mid, or long
--namespace -n string auto-detected Namespace (auto-detects from git remote or directory name)
--tags   string "" Comma-separated tags
--priority -p int 5 Priority 1-10
--confidence   float 1.0 Confidence 0.0-1.0
--source -S string cli Source — one of VALID_SOURCES: user, nhi, claude (deprecated), hook, api, cli, import, consolidation, system, chaos, notify
--expires-at   string   Explicit expiry timestamp (RFC 3339). Overrides tier default
--ttl-secs   int   TTL in seconds. Overrides tier default

update

Update an existing memory by ID. Only the fields you provide are changed.

Flag Short Type Default Description
(positional)   string required Memory ID
--title -T string   New title
--content -c string   New content
--tier -t string   New tier: short, mid, or long
--namespace -n string   New namespace
--tags   string   New comma-separated tags
--priority -p int   New priority 1-10
--confidence   float   New confidence 0.0-1.0
--expires-at   string   Expiry timestamp (RFC 3339), or empty string to clear

recall

Recall memories relevant to a context. Fuzzy OR search with ranked results; auto-touches recalled memories.

Flag Short Type Default Description
(positional)   string required Context query string
--namespace -n string   Filter by namespace
--limit   int 10 Maximum number of results
--tags   string   Filter by tags (comma-separated)
--since   string   Only memories created after this timestamp (RFC 3339)
--until   string   Only memories created before this timestamp (RFC 3339)
--tier -T string   Feature tier for recall: keyword, semantic, smart, autonomous

Note: Default limit is 10 for CLI, 10 for MCP, capped at 50.


Search memories by text. AND semantics – all terms must match.

Flag Short Type Default Description
(positional)   string required Search query string
--namespace -n string   Filter by namespace
--tier -t string   Filter by tier: short, mid, or long
--limit   int 20 Maximum number of results
--since   string   Only memories created after this timestamp (RFC 3339)
--until   string   Only memories created before this timestamp (RFC 3339)
--tags   string   Filter by tags (comma-separated)

get

Retrieve a single memory by ID, including its links.

Flag Short Type Default Description
(positional)   string required Memory ID

list

Browse memories with filters and pagination.

Flag Short Type Default Description
--namespace -n string   Filter by namespace
--tier -t string   Filter by tier: short, mid, or long
--limit   int 20 Maximum number of results
--offset   int 0 Skip this many results (for pagination)
--since   string   Only memories created after this timestamp (RFC 3339)
--until   string   Only memories created before this timestamp (RFC 3339)
--tags   string   Filter by tags (comma-separated)

delete

Delete a memory by ID.

Flag Short Type Default Description
(positional)   string required Memory ID

promote

Promote a memory to long-term tier (clears expiry).

Flag Short Type Default Description
(positional)   string required Memory ID

forget

Bulk delete memories matching a pattern, namespace, and/or tier.

Flag Short Type Default Description
--namespace -n string   Delete only in this namespace
--pattern -p string   Delete memories matching this text pattern
--tier -t string   Delete only memories of this tier

Link two memories with a typed relation.

Flag Short Type Default Description
(positional 1)   string required Source memory ID
(positional 2)   string required Target memory ID
--relation -r string related_to Relation type (nine at v0.8.0): related_to, supersedes, contradicts, derived_from, reflects_on, derives_from, decomposes_into, depends_on, advances

consolidate

Consolidate multiple memories into one long-term summary. Deletes source memories.

Flag Short Type Default Description
(positional)   string required Comma-separated memory IDs (2-100)
--title -T string required Title for the consolidated memory
--summary -s string required Summary content for the consolidated memory
--namespace -n string   Namespace for the consolidated memory

resolve

Resolve a contradiction – mark one memory as superseding another. Creates a “supersedes” link and demotes the loser (priority=1, confidence=0.1).

Flag Short Type Default Description
(positional 1)   string required Winner memory ID (supersedes)
(positional 2)   string required Loser memory ID (superseded)

mcp

Run as an MCP (Model Context Protocol) tool server over stdio.

Flag Short Type Default Description
--tier   string semantic Feature tier: keyword, semantic, smart, or autonomous

serve

Start the HTTP memory daemon with automatic background garbage collection.

Flag Short Type Default Description
--host   string 127.0.0.1 Listen address
--port   int 9077 Listen port

shell

Launch the interactive memory REPL.

(No command-specific flags)


sync

Sync memories between two SQLite database files.

Flag Short Type Default Description
(positional)   path required Path to the remote database to sync with
--direction -d string merge Sync direction: pull, push, or merge

auto-consolidate

Automatically group memories by namespace and primary tag, then consolidate groups into long-term summaries.

Flag Short Type Default Description
--namespace -n string   Only consolidate this namespace
--short-only   bool false Only consolidate short-term memories
--min-count   int 3 Minimum memories in a group to trigger consolidation
--dry-run   bool false Show what would be consolidated without doing it

gc

Run garbage collection on expired memories.

(No command-specific flags)


stats

Show memory statistics (counts, tiers, namespaces, links, DB size).

(No command-specific flags)


namespaces

List all namespaces with memory counts.

(No command-specific flags)


export

Export all memories and links as JSON to stdout.

Export format: {"memories": [...], "links": [...], "count": N, "exported_at": "RFC3339"}. Import expects the same structure (count and exported_at are optional).

(No command-specific flags)


import

Import memories and links from JSON (reads from stdin).

(No command-specific flags)


completions

Generate shell completions for the specified shell.

Flag Short Type Default Description
(positional)   string required Shell type: bash, zsh, fish, elvish, powershell

man

Generate a roff man page to stdout. Pipe to man -l - to view.

(No command-specific flags)


mine

Import memories from historical conversations (Claude, ChatGPT, Slack exports).

Flag Short Type Default Description
(positional)   path required Path to the export file or directory
--format -f string required Export format: claude, chatgpt, slack
--namespace -n string auto-detected Namespace for imported memories
--tier -t string mid Memory tier for imported memories
--min-messages   int 3 Minimum message count to import a conversation
--dry-run   bool false Show what would be imported without writing

archive

Manage the memory archive. Has four sub-subcommands:

archive list

List archived memories.

Flag Short Type Default Description
--namespace -n string   Filter by namespace
--limit   int 50 Maximum number of results
--offset   int 0 Skip this many results (for pagination)

archive restore

Restore an archived memory back to active status (the archived row’s original_tier / original_expires_at are re-applied where present).

Flag Short Type Default Description
(positional)   string required Archived memory ID

archive purge

Permanently delete archived memories.

Flag Short Type Default Description
--older-than-days   int   Only purge entries older than N days (all if omitted)

archive stats

Show archive statistics (count, size, oldest/newest).

(No command-specific flags)


Memory Tiers Explained

Tier TTL Use Case Example
short 6 hours What you’re doing right now “Currently debugging auth flow in login.rs”
mid 7 days This week’s working knowledge “Sprint goal: migrate to new API v2”
long Forever Permanent knowledge “User prefers tabs over spaces”

Automatic Behaviors

Namespaces

Namespaces isolate memories per project. If you omit --namespace, it auto-detects from the git remote URL or the current directory name.

# These are equivalent when run inside a git repo named "my-app":
ai-memory store -T "API uses REST" -c "..." --namespace my-app
ai-memory store -T "API uses REST" -c "..."  # auto-detects "my-app"

List all namespaces:

ai-memory namespaces

Filter recall or search to a specific namespace:

ai-memory recall "auth flow" --namespace my-app

Memory Linking

Connect related memories with typed relations:

ai-memory link <source-id> <target-id> --relation supersedes

Valid relation types (nine at v0.8.0): related_to (default), supersedes, contradicts, derived_from, reflects_on, derives_from, decomposes_into, depends_on, advances.

When you get a memory, its links are shown alongside it:

ai-memory get <id>
# Shows the memory plus all its links

Contradiction Detection

When you store a memory, ai-memory automatically checks for existing memories in the same namespace with similar titles. If potential contradictions are found, you get a warning:

stored: abc123 [long] (ns=my-app)
warning: 2 similar memories found in same namespace (potential contradictions)

In JSON mode (--json), the response includes potential_contradictions with the IDs of conflicting memories, so you can review and resolve them.

Consolidation

After accumulating scattered memories about a topic, merge them into a single long-term summary:

ai-memory consolidate "id1,id2,id3" \
  -T "Auth system architecture" \
  -s "JWT tokens with refresh rotation, RBAC via middleware, sessions in Redis."

Consolidation:

Updating Memories

Update an existing memory by ID. Only the fields you provide are changed:

ai-memory update <id> -T "New title" -c "New content" --priority 8

# Set a custom expiration on an existing memory
ai-memory update <id> --expires-at "2026-06-01T00:00:00Z"

Listing with Pagination

Browse memories with filters and pagination using --offset:

# First page (default)
ai-memory list --namespace my-app --limit 20

# Second page
ai-memory list --namespace my-app --limit 20 --offset 20

# Third page
ai-memory list --namespace my-app --limit 20 --offset 40

Common Workflows

Start of Session

Recall context relevant to what you’re about to work on:

ai-memory recall "auth module refactor" --namespace my-app --limit 5

Learning Something New

When you discover something important during a session:

ai-memory store \
  -T "Rate limiter uses token bucket" \
  -c "The rate limiter in middleware.rs uses a token bucket algorithm with 100 req/min default." \
  --tier mid --priority 6

User Correction

When the user corrects you, store it as high-priority long-term:

ai-memory store \
  -T "User correction: always use snake_case for API fields" \
  -c "The user prefers snake_case for all JSON API response fields, not camelCase." \
  --tier long --priority 9 --source user

Promoting a Memory

If a mid-tier memory turns out to be permanently valuable:

ai-memory promote <memory-id>

Bulk Cleanup

Delete all short-term memories in a namespace:

ai-memory forget --namespace my-app --tier short

Delete memories matching a pattern:

ai-memory forget --pattern "deprecated API"

Time-Filtered Queries

List memories created in the last week:

ai-memory list --since 2026-03-23T00:00:00Z

Search within a date range:

ai-memory search "migration" --since 2026-01-01T00:00:00Z --until 2026-03-01T00:00:00Z

Importing Historical Conversations

Use the mine command to import memories from past conversations across platforms:

ai-memory mine /path/to/export --format claude     # Claude JSONL export
ai-memory mine /path/to/export.json --format chatgpt  # ChatGPT JSON export
ai-memory mine /path/to/slack-export/ --format slack   # Slack export directory

Supported formats (--format / -f flag values):

Additional flags: --namespace (override auto-detection), --tier (default: mid), --min-messages (minimum message count, default: 3), --dry-run (preview without writing).

This extracts key knowledge from historical conversations and stores them as memories, giving your AI assistant a head start with context it would otherwise have lost.

Export and Backup

ai-memory export > memories-backup.json

Restore (preserves links):

ai-memory import < memories-backup.json

Priority Guide

Priority When to Use
1-3 Low-value context, temporary notes
4-6 Standard working knowledge (default is 5)
7-8 Important architecture decisions, user preferences
9-10 Critical corrections, hard-won lessons, “never forget this”

Confidence

Confidence (0.0 to 1.0) indicates how certain a memory is. Default is 1.0. Lower confidence for things that might change:

ai-memory store \
  -T "API might switch to GraphQL" \
  -c "Team is evaluating GraphQL migration." \
  --confidence 0.5

Confidence is factored into recall scoring (weight: 2.0) – higher confidence memories rank higher.

Source Tracking

Every memory tracks its source. Valid sources:

Source Meaning
user Created by the user directly
claude Created by Claude during a session
hook Created by an automated hook
api Created via the HTTP API (default for API)
cli Created via the CLI (default for CLI)
nhi v0.7.x vendor-neutral default for AI NHI writes (#1175)
import Imported from a backup
consolidation Created by consolidating other memories
system System-generated
chaos Chaos-harness probe writes
notify Inbox rows written by memory_notify

Tags

Tag memories for filtered retrieval:

ai-memory store -T "Deploy process" -c "..." --tags "devops,ci,deploy"
ai-memory recall "deployment" --tags "devops"

Tags format: Tags are comma-separated strings in quotes: --tags "devops,ci,deploy". Max 50 tags, 128 bytes each. Tags may contain hyphens and underscores but not spaces or commas.

Interactive Shell

The shell command opens a REPL (read-eval-print loop) for browsing and managing memories interactively. Output uses color-coded tier labels and priority bars.

ai-memory shell

Available REPL commands:

Command Description
recall <context> (or r) Fuzzy recall with colored output
search <query> (or s) Keyword search
list [namespace] (or ls) List memories, optionally filtered by namespace
get <id> Show full memory details as JSON
stats Show memory statistics with tier breakdown
namespaces (or ns) List all namespaces with counts
delete <id> (or del, rm) Delete a memory
help (or h) Show command help
quit (or exit, q) Exit the shell

Type commands without the ai-memory prefix. Use up/down arrows for history. Exit with quit, exit, Ctrl+C, or Ctrl+D. The prompt shows ai-memory>.

In the shell, tier labels are color-coded: red for short, yellow for mid, green for long. Priority is shown as a visual bar (████░░░░░░).

Color Output

CLI output uses ANSI colors when connected to a terminal (auto-detected). Colors are suppressed when piping to a file or another command. Use --json for machine-parseable output.

Color scheme:

Multi-Node Sync

Sync memories between two SQLite database files. Useful for keeping laptop and server in sync, or merging memories from different machines.

# Pull all memories from remote database into local
ai-memory sync /path/to/remote.db --direction pull

# Push local memories to remote database
ai-memory sync /path/to/remote.db --direction push

# Bidirectional merge -- both databases end up with all memories
ai-memory sync /path/to/remote.db --direction merge

Sync uses the same dedup-safe upsert as regular stores:

Typical workflow (laptop and server):

# On laptop, mount remote DB (e.g., via sshfs or rsync'd copy)
scp server:/var/lib/ai-memory/ai-memory.db /tmp/remote-memory.db

# Merge both ways
ai-memory sync /tmp/remote-memory.db --direction merge

# Copy merged remote back
scp /tmp/remote-memory.db server:/var/lib/ai-memory/ai-memory.db

Auto-Consolidation

Automatically group memories by namespace and primary tag, then consolidate groups with enough members into a single long-term summary:

# Dry run -- see what would be consolidated
ai-memory auto-consolidate --dry-run

# Consolidate all namespaces (groups of 3+ memories)
ai-memory auto-consolidate

# Only short-term memories, minimum 5 per group
ai-memory auto-consolidate --short-only --min-count 5

# Only a specific namespace
ai-memory auto-consolidate --namespace my-project

How it works:

  1. Lists all namespaces (or the specified one)
  2. For each namespace, groups memories by their primary tag (first tag)
  3. Groups with >= min_count members are consolidated into one long-term memory
  4. The consolidated memory gets the title “Consolidated: tag (N memories)” and combines the content from all source memories
  5. Source memories are deleted; the new memory inherits the highest priority and all tags

Use --dry-run first to preview what would be consolidated.

Configurable TTL

Memory TTLs (time-to-live) can be customized per tier via config.toml. This lets you tune how long short-term and mid-term memories survive before expiring:

# ~/.config/ai-memory/config.toml
[ttl]
short_ttl_secs = 43200       # 12 hours (default: 21600 = 6 hours)
mid_ttl_secs = 1209600       # 14 days (default: 604800 = 7 days)
long_ttl_secs = 0            # never expires (default: 0)
short_extend_secs = 3600     # +1 hour on access (default: 3600)
mid_extend_secs = 86400      # +1 day on access (default: 86400)

Set any TTL to 0 to disable expiry for that tier. Values are clamped to a 10-year maximum.

CLI flags --ttl-secs and --expires-at on individual memories override the tier defaults.

Note: Configuration is loaded once at process startup. Changes to config.toml require restarting the ai-memory process (MCP server, HTTP daemon, or CLI) to take effect.

Archive Management

When archive_on_gc = true is set in config.toml, garbage collection archives expired memories instead of permanently deleting them. This gives you a safety net to recover accidentally expired memories.

List archived memories

ai-memory archive list

Restore an archived memory

ai-memory archive restore <id>

This moves the memory back to active status with its content intact; the archived row’s original_tier and original_expires_at are re-applied where present (legacy archive rows without those columns restore as long with no expiry).

Purge the archive

ai-memory archive purge
ai-memory archive purge --older-than-days 30   # only purge archives older than 30 days

Permanently deletes archived memories. This cannot be undone.

Archive statistics

ai-memory archive stats

Shows the total count, size, and date range of archived memories.

Contradiction Resolution

When two memories conflict, resolve the contradiction by declaring a winner:

ai-memory resolve <winner_id> <loser_id>

This command:

  1. Creates a “supersedes” link from the winner to the loser
  2. Demotes the loser (priority set to 1, confidence set to 0.1)
  3. Touches the winner (bumps access count, extends TTL)

The loser memory is not deleted – it remains searchable but ranks much lower due to its reduced priority and confidence.

Man Page

Generate and view the built-in man page:

# View immediately
ai-memory man | man -l -

# Install system-wide
ai-memory man | sudo tee /usr/local/share/man/man1/ai-memory.1 > /dev/null

Troubleshooting

Common issues, their causes, and how to fix them.

MCP server not showing tools after restart

Symptom: Your AI client does not list any memory_* tools after restarting.

Cause: The MCP configuration is in the wrong file, has a syntax error, or the ai-memory binary is not in PATH.

Solution:

  1. Verify the config is in the correct file for your platform (e.g., ~/.claude.json for Claude Code – not settings.json).
  2. Validate the JSON/TOML/YAML syntax (a trailing comma or missing bracket will silently fail).
  3. Run ai-memory mcp --tier semantic manually in a terminal. If it prints {"jsonrpc":"2.0",...} lines, the binary works. If it errors, fix the error first.
  4. Restart your AI client completely (not just a new conversation).

“database locked” error

Symptom: Operations fail with database is locked or SQLITE_BUSY.

Cause: Multiple processes are writing to the same SQLite database simultaneously (e.g., the MCP server and a CLI command, or two MCP servers).

Solution:

  1. Ensure only one MCP server instance runs per database file.
  2. If using the CLI while the MCP server is running, they can share the same database – SQLite WAL mode handles concurrent reads. Write conflicts are rare but possible under heavy load.
  3. If the error persists, stop all ai-memory processes, then run ai-memory gc to checkpoint the WAL and release any stale locks.

Embedding model download failed / hangs on first run

Symptom: The first recall at the semantic tier (or above) hangs for a long time or fails with a network error.

Cause: The semantic tier downloads the all-MiniLM-L6-v2 model (~90 MB) from Hugging Face on first use. Slow connections, firewalls, or proxy issues can block this.

Solution:

  1. Ensure you have internet access and can reach huggingface.co.
  2. If behind a corporate proxy, set HTTPS_PROXY before starting ai-memory.
  3. Wait – the first download can take a few minutes on slow connections. Subsequent runs use the cached model.
  4. If the download is corrupted, delete the cached model directory (~/.cache/huggingface/hub/models--sentence-transformers--all-MiniLM-L6-v2/) and retry.

“not found” when using a memory ID

Symptom: ai-memory get <id>, update, delete, or promote returns “not found”.

Cause: The ID is wrong, the memory was garbage-collected (expired), or you are pointing at a different database file.

Solution:

  1. Run ai-memory list to see current memory IDs.
  2. Check that you are using the same --db path as the MCP server or previous CLI session.
  3. If the memory expired, check the archive: ai-memory archive list. If archive_on_gc = true, expired memories are preserved there and can be restored with ai-memory archive restore <id>.

“already exists in namespace” on store

Symptom: Storing a memory reports that a memory with the same title already exists.

Cause: ai-memory deduplicates by title+namespace. A memory with that exact title already exists in the same namespace.

Solution: This is expected behavior – the existing memory is upserted (content updated, priority takes the higher value, tier never downgrades). If you want a separate memory, change the title to be distinct.

Ollama connection refused (smart/autonomous tier)

Symptom: Smart or autonomous tier tools (memory_expand_query, memory_auto_tag, memory_detect_contradiction) fail with “connection refused” or timeout errors.

Cause: The smart and autonomous tiers need an LLM backend. With the default ollama backend, Ollama must be running locally.

Solution:

  1. Install Ollama: curl -fsSL https://ollama.com/install.sh | sh
  2. Start it: ollama serve
  3. Pull the default model: ollama pull gemma3:4b (the v0.7.0 compiled default for the Ollama backend).
  4. Verify it is running: curl http://localhost:11434/api/tags should return a JSON response.
  5. If Ollama is on a non-default port or host, set [llm].base_url in ~/.config/ai-memory/config.toml (or AI_MEMORY_LLM_BASE_URL).
  6. Alternatively, skip Ollama entirely: post-#1067 any OpenAI-compatible vendor works via AI_MEMORY_LLM_BACKEND (+ the vendor API key). Run ai-memory doctor and check the “LLM Reachability (#1146)” section.

Binary not found in PATH after install

Symptom: Running ai-memory returns “command not found”.

Cause: The install script placed the binary in a directory that is not in your shell’s PATH, or you have not restarted your terminal.

Solution:

  1. Restart your terminal (or run source ~/.bashrc / source ~/.zshrc).
  2. If installed via cargo install, ensure ~/.cargo/bin is in your PATH.
  3. If installed via the install script, check where it placed the binary (usually ~/.local/bin or /usr/local/bin) and add that directory to PATH if needed.
  4. Verify: which ai-memory should print the binary path.

Config changes not taking effect

Symptom: You edited ~/.config/ai-memory/config.toml but the behavior has not changed.

Cause: Configuration is loaded once at process startup. The running MCP server or HTTP daemon is still using the old config.

Solution: Restart the ai-memory process. If running as an MCP server, restart your AI client (which restarts the MCP server). If running ai-memory serve, stop and restart it.

Archive table growing too large

Symptom: The database file keeps growing and ai-memory archive stats shows thousands of archived memories.

Cause: With archive_on_gc = true, every expired memory is preserved in the archive instead of being deleted. Over time this accumulates.

Solution:

  1. Purge old archives: ai-memory archive purge --older-than-days 30
  2. Purge everything: ai-memory archive purge
  3. To stop archiving entirely, set archive_on_gc = false in config.toml and restart.

“VALIDATION_FAILED” errors

Symptom: Store or update fails with a validation error mentioning title length, content size, or invalid priority/confidence.

Cause: ai-memory enforces input limits to keep the database healthy.

Solution: Check your input against these limits:

Adjust your input to fit within these constraints. If storing large content, split it into multiple memories or summarize it first.


FAQ

Q: Where is the database stored? A: By default, ai-memory.db in the current directory. Override with --db /path/to/db or the AI_MEMORY_DB environment variable.

Q: Do I need to run the HTTP daemon? A: No. The MCP server and CLI commands work directly against the SQLite database. The HTTP daemon is an alternative interface that adds automatic background garbage collection.

Q: What happens if I store a memory with a title that already exists in the same namespace? A: It upserts – the content is updated, the priority takes the higher value, and the tier never downgrades (a long memory stays long).

Q: How big can a memory be? A: Content is limited to 65,536 bytes (64 KB).

Q: What is recency decay? A: A factor of 1/(1 + days_old * 0.1) applied during recall ranking. A memory updated today gets a boost of 1.0, a memory from 10 days ago gets 0.5, and a memory from 100 days ago gets 0.09. This ensures recent memories are preferred when relevance is similar.

Q: Can I use this with AI tools other than Claude Code? A: Absolutely. ai-memory is AI-agnostic. The MCP server speaks standard JSON-RPC over stdio and works with any MCP-compatible client – Claude AI, OpenAI ChatGPT, xAI Grok, META Llama, and others. The HTTP API at http://127.0.0.1:9077/api/v1/ is completely platform-independent – any tool, framework, or script that can make HTTP requests can store and recall memories.

Q: Are there limits on bulk operations? A: Yes. Bulk create (POST /memories/bulk) and import (POST /import) are limited to 1,000 items per request to prevent abuse and memory exhaustion.

Q: Is the HTTP API safe from injection attacks? A: Yes. All FTS queries are sanitized – special characters are stripped and tokens are individually quoted before being passed to SQLite FTS5. The API also sanitizes error responses to avoid leaking internal database details to clients.