DEV Community

Cover image for How to Add Persistent Memory to Any AI Agent (So It Remembers Yesterday)
Wanda
Wanda

Posted on • Originally published at apidog.com

How to Add Persistent Memory to Any AI Agent (So It Remembers Yesterday)

TL;DR

Add persistent memory to AI agents in 4 steps:

  1. Set up an MCP memory server with remember, recall, search, and rollback tools.
  2. Add memory instructions to agent prompts.
  3. Configure ~/.claude/settings.json for Claude Code or .cursor/mcp.json for Cursor.
  4. Use memory patterns for decision logging, agent handoffs, and session checkpoints.

Agents retain context across sessions—no more copy-pasting previous conversations.

Try Apidog today

Solve the “I don’t remember yesterday” problem. Add persistent memory to AI agents using MCP protocol, and they’ll recall decisions, deliverables, and context from previous sessions.

You know the drill:

Day 1: "Build the user authentication system"
Agent: [Builds JWT auth, creates users table, implements refresh tokens]

Day 2: "Continue from yesterday"
Agent: "I don't have context from previous sessions. Can you paste what we did?"
Enter fullscreen mode Exit fullscreen mode

You copy-paste the previous conversation. The agent reads 2000 lines of context. You both waste 15 minutes getting back up to speed.

Persistent memory fixes this. With MCP (Model Context Protocol) memory, agents store decisions automatically and recall them when needed. No copy-paste. No re-explanation.

In this guide, you’ll set up MCP memory for AI agents. You’ll see how to store decisions from Backend Architect sessions, recall context when switching to Database Optimizer, and hand off deliverables to Frontend Developer—all without losing context. These memory patterns work whether you’re building APIs with Apidog integration or managing multi-day development sprints.

What Is MCP Memory?

MCP memory lets AI agents store and retrieve information across sessions. Think of it as a shared notebook that agents can write to and read from.

Four tools power MCP memory:

Tool Purpose Example
remember Store information with tags Save “users table with UUID, bcrypt”
recall Search by keyword or tag Find “auth decisions”
rollback Restore to previous state Undo bad schema changes
search Find across sessions “What did Backend Architect decide?”
┌─────────────────┐         ┌──────────────────┐         ┌─────────────┐
│  AI Agent       │         │  MCP Memory      │         │  Storage    │
│  (Claude Code)  │◄───────►│  Server          │◄───────►│  (SQLite)   │
└─────────────────┘   JSON  └──────────────────┘  I/O    └─────────────┘
Enter fullscreen mode Exit fullscreen mode

Step 1: Set Up an MCP Memory Server

You need an MCP server that exposes memory tools. There are several open-source implementations. Here are two options:

Option A: Use a hosted memory server

npm install -g @example/mcp-memory-server
Enter fullscreen mode Exit fullscreen mode

Option B: Run a simple local server

Create memory-server.js:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

const MEMORY_FILE = path.join(process.env.HOME, ".mcp-memory", "memories.json");

const server = new McpServer({
  name: "memory",
  version: "1.0.0"
});

// Ensure memory file exists
async function initMemory() {
  await fs.mkdir(path.dirname(MEMORY_FILE), { recursive: true });
  try {
    await fs.access(MEMORY_FILE);
  } catch {
    await fs.writeFile(MEMORY_FILE, JSON.stringify([]));
  }
}

