Skip to content

Repository files navigation

agentmemory.md

Persistent memory for AI agents — semantic search, knowledge graph, and 22 MCP tools.

Works with any MCP-compatible agent: Cursor, Claude Desktop, OpenClaw, and more.

Repository: https://github.com/tonyzorin/agentmemory
Website: https://agentmemory.md


Add to Cursor Claude Desktop

Prerequisites: Set AGENTMEMORY_HOST to your server's Tailscale IP before clicking "Add to Cursor":

# Add to ~/.bashrc or ~/.zshrc
export AGENTMEMORY_HOST=100.x.x.x

Then restart Cursor and click the button. See Connecting Agents for full setup.


The forgetting problem

Every session starts from zero. You re-explain the same project context. The agent re-asks questions you've already answered. It suggests approaches you already tried and rejected — without knowing why.

agentmemory.md gives your agent a brain that persists across every session and every tool:

  • Decisions with rationale — the agent never re-asks "should we use Redis or Postgres?" when you settled that three weeks ago
  • Project context — stack, repo path, deploy commands, environments — no more three-message warm-up
  • Failed experiments — root causes stored and recalled before the agent suggests the same approach again
  • Goals and tasks — the agent picks up exactly where you left off
  • People and relationships — stakeholders, customers, collaborators — who owns what, what customers have said
  • Preferences and patterns — coding style, tooling preferences, anti-patterns, reusable workflows

How it works

1. Deploy

Run on any Linux box — a VPS, homelab server, or Proxmox LXC. One command starts everything:

docker compose up -d

2. Connect via Tailscale

Install Tailscale on your server and laptop. Your server gets a stable private IP (100.x.x.x). No port forwarding, no firewall rules — Tailscale encrypts everything at the network layer.

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale ip -4  # note this IP

# Expose agentmemory to your tailnet (persists across reboots)
tailscale serve --bg --tcp 8081 tcp://localhost:8081

Port 8081 must not be open to the public internet without Bearer token auth enabled. For Tailscale-only access, tailscale serve binds port 8081 on your Tailscale IP so only devices in your tailnet can reach it — no firewall rules needed. For public HTTPS exposure, see HTTP authentication.

3. Add to your agent

Point Cursor, Claude Desktop, or any MCP-compatible agent at your server's Tailscale IP:

{
  "mcpServers": {
    "agentmemory": {
      "url": "http://100.x.x.x:8081/mcp"
    }
  }
}

Plain HTTP is safe here — Tailscale handles encryption at the network layer.

4. Add agent rules

Copy the rules file for your agent so it knows when to fetch and store memories automatically — without you having to ask every time.

Agent File Where to put it
All (reference) rules/CORE.md Shared policy — adapters below include this
Cursor rules/cursor.mdc .cursor/rules/agentmemory.mdc in your project
Claude Desktop rules/CLAUDE.md Project → Set custom instructions → paste contents
Claude Code rules/CLAUDE.md Project root as CLAUDE.md
ChatGPT rules/chatgpt-instructions.md Settings → Personalization → Custom Instructions
Grok rules/grok-instructions.md Custom instructions + MCP connector (see OAuth in README)

Without rules, the agent only uses memory when you explicitly ask. With rules, it fetches context automatically at the start of each session and stores decisions as they happen.

5. Remember

Your agent now stores decisions, recalls context, tracks goals, and picks up exactly where you left off — across every session and every tool.


Architecture

AI Agent (Cursor / Claude Desktop / OpenClaw / any MCP client)
    │
    ├─[MCP / HTTP]──► MCP Server (FastMCP v3)
    │                     │
    │                     ▼
    │              Memory Core Library
    │              ├── Embeddings (BAAI/bge-base-en-v1.5, 768-dim)
    │              ├── Hybrid Retrieval (Redis FT.HYBRID + AGE graph)
    │              └── Storage
    │                   ├── Redis 8.6 (FT.HYBRID: BM25 + vector, RRF fusion)
    │                   └── PostgreSQL 18 + Apache AGE 1.7.0 (graph)
    │
    └─[CLI]──────► memory store / recall / goal / task / ...
                   mem store / recall / ...  (short alias)

