TL;DR
Add persistent memory to AI agents in four 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; and (4) use memory patterns for decision logging, agent handoffs, and session checkpoints. Agents retain context across sessions, so you can stop copy-pasting previous conversations.
Persistent MCP memory solves the “I don’t remember yesterday” problem. Your agents can store decisions, deliverables, and project context, then retrieve them in a later session.
Typical workflow without persistent memory:
Day 1: "Build the user authentication system"
Agent: Builds JWT auth, creates a 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?"
You paste the previous conversation, the agent reads thousands of lines of context, and you spend time rebuilding shared understanding.
With MCP memory, the agent stores useful project context as it works and recalls it when needed. This works for single-agent sessions and multi-agent workflows, such as handing backend decisions to a frontend agent while building APIs with Apidog integration.
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.
| Tool | Purpose | Example |
|---|---|---|
remember |
Store information with tags | Save “users table with UUID, bcrypt” |
recall |
Search by keyword or agent | Find “auth decisions” |
search |
Find memories by tags | Find all ecommerce bugs |
rollback |
Restore a previous state | Undo bad schema changes |
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ AI Agent │ │ MCP Memory │ │ Storage │
│ (Claude Code) │◄───────►│ Server │◄───────►│ (SQLite) │
└─────────────────┘ JSON └──────────────────┘ I/O └─────────────┘
Step 1: Set Up an MCP Memory Server
You need an MCP server that exposes memory tools. You can use an existing implementation or run a local server.
Option A: Use a hosted memory server
npm install -g @example/mcp-memory-server
Option B: Run a simple local server
Install the dependencies:
npm install @modelcontextprotocol/sdk zod
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 the memory file exists before any tool reads or writes it.
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([]));
}
}
// Store a memory entry.
server.tool(
"remember",
{
content: z.string().describe("Information to store"),
tags: z
.array(z.string())
.describe("Tags for retrieval, for example ['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(", ")}`
}
]
};
}
);
// Find memories by text or tag, optionally scoped to an agent.
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((memory) => {
const matchesQuery =
memory.content.toLowerCase().includes(query.toLowerCase()) ||
memory.tags.some((tag) =>
tag.toLowerCase().includes(query.toLowerCase())
);
const matchesAgent = !agent || memory.agent === agent;
return matchesQuery && matchesAgent;
});
return {
content: [
{
type: "text",
text:
results.length === 0
? "No memories found"
: results
.map(
(memory) =>
`[${memory.timestamp}] ${memory.content}`
)
.join("\n\n")
}
]
};
}
);
// Find memories by one or more exact tags.
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((memory) =>
tags.some((tag) => memory.tags.includes(tag))
)
.slice(0, limit);
return {
content: [
{
type: "text",
text: results
.map(
(memory) =>
`[${memory.agent || "unknown"}] ${memory.content}`
)
.join("\n\n")
}
]
};
}
);
// Remove memories for an agent that were created after a timestamp.
server.tool(
"rollback",
{
agent: z.string().describe("Agent name to roll back"),
timestamp: z.string().describe("Roll back to this timestamp")
},
async ({ agent, timestamp }) => {
await initMemory();
const memories = JSON.parse(
await fs.readFile(MEMORY_FILE, "utf-8")
);
const rolledBack = memories.filter(
(memory) =>
memory.agent !== agent ||
new Date(memory.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);
Start the server:
node memory-server.js
The server stores memories in:
~/.mcp-memory/memories.json
Step 2: Add Memory Instructions to Your Agent Prompt
You do not need to modify individual agent files. Give the agent clear instructions for when to read and write memory.
Use a prompt like this:
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 the last session.
When you complete work:
1. Store 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. Store 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"
)
When something fails:
1. Search for the last known-good state.
2. Use rollback to restore it when appropriate.
Example: Resume a backend session
Start the first session:
You: Activate Backend Architect mode. Design the user system for our e-commerce API.
After the agent designs the system and implements authentication, it stores a summary:
remember(
content="Users table with UUID primary keys, bcrypt password hashing, JWT auth, and refresh tokens",
tags=["ecommerce-api", "auth", "database"],
agent="Backend Architect"
)
On the next day, continue with:
You: Continue from yesterday.
The agent retrieves the saved context:
recall(query="ecommerce-api")
Then it can respond with actionable context:
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.
Step 3: Configure 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"
}
}
}
}
Restart Claude Code so it loads the new MCP server.
Test the connection:
- Use
rememberto store:
Test memory for ecommerce project
- Use these tags:
["test", "ecommerce-api"]
- Use
recallto search for:
test
If the recall result includes the stored text, the integration is working.
Step 4: Configure Cursor
Create .cursor/mcp.json in your project:
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["/absolute/path/to/memory-server.js"]
}
}
}
Test the server from Cursor:
@memory remember "Starting ecommerce API project with PostgreSQL"
Add tags:
["ecommerce-api", "setup"]
Then recall the project context:
@memory recall query="ecommerce"
Memory Patterns for Real Workflows
Pattern 1: Decision Logging
Store technical decisions when you make them, not after the context is lost.
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"
})
Later, retrieve the rationale:
recall(query="PostgreSQL MySQL decision")
This is useful when a teammate asks why a database, framework, schema, or deployment choice was made.
Pattern 2: Agent Handoffs
When switching from one agent role to another, store a handoff note containing completed work, pending work, and known issues.
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"
})
The frontend agent starts by retrieving the handoff:
recall(query="handoff", agent="Backend Architect")
Pattern 3: Session Checkpoints
At the end of each session, write one checkpoint that includes completed work, next work, and blockers.
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"
})
Resume with:
recall(query="checkpoint session-1")
Pattern 4: Bug Tracking
Store bugs with enough detail for another session or agent to reproduce and fix them.
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"
})
Find known bugs later:
search(tags=["bug", "ecommerce-api"])
Troubleshooting
Memory does not persist
Check the memory file:
ls -la ~/.mcp-memory/memories.json
Then verify:
- The MCP server is running before you start Claude Code or Cursor.
- Your MCP config points to the correct absolute path for
memory-server.js. - The memory file is readable and writable.
chmod 644 ~/.mcp-memory/memories.json
recall returns too many results
Make retrieval more specific:
- Add project-specific tags such as
ecommerce-api. - Filter by agent name.
- Use more specific query phrases.
- Use
searchwhen you know the exact tags.
For example:
recall(query="refresh token", agent="Backend Architect")
recall returns no results
Check whether memories were written:
cat ~/.mcp-memory/memories.json
Then verify:
- Your query matches text or tags in stored memories.
- The
agentfilter matches the agent name used inremember. - You are using broader query terms when needed.
- The MCP server is connected to the same memory file.
The memory file is too large
The JSON implementation grows indefinitely. For larger projects:
- Archive old memories periodically.
- Use
rollbackto remove no-longer-needed entries. - Add expiration dates to your memory schema.
- Split memory files by project or date.
- Move to a database backend such as SQLite.
The server fails to start
Check your Node.js version:
node --version
Use Node.js 18 or later.
Install dependencies if needed:
npm install @modelcontextprotocol/sdk zod
Run the server directly to view errors:
node memory-server.js
Multiple agents overwrite or mix memories
Always include an agent value and project tags when storing entries:
remember({
content: "Implemented login endpoint",
tags: ["project-x", "backend", "auth"],
agent: "Backend Architect"
})
When retrieving, scope the result:
recall(query="auth", agent="Backend Architect")
For stronger separation, use separate memory files per project.
What You Built
| Component | Purpose |
|---|---|
| MCP Memory Server | Stores and retrieves information across sessions |
remember tool |
Logs decisions, deliverables, and handoffs |
recall tool |
Finds context from previous sessions |
search tool |
Queries memories by tags |
rollback tool |
Restores a previous state when needed |
| Memory patterns | Supports decision logging, handoffs, checkpoints, and bug tracking |
Memory Server Security Considerations
Avoid storing secrets in plaintext
Do not store API keys, passwords, or other sensitive values in a plain JSON memory file. If your memory server must store sensitive data, add 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")
);
}
Add access control for team memory
For a shared team memory server:
- Require an API key for memory tool calls.
- Use user-specific or project-specific memory namespaces.
- Log memory operations for audit trails.
- Rate limit requests per user.
Next Steps
Extend the memory server:
- Add semantic search with embeddings.
- Add memory expiration and auto-archiving after 30 days.
- Add summarization to condense long sessions.
Build shared team memory:
- Run a central memory server for your team.
- Tag memories by project and developer.
- Create onboarding prompts that retrieve relevant project context.
Integrate your workflow tools:
- Auto-log Git commits as memories.
- Sync project updates from Jira or Linear.
- Export important decisions to documentation.
FAQ
What is MCP memory?
MCP memory is a protocol implementation that lets AI agents store and retrieve information across sessions. It acts like a shared notebook that persists context beyond a single conversation.
How do I set up persistent memory for Claude Code?
Install or run an MCP memory server, add it to ~/.claude/settings.json, and restart Claude Code. The remember, recall, search, and rollback tools should then become available.
Which AI agents support MCP memory?
Any agent running in an MCP-compatible client, including Claude Code, Cursor, and Windsurf, can use MCP memory tools. You do not need to modify agent files; configure the server and add memory instructions to your prompts.
What are the best memory patterns for agent handoffs?
Use remember with tags such as handoff and your project name. Store completed work, pending items, technical decisions, and known issues. The receiving agent can retrieve the note with recall(query="handoff").
How much memory can MCP servers store?
It depends on the implementation. This reference server uses a JSON file that grows indefinitely. Production implementations should add expiration policies, auto-archiving, or a database backend for larger workloads.
Can teams share a central memory server?
Yes. Run the memory server on a shared machine or cloud instance, configure team clients to connect to it, and use consistent project and developer tags for retrieval.
What if memory recall returns too many results?
Use more specific tags when writing memories, filter by agent name when recalling, and query with exact phrases. For larger memory stores, add semantic search with embeddings.
Top comments (0)