// Tool: remember
server.tool(
  "remember",
  {
    content: z.string().describe("Information to store"),
    tags: z.array(z.string()).describe("Tags for retrieval (e.g., ['backend', 'auth'])"),
    agent: z.string().optional().describe("Agent name for tagging")
  },
  async ({ content, tags, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const memory = {
      id: Date.now().toString(),
      content,
      tags,
      agent,
      timestamp: new Date().toISOString()
    };
    memories.push(memory);
    await fs.writeFile(MEMORY_FILE, JSON.stringify(memories, null, 2));
    return { content: [{ type: "text", text: `Stored memory with tags: ${tags.join(", ")}` }] };
  }
);

// Tool: recall
server.tool(
  "recall",
  {
    query: z.string().describe("Search query or tag to find"),
    agent: z.string().optional().describe("Filter by agent name")
  },
  async ({ query, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories.filter(m => {
      const matchesQuery = m.content.toLowerCase().includes(query.toLowerCase()) ||
                          m.tags.some(t => t.toLowerCase().includes(query.toLowerCase()));
      const matchesAgent = !agent || m.agent === agent;
      return matchesQuery && matchesAgent;
    });
    return {
      content: [{
        type: "text",
        text: results.length === 0
          ? "No memories found"
          : results.map(m => `[${m.timestamp}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: search
server.tool(
  "search",
  {
    tags: z.array(z.string()).describe("Tags to search for"),
    limit: z.number().optional().default(10)
  },
  async ({ tags, limit }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories
      .filter(m => tags.some(t => m.tags.includes(t)))
      .slice(0, limit);
    return {
      content: [{
        type: "text",
        text: results.map(m => `[${m.agent || "unknown"}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: rollback
server.tool(
  "rollback",
  {
    agent: z.string().describe("Agent name to rollback"),
    timestamp: z.string().describe("Rollback to this timestamp")
  },
  async ({ agent, timestamp }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const rolledBack = memories.filter(m =>
      m.agent !== agent || new Date(m.timestamp) <= new Date(timestamp)
    );
    await fs.writeFile(MEMORY_FILE, JSON.stringify(rolledBack, null, 2));
    return {
      content: [{
        type: "text",
        text: `Rolled back ${agent} to ${timestamp}`
      }]
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
Enter fullscreen mode Exit fullscreen mode

Run the server:

node memory-server.js
Enter fullscreen mode Exit fullscreen mode

Step 2: Add Memory Instructions to Any Agent

You don’t need to modify agent files. Add memory instructions in your prompt:

You have access to MCP memory tools: remember, recall, search, rollback.

Follow these memory protocols:

**When you start a session:**
1. Recall context: recall(query="ecommerce-api", agent="Backend Architect")
2. Review pending items from last session

**When you complete work:**
1. Remember deliverables with tags:
   - remember(content="Created users table with UUID primary key, bcrypt password hashing, JWT auth with refresh tokens", tags=["ecommerce-api", "database", "auth"], agent="Backend Architect")
2. Include decisions made and pending items

**When handing off to another agent:**
1. Leave context for the receiving agent:
   - remember(content="API endpoints: /auth/login, /auth/register, /products, /orders. Auth flow: JWT access token (15min) + refresh token (7 days). Known issue: rate limiting not yet implemented", tags=["ecommerce-api", "handoff"], agent="Backend Architect", for="Frontend Developer")

**When something fails:**
1. Search for last known-good state
2. Use rollback to restore
Enter fullscreen mode Exit fullscreen mode

Example in action:

You: "Activate Backend Architect mode. Design the user system for our e-commerce API."

[Agent designs the system, creates schema, implements auth]

Agent: "User system complete. Storing memory..."
→ Calls: remember("users table with UUID, bcrypt, JWT + refresh tokens", tags: ["ecommerce-api", "auth", "database"])

[Session ends]

[Next day]

You: "Continue from yesterday"

Agent: "Recalling context..."
→ Calls: recall(query="ecommerce-api")
→ Returns: "users table with UUID, bcrypt, JWT + refresh tokens"

Agent: "Yesterday I designed the users table with UUID primary keys, implemented bcrypt password hashing, and set up JWT authentication with refresh tokens. Next step: product catalog schema. Ready to continue."
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure for Claude Code

Add the memory server to your MCP configuration.

Edit ~/.claude/settings.json:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"],
      "env": {
        "HOME": "/Users/your-username"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Restart Claude Code. The memory tools should now be available.

Test it:

Use the remember tool to store: "Test memory for ecommerce project"
Tags: ["test", "ecommerce-api"]
Enter fullscreen mode Exit fullscreen mode
Use the recall tool to find memories about "test"
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure for Cursor

Create .cursor/mcp.json in your project:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Test it:

@memory remember "Starting ecommerce API project with PostgreSQL"
Tags: ["ecommerce-api", "setup"]
Enter fullscreen mode Exit fullscreen mode
@memory recall query="ecommerce"
Enter fullscreen mode Exit fullscreen mode

Memory Patterns for Real Workflows

Pattern 1: Decision Logging

Log every technical decision:

remember({
  content: "Chose PostgreSQL over MySQL for: (1) JSONB support for flexible product attributes, (2) better full-text search, (3) UUID native support",
  tags: ["ecommerce-api", "database", "decision"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

Later, retrieve the decision:

recall(query="PostgreSQL MySQL decision")
Enter fullscreen mode Exit fullscreen mode

Pattern 2: Agent Handoffs

Leave a handoff note when switching agents:

remember({
  content: "Backend complete. Endpoints: POST /auth/login, POST /auth/register, GET /products, POST /orders. Auth: JWT 15min access + 7 day refresh. Pending: rate limiting, email verification. Frontend needs: login form, product list, cart, checkout.",
  tags: ["ecommerce-api", "handoff", "backend-complete"],
  agent: "Backend Architect",
  for: "Frontend Developer"
})
Enter fullscreen mode Exit fullscreen mode

Frontend Developer starts with:

recall(query="handoff", agent="Backend Architect")
Enter fullscreen mode Exit fullscreen mode

Pattern 3: Session Checkpoints

At the end of each work session:

remember({
  content: "Session complete. Done: users table, auth endpoints, product schema. Next session: order system, payment webhook. Blockers: waiting for Stripe API keys.",
  tags: ["ecommerce-api", "checkpoint", "session-1"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

Resume next session:

recall(query="checkpoint session-1")
Enter fullscreen mode Exit fullscreen mode

Pattern 4: Bug Tracking

When you find a bug:

remember({
  content: "BUG: Refresh token not expiring after logout. Token stored in memory, not persisted. Fix: move to Redis with TTL.",
  tags: ["ecommerce-api", "bug", "auth"],
  agent: "Code Reviewer",
  severity: "high"
})
Enter fullscreen mode Exit fullscreen mode

Search for bugs later:

search(tags=["bug", "ecommerce-api"])
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

Memory not persisting:

  • Check the memory file path (~/.mcp-memory/memories.json)
  • Ensure the MCP server is running
  • Verify Claude Code/Cursor has the MCP config

Too many results on recall:

  • Add more specific tags
  • Filter by agent name
  • Use exact phrases in quotes

Memory file growing large:

  • Periodically archive old memories
  • Use rollback to clean up completed projects
  • Add expiration dates to your memory schema

What You Built

Component Purpose
MCP Memory Server Stores/retrieves information across sessions
remember tool Log decisions, deliverables, handoffs
recall tool Find context from previous sessions
search tool Query by tags across all memories
rollback tool Restore to previous state when needed
Memory patterns Decision logging, handoffs, checkpoints, bug tracking

Next Steps

Extend the memory server:

  • Add semantic search with embeddings
  • Implement memory expiration (auto-archive after 30 days)
  • Add memory summarization (condense long sessions)

Build team memory:

  • Share a central memory server across your team
  • Tag memories by project and developer
  • Create onboarding flows for new team members

Integrate with tools:

  • Auto-log git commits as memories
  • Sync with project management (Jira, Linear)
  • Export memories to documentation

Troubleshooting Common Issues

Memory not persisting between sessions:

  • Check that the MCP server is running before starting Claude Code
  • Verify the memory file path exists: ls -la ~/.mcp-memory/memories.json
  • Ensure file permissions allow read/write: chmod 644 ~/.mcp-memory/memories.json
  • Confirm the server config in ~/.claude/settings.json points to the correct path

Recall returns empty results:

  • Verify the query matches stored tags (case-sensitive)
  • Try broader search terms or use search with specific tags
  • Check if memories were actually stored: cat ~/.mcp-memory/memories.json
  • Ensure the agent name filter matches (if using agent parameter)

Memory file growing too large:

  • Implement automatic archiving for memories older than 30 days
  • Add a prune tool that deletes memories by date range
  • Split memories into separate files by project or date
  • Use a database backend (SQLite) instead of JSON for large-scale use

Server fails to start:

  • Check Node.js version: node --version (should be 18+)
  • Install missing dependencies: npm install @modelcontextprotocol/sdk zod
  • Look for syntax errors in the server code
  • Run directly to see errors: node memory-server.js

Multiple agents overwriting each other’s memories:

  • Always include the agent field when calling remember
  • Use unique tags per project: ["project-x", "backend", "auth"]
  • Filter recall by agent name when retrieving context
  • Consider separate memory files per project

Memory Server Security Considerations

API Key Storage: If your memory server stores sensitive data (API keys, passwords), implement encryption:

import crypto from 'crypto';

const ENCRYPTION_KEY = process.env.MEMORY_ENCRYPTION_KEY;
const ALGORITHM = 'aes-256-gcm';

function encrypt(text) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY), iv);
  const encrypted = cipher.update(text, 'utf8', 'hex');
  return {
    encryptedData: encrypted + cipher.final('hex'),
    iv: iv.toString('hex'),
    authTag: cipher.getAuthTag().toString('hex')
  };
}

function decrypt(encrypted) {
  const decipher = crypto.createDecipheriv(
    ALGORITHM,
    Buffer.from(ENCRYPTION_KEY),
    Buffer.from(encrypted.iv, 'hex')
  );
  decipher.setAuthTag(Buffer.from(encrypted.authTag, 'hex'));
  return decipher.update(encrypted.encryptedData, 'hex', 'utf8') + decipher.final('utf8');
}
Enter fullscreen mode Exit fullscreen mode

Access Control: For team memory servers, add authentication:

  • Require API key in memory tool calls
  • Implement user-specific memory namespaces
  • Log all memory operations for audit trails
  • Rate limit requests per user

Your AI agents now have persistent memory. They remember yesterday. They recall decisions. They hand off context without copy-paste.

No more “I don’t have context from previous sessions.” No more re-explaining. No more wasted time.

That’s the power of MCP memory: give your agents a shared notebook, and watch them become actually useful across multi-day projects.

FAQ

What is MCP memory?

MCP memory is a protocol implementation that lets AI agents store and retrieve information across sessions. Think of it as a shared notebook agents can write to and read from, persisting context beyond single conversations.

How do I set up persistent memory for Claude Code?

Install an MCP memory server, then add it to ~/.claude/settings.json with the server command and path. Restart Claude Code and the memory tools (remember, recall, search, rollback) become available.

Which AI agents support MCP memory?

Any agent running on MCP-compatible clients (Claude Code, Cursor, Windsurf) can use memory tools. You don’t need to modify agent files—just add memory instructions to your prompts.

What are the best memory patterns for agent handoffs?

Use remember with tags like ["handoff", "project-name"] to leave context for the next agent. Include completed work, pending items, and known issues. The receiving agent calls recall(query="handoff") to retrieve it.

How much memory can MCP servers store?

Depends on implementation. The reference server uses a JSON file that grows indefinitely. Production servers should add expiration policies, auto-archiving, or database backends for large-scale use.

Can teams share a central memory server?

Yes. Run the memory server on a shared machine or cloud instance, configure all team members’ clients to connect to it, and tag memories by project and developer for organized retrieval.

What if memory recall returns too many results?

Add more specific tags when storing memories. Filter by agent name in your recall queries. Use exact phrases in quotes. Consider implementing semantic search with embeddings for smarter retrieval.

Top comments (0)