Retrieval pipeline

flowchart LR
    Query --> Expand["Query Expansion\n(BM25 synonyms)"]
    Expand --> Hybrid["Hybrid Search\n(Redis FT.HYBRID\nBM25 + Vector, RRF)"]
    Hybrid --> Reranker["Cross-Encoder\nReranker\n(optional)"]
    Reranker --> GraphBoost["Graph Boost\n(AGE neighborhood\n1-hop +0.3, 2-hop +0.1)"]
    GraphBoost --> Score["Final Scoring\n(similarity + graph\n+ recency + importance)"]
    Score --> Filter["Superseded\nFilter"]
    Filter --> TopN["Top-N results\nto LLM context"]
Loading

The reranker is disabled by default. Enable it when your corpus exceeds ~1000 nodes or you need higher retrieval precision (see Reranker below).


System Requirements

Runs comfortably on a small VPS or homelab node.

Component Minimum
CPU 2+ cores — embeddings run on CPU, no GPU needed
RAM 4 GB — PostgreSQL + Redis + embedding model
Disk 2 GB for Docker images, grows with your memory corpus
OS Any Linux with Docker — Ubuntu, Debian, Proxmox LXC
Network Tailscale recommended for secure private access

Quick Start

# Start storage services
docker compose up -d

# Install
pip install -e .

# Store a memory
memory store "Anton prefers Claude for coding tasks" --tags preferences,tools

# Recall
memory recall "what does Anton prefer for coding"

# Get user profile
memory profile

# Stats
memory stats

HTTP authentication

For public internet exposure (HTTPS via Caddy or Cloudflare), enable Bearer token auth on the MCP HTTP/SSE endpoint. The LLM never sees the token — your MCP client sends it in the Authorization header.

1. Generate a token (on the server)

docker compose run --rm app mem token create

Copy the raw token immediately. Add the hash to your server .env:

AGENTMEMORY_AUTH_REQUIRED=true
AGENTMEMORY_TOKEN_HASHES=<sha256-hex-from-mem-token-create>

Restart the app: docker compose up -d --build app

2. Configure clients

Cursor (~/.cursor/mcp.json):

{
  "mcpServers": {
    "agentmemory": {
      "url": "https://mem.yourdomain.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:AGENTMEMORY_TOKEN}"
      }
    }
  }
}

Set AGENTMEMORY_TOKEN in your shell profile (never commit the raw token).

Claude Desktop (via mcp-remote):

{
  "mcpServers": {
    "agentmemory": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://mem.yourdomain.com/mcp",
        "--header", "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "Bearer am_..."
      }
    }
  }
}

Use env for the token value to avoid Windows/Cursor space-mangling bugs in args.

Claude Code:

claude mcp add --transport http agentmemory https://mem.yourdomain.com/mcp \
  --header "Authorization: Bearer $AGENTMEMORY_TOKEN"

OpenClaw / stdio: unchanged — local process, no Bearer auth.

OAuth (browser MCP clients)

Some browser-based MCP clients cannot paste a static Bearer token — they need OAuth with PKCE. agentmemory exposes /authorize and /token without /.well-known discovery so Cursor static Bearer headers keep working on the same host.

1. Enable on the server

Google Sign-In (recommended) — set in .env:

AGENTMEMORY_OAUTH_ENABLED=true
AGENTMEMORY_GOOGLE_CLIENT_ID=<from Google Cloud Console>
AGENTMEMORY_GOOGLE_CLIENT_SECRET=<from Google Cloud Console>
AGENTMEMORY_OAUTH_ALLOWED_EMAIL=tonyzorin@gmail.com
AGENTMEMORY_OAUTH_CLIENT_ID=agentmemory
AGENTMEMORY_PUBLIC_BASE_URL=https://mem.agentmemory.md

Add authorized redirect URI in Google Cloud Console:

https://mem.agentmemory.md/oauth/google/callback

Or password consent (fallback):

mem oauth password-hash   # prints AGENTMEMORY_OAUTH_PASSWORD_HASH

Proxy /authorize, /token, and /oauth/google/* in Caddy. Restart the app.

2. Custom connector (paste these in the client's OAuth form)

Field Value
Server URL https://mem.agentmemory.md/mcp
Client ID agentmemory
Client Secret (leave blank)
Authorization Endpoint https://mem.agentmemory.md/authorize
Token Endpoint https://mem.agentmemory.md/token
Scopes memory:full
Token Auth Method none (PKCE only)

Click Save & Connect, then Sign in with Google as tonyzorin@gmail.com (or enter the allowed email + password if using password mode).

Both auth methods run together — enabling OAuth does not replace Bearer tokens. Cursor keeps using Authorization: Bearer am_… in mcp.json; OAuth clients use amo_… tokens. /mcp accepts either via MultiAuth.

OAuth sessions are in-memory; restarting the container invalidates OAuth tokens (re-authorize). OAuth access tokens expire after 7 days. Bearer API key hashes are unaffected.

Google Sign-In binds the authorize session to an HttpOnly cookie (am_oauth_sid) to block login CSRF. If a Google OAuth client secret is ever exposed, rotate it in Google Cloud Console and update AGENTMEMORY_GOOGLE_CLIENT_SECRET on the server.

Deploy shape

  1. App listens on 127.0.0.1:8081 (or your port)
  2. Caddy/Cloudflare terminates TLS and proxies to localhost
  3. AGENTMEMORY_AUTH_REQUIRED=true + token hashes in .env
  4. For OAuth: also proxy /authorize, /token, /oauth/google/*; keep /.well-known/* as 404 on the Cursor hostname
  5. Never expose the raw Docker port publicly without TLS and auth

Connecting Agents

Connect — Cursor IDE

Cursor supports the modern Streamable HTTP transport natively. In ~/.cursor/mcp.json:

{
  "mcpServers": {
    "agentmemory": {
      "url": "http://100.x.x.x:8081/mcp"
    }
  }
}

Replace 100.x.x.x with your server's Tailscale IP. If HTTP authentication is enabled, add the headers block with Authorization: Bearer ${env:AGENTMEMORY_TOKEN}.


Connect — Claude Desktop

Option A — One-click Desktop Extension (recommended)

Download agentmemory.mcpb and double-click it. Claude Desktop will open an install dialog and prompt you for your server's Tailscale IP — no config file editing required.

Requires Claude Desktop 1.0.0 or later. See Anthropic's Desktop Extensions docs for more detail.

Option B — Manual setup

Claude Desktop does not natively support Streamable HTTP. Use mcp-remote as a bridge. In ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "agentmemory": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://100.x.x.x:8081/mcp",
        "--allow-http"
      ]
    }
  }
}

Replace 100.x.x.x with your server's Tailscale IP. The --allow-http flag is needed because mcp-remote defaults to HTTPS — Tailscale traffic is already encrypted at the network layer so plain HTTP is safe here.

Restart Claude Desktop after saving.


Connect — OpenClaw

For OpenClaw (stdio mode), see OPENCLAW_SETUP.md. For automatic recall/capture, see the agentmemory-openclaw-plugin.


Agent Rules (v2026.03.15)

Ready-to-use rule files that teach your AI agent when to fetch and store memories. Copy the one matching your client:

Client File Install to
All (reference) rules/CORE.md Shared fetch/store policy
Cursor rules/cursor.mdc .cursor/rules/agentmemory.mdc
Claude Code rules/CLAUDE.md Project root as CLAUDE.md
ChatGPT rules/chatgpt-instructions.md Settings → Personalization → Custom Instructions
Grok rules/grok-instructions.md Custom instructions + MCP connector
OpenClaw rules/openclaw.md See OPENCLAW_SETUP.md

These rules implement tiered context fetching — the agent loads only the memory context needed for each request, from zero (bug fixes) to full profile (explicit memory queries). See AGENTS.md for the full specification.

After copying, customize the project tags section for your own projects.


Stack

Component Technology
Language Python 3.14
MCP Server FastMCP v3 (Streamable HTTP on port 8081)
Vector Search Redis 8.6 (FT.HYBRID: BM25 + vector, RRF fusion)
Knowledge Graph PostgreSQL 18 + Apache AGE 1.7.0
Embeddings BAAI/bge-base-en-v1.5 (768-dim, CPU, MTEB ~63)
CLI Click + Rich (memory or mem)

Scoring

Every memory_recall result includes these fields:

Field Description
score Final combined score (0–1)
similarity Normalized RRF score from Redis FT.HYBRID, or cross-encoder score if reranker is enabled
reranker_score Cross-encoder score (0–1) — present only when reranker is enabled
graph_boost +0.3 for 1-hop graph neighbors, +0.1 for 2-hop
recency Exponential decay: 1.0 = just stored, ~0.5 = 29 days old
importance Stored importance value (varies by node type)

Formula (similarity-adaptive):

  • Strong match (similarity > 0.6): (sim×0.80 + graph×0.15 + recency×0.05) × importance_weight
  • Weak/medium match: (sim×0.50 + graph×0.20 + recency×0.20 + importance×0.10) × importance_weight

Reranker

A cross-encoder reranker sits between hybrid search and the final scoring step. When enabled, it scores the top-K candidates jointly with the query, replacing the RRF similarity score with a more nuanced relevance score.

Enable with:

RERANKER_ENABLED=true
RERANKER_MODEL=cross-encoder/ms-marco-MiniLM-L6-v2  # default, CPU-feasible
RERANKER_TOP_K=20  # number of candidates to rerank

Or in docker-compose:

environment:
  RERANKER_ENABLED: "true"

Model choices:

  • cross-encoder/ms-marco-MiniLM-L6-v2 — 22M params, ~50ms for top-20 on CPU, good quality (default)
  • cross-encoder/ms-marco-MiniLM-L12-v2 — 33M params, slightly better quality
  • BAAI/bge-reranker-v2-m3 — multilingual, strong MTEB scores

When to enable: Corpus of 1000+ nodes, or if you notice relevant memories being ranked below less-relevant ones. At small corpus sizes the improvement is marginal.

The model is lazy-loaded on first use and uses the same sentence-transformers dependency already in the stack — no new installs required.


Knowledge Graph

17 node types: Memory, Learning, Decision, Goal, Initiative, Task, Project, Person, ExternalContact, Preference, Environment, Tool, Workflow, Resource, Competitor, Metric, CustomerFeedback

26 edge types: WORKS_ON, ABOUT, BELONGS_TO, FOR, ACHIEVED_VIA, BROKEN_INTO, TRACKS, COMPETES_WITH, PREVENTED, SUPERSEDES, and more.

Conflict resolution with SUPERSEDES

When a new memory contradicts or replaces an older one, mark the old one as superseded:

# New memory that replaces an old one
result = memory_store("Anton switched to Rust for systems work", node_type="Preference")

# memory_store returns potential_conflict if a similar memory (0.75–0.92) already exists:
# {"id": "...", "potential_conflict": {"id": "<old-id>", "content": "Anton prefers Python..."}}

# Supersede the old one — it will be excluded from future search results
memory_supersede(new_id=result["id"], old_id="<old-id>")

Superseded nodes are kept in the graph for audit purposes but filtered from all memory_recall results.


MCP Tools (22)

Core memory: memory_store, memory_recall, memory_update, memory_relate, memory_context, memory_forget, memory_supersede, memory_entities, memory_split, memory_batch_update, memory_profile

Work structure: goal_manage, initiative_manage, task_manage, timeline

Knowledge: learning_store, workflow_store

Market intelligence: competitor_manage, metric_record, metric_query, customer_feedback_store


CLI Reference

memory and mem are interchangeable — mem is the short alias.

mem store "content" [--type Memory|Learning|Decision|...] [--tags tag1,tag2] [--importance 0.8]
mem recall "query" [--limit 10] [--type Memory]
mem profile [--no-recent] [--limit 20]
mem update <memory-id> [--content "..."] [--name "..."] [--tags tag1,tag2] [--importance 0.8]
mem forget <memory-id> [--yes]
mem learn "what failed" --what-failed "..." --why "..." --avoid "..."
mem decide "decision" --rationale "why"
mem goal create "name" [--project <id>]
mem goal list
mem initiative create "name" [--goal <id>]
mem task create "name" [--initiative <id>]
mem task done <task-id> [--summary "what was done"]
mem task list
mem relate <from-id> <to-id> <EDGE_TYPE>
mem context <entity-id> [--depth 2]
mem timeline [--since 7d] [--type Memory]
mem graph <entity-id> [--depth 2]
mem competitor add "name" [--website url] [--positioning "..."]
mem metric record "name" <value> [--type visitors] [--unit count]
mem metric query "name" [--since 30d]
mem workflow "name" "description" [--step "step 1"] [--step "step 2"]
mem split <memory-id> --chunk "fact 1" --chunk "fact 2"
mem batch-update --type Goal --importance 0.8
mem consolidate [--dry-run] [--no-dry-run] [--threshold 0.85] [--type Memory]
mem gc [--dry-run] [--type Task]
mem stats
mem export [-o backup.json]
mem import backup.json

memory consolidate — merge near-duplicate nodes

Run periodically to keep the corpus clean as it grows:

# Preview what would be merged (safe — no changes)
memory consolidate

# Actually merge near-duplicates (asks for confirmation)
memory consolidate --no-dry-run

# More conservative threshold (only very close duplicates)
memory consolidate --no-dry-run --threshold 0.90

# Only consolidate Memory nodes
memory consolidate --no-dry-run --type Memory

The command groups semantically similar nodes into clusters and merges each cluster into its highest-importance node, preserving content and re-attaching graph edges.


agentmemory.md vs a flat memory file

Many agent frameworks offer simple flat-file memory (a markdown file with notes). Here's an honest comparison:

Flat file agentmemory.md
Setup Zero — just a file Docker + PostgreSQL + Redis
Reliability Always works Requires running services
Corpus limit ~50–100KB before context overflow Unlimited
Retrieval at scale LLM reads everything (works well <100KB) Hybrid search + graph boost (needed >100KB)
Structure Unstructured text Typed nodes, edges, relationships
MCP API Agent edits text file (fragile) 22 purpose-built tools
Conflict handling Manual SUPERSEDES edges + potential_conflict detection
Maintenance None gc, consolidate, reindex

Use a flat file when: You have one project, short memory, and don't want infrastructure.
Use agentmemory.md when: You have multiple projects, growing history, or want structured goal/task/decision tracking.


Two ways to run it

Self-hosted — Free
Open source. Your data stays on your server. Full control over every component. Docker Compose setup, all 22 MCP tools, CLI included, runs on any Linux box.

Managed — Coming soon
Zero infrastructure. We run the server, handle updates, and keep your memory available everywhere. Join the waitlist.


Running Tests

# Start services first
docker compose up -d

# Run all tests
pytest

# Run specific phase
pytest tests/test_redis_client.py -v
pytest tests/test_age_client.py -v
pytest tests/test_e2e.py -v

About

Persistent memory for AI agents

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages