要約
AIエージェントに永続メモリを4つのステップで追加します。(1) `remember`、`recall`、`search`、`rollback`ツールを備えたMCPメモリサーバーをセットアップし、(2) エージェントプロンプトにメモリ指示を追加し、(3) Claude Codeの場合は`~/.claude/settings.json`を、Cursorの場合は`.cursor/mcp.json`を設定し、(4) 意思決定ログ、エージェントの引き継ぎ、セッションのチェックポイントにメモリパターンを使用します。エージェントはセッション間でコンテキストを保持するため、以前の会話をコピー&ペーストする必要がなくなります。
「昨日を覚えていない」問題を解決します。MCPプロトコルを使用してAIエージェントに永続メモリを追加すると、以前のセッションからの決定、成果物、およびコンテキストを想起できるようになります。
お決まりのパターンです。
Day 1: "Build the user authentication system"
Agent: [ユーザー認証システムを構築し、usersテーブルを作成し、リフレッシュトークンを実装する]
Day 2: "Continue from yesterday"
Agent: "I don't have context from previous sessions. Can you paste what we did?"
(前回のセッションからのコンテキストがありません。何をしたかペーストしてもらえますか?)
あなたは以前の会話をコピー&ペーストします。エージェントは2000行のコンテキストを読み込みます。両者とも状況を把握するのに15分を無駄にします。
永続メモリはこれを解決します。MCP(Model Context Protocol)メモリを使用すると、エージェントは決定を自動的に保存し、必要に応じてそれらを呼び出します。コピー&ペーストも再説明も不要です。
このチュートリアルでは、AIエージェント用のMCPメモリを設定します。Backend Architectセッションからの決定を保存する方法、Database Optimizerに切り替えるときにコンテキストを呼び出す方法、そしてFrontend Developerに成果物を引き渡す方法を、コンテキストを失うことなく学びます。同じメモリパターンは、Apidog統合でAPIを構築する場合でも、数日間にわたる開発スプリントを管理する場合でも機能します。
MCPメモリとは?
MCPメモリは、AIエージェントがセッション間で情報を保存および取得できるようにします。これを、エージェントが書き込みと読み取りができる共有ノートブックと考えてください。
MCPメモリを動かす4つのツール:
| ツール | 目的 | 例 |
|---|---|---|
remember |
タグ付きで情報を保存 | 「UUID、bcrypt付きユーザーテーブル」を保存 |
recall |
キーワードまたはタグで検索 | 「認証決定」を検索 |
rollback |
以前の状態に復元 | 間違ったスキーマ変更を元に戻す |
search |
セッションを横断して検索 | 「Backend Architectは何を決定したか?」 |
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ AI Agent │ │ MCP Memory │ │ Storage │
│ (Claude Code) │◄───────►│ Server │◄───────►│ (SQLite) │
└─────────────────┘ JSON └──────────────────┘ I/O └─────────────┘
ステップ1: MCPメモリサーバーのセットアップ
メモリツールを公開するMCPサーバーが必要です。いくつかのオープンソース実装が存在します。
オプションA: ホストされたメモリサーバーを使用する
npm install -g @example/mcp-memory-server
オプションB: シンプルなローカルサーバーを実行する
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);
サーバーを実行します。
node memory-server.js
ステップ2: 任意のエージェントにメモリ指示を追加する
エージェントファイルを変更する必要はありません。プロンプトにメモリ指示を追加するだけです。
あなたはMCPメモリツール (remember, recall, search, rollback) にアクセスできます。
以下のメモリプロトコルに従ってください。
**セッションを開始するとき:**
1. コンテキストを呼び出す: recall(query="ecommerce-api", agent="Backend Architect")
2. 前回のセッションからの保留中の項目を確認する
**作業を完了するとき:**
1. 成果物をタグ付きで記憶する:
- 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. 行われた決定と保留中の項目を含める
**他のエージェントに引き継ぐとき:**
1. 受信側のエージェントのためにコンテキストを残す:
- 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")
**何かが失敗したとき:**
1. 最後の既知の良好な状態を検索する
2. ロールバックを使用して復元する
実行例:
You: "Activate Backend Architect mode. Design the user system for our e-commerce API."
(「Backend Architectモードをアクティブにしてください。eコマース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."
(「昨日、私はUUID主キーを持つusersテーブルを設計し、bcryptパスワードハッシュを実装し、リフレッシュトークン付きのJWT認証を設定しました。次のステップは商品カタログのスキーマです。続行する準備ができています。」)
ステップ3: Claude Code向けの設定
MCP構成にメモリサーバーを追加します。
~/.claude/settings.jsonを編集します。
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["/absolute/path/to/memory-server.js"],
"env": {
"HOME": "/Users/your-username"
}
}
}
}
Claude Codeを再起動します。メモリツールが利用可能になります。
テスト:
Use the remember tool to store: "Test memory for ecommerce project"
(rememberツールを使って「eコマースプロジェクトのテストメモリ」を保存してください)
Tags: ["test", "ecommerce-api"]
(タグ: ["test", "ecommerce-api"])
Use the recall tool to find memories about "test"
(recallツールを使って「test」に関するメモリを見つけてください)
ステップ4: Cursor向けの設定
プロジェクトに.cursor/mcp.jsonを作成します。
{
"mcpServers": {
"memory": {
"command": "node",
"args": ["/absolute/path/to/memory-server.js"]
}
}
}
テスト:
@memory remember "Starting ecommerce API project with PostgreSQL"
Tags: ["ecommerce-api", "setup"]
@memory recall query="ecommerce"
実際のワークフローのためのメモリパターン
パターン1: 決定のログ記録
技術的な決定をするたびに、ログに記録します。
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"
})
後で誰かが「なぜPostgreSQLなのか?」と尋ねたとき:
recall(query="PostgreSQL MySQL decision")
パターン2: エージェントの引き継ぎ
エージェントを切り替える際に、引き継ぎノートを残します。
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"
})
Frontend Developerは以下から開始します。
recall(query="handoff", agent="Backend Architect")
パターン3: セッションのチェックポイント
各作業セッションの終わりに:
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"
})
次のセッションを再開します。
recall(query="checkpoint session-1")
パターン4: バグの追跡
バグを発見したとき:
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"
})
後でバグを検索します。
search(tags=["bug", "ecommerce-api"])
トラブルシューティング
メモリが永続化されない場合:
- メモリファイルのパス(
~/.mcp-memory/memories.json)を確認してください。 - MCPサーバーが実行中であることを確認してください。
- Claude Code/CursorにMCP設定があることを確認してください。
recallで結果が多すぎる場合:
- より具体的なタグを追加してください。
- エージェント名でフィルタリングしてください。
- 正確なフレーズを引用符で囲んで使用してください。
メモリファイルが大きくなりすぎる場合:
- 古いメモリを定期的にアーカイブしてください。
- 完了したプロジェクトをクリーンアップするために
rollbackを使用してください。 - メモリのスキーマに有効期限を追加してください。
構築したもの
| コンポーネント | 目的 |
|---|---|
| MCPメモリサーバー | セッション間で情報を保存/取得 |
rememberツール |
決定、成果物、引き継ぎをログに記録 |
recallツール |
以前のセッションからコンテキストを検索 |
searchツール |
すべてのメモリをタグでクエリ |
rollbackツール |
必要に応じて以前の状態に復元 |
| メモリパターン | 決定ログ、引き継ぎ、チェックポイント、バグ追跡 |
次のステップ
メモリサーバーを拡張する:
- 埋め込みを使用したセマンティック検索を追加する
- メモリの有効期限(30日後に自動アーカイブ)を実装する
- メモリの要約(長いセッションを凝縮)を追加する
チームメモリを構築する:
- チーム全体で中央メモリサーバーを共有する
- プロジェクトと開発者ごとにメモリをタグ付けする
- 新しいチームメンバーのオンボーディングフローを作成する
ツールとの統合:
- Gitコミットをメモリとして自動ログ記録する
- プロジェクト管理ツール(Jira、Linear)と同期する
- メモリをドキュメントにエクスポートする
よくある問題のトラブルシューティング
セッション間でメモリが永続化されない場合:
- Claude Codeを起動する前にMCPサーバーが実行されていることを確認してください。
- メモリファイルパスが存在するか確認してください:
ls -la ~/.mcp-memory/memories.json - ファイル権限が読み書きを許可していることを確認してください:
chmod 644 ~/.mcp-memory/memories.json ~/.claude/settings.jsonのサーバー設定が正しいパスを指していることを確認してください。
recallが空の結果を返す場合:
- クエリが保存されたタグと一致しているか(大文字と小文字を区別する)確認してください。
- より広い検索語を試すか、特定のタグで
searchを使用してください。 - メモリが実際に保存されているか確認してください:
cat ~/.mcp-memory/memories.json - エージェント名フィルターが一致しているか確認してください(
agentパラメータを使用している場合)。
メモリファイルが大きくなりすぎる場合:
- 30日以上前のメモリを自動的にアーカイブする機能を実装してください。
- 日付範囲でメモリを削除する
pruneツールを追加してください。 - プロジェクトまたは日付ごとにメモリを別々のファイルに分割してください。
- 大規模な使用には、JSONではなくデータベースバックエンド(SQLite)を使用してください。
サーバーが起動しない場合:
- Node.jsのバージョンを確認してください:
node --version(18以上である必要があります)。 - 不足している依存関係をインストールしてください:
npm install @modelcontextprotocol/sdk zod - サーバーコードに構文エラーがないか確認してください。
- エラーを確認するために直接実行してください:
node memory-server.js
複数のエージェントが互いのメモリを上書きする場合:
rememberを呼び出す際には、常にagentフィールドを含めてください。- プロジェクトごとに一意のタグを使用してください:
["project-x", "backend", "auth"] - コンテキストを取得する際には、エージェント名で呼び出しをフィルタリングしてください。
- プロジェクトごとに別々のメモリファイルを検討してください。
メモリサーバーのセキュリティに関する考慮事項
APIキーの保存:メモリサーバーが機密データ(APIキー、パスワード)を保存する場合、暗号化を実装してください。
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');
}
アクセス制御:チームメモリサーバーの場合、認証を追加してください。
- メモリツール呼び出しにAPIキーを要求する
- ユーザー固有のメモリ名前空間を実装する
- 監査ログのためにすべてのメモリ操作をログに記録する
- ユーザーごとのリクエストをレート制限する
AIエージェントに永続メモリが備わりました。 彼らは昨日を覚えています。決定を想起します。コピー&ペーストなしでコンテキストを引き渡します。
「前回のセッションからのコンテキストがありません」はもう言いません。再説明も不要です。時間の無駄もなくなります。
それがMCPメモリの力です。エージェントに共有ノートブックを与えれば、彼らが数日間にわたるプロジェクトで実際に役立つようになるのを見ることができます。
よくある質問
MCPメモリとは何ですか?MCPメモリは、AIエージェントがセッション間で情報を保存および取得できるようにするプロトコル実装です。エージェントが書き込みと読み取りができる共有ノートブックと考えてください。これにより、単一の会話を超えてコンテキストが永続化されます。
Claude Codeに永続メモリを設定するにはどうすればよいですか?MCPメモリサーバーをインストールし、サーバーコマンドとパスを使用して~/.claude/settings.jsonに追加します。Claude Codeを再起動すると、メモリツール(remember、recall、search、rollback)が利用可能になります。
どのAIエージェントがMCPメモリをサポートしていますか?MCP互換クライアント(Claude Code、Cursor、Windsurf)で実行されているエージェントは、メモリツールを使用できます。エージェントファイルを変更する必要はなく、プロンプトにメモリ指示を追加するだけです。
エージェントの引き継ぎに最適なメモリパターンは何ですか?rememberを["handoff", "project-name"]のようなタグとともに使用して、次のエージェントにコンテキストを残します。完了した作業、保留中の項目、既知の問題を含めます。受信側エージェントはrecall(query="handoff")を呼び出してそれらを取得します。
MCPサーバーはどのくらいのメモリを保存できますか?実装によって異なります。参照サーバーは、際限なく増加するJSONファイルを使用します。本番サーバーでは、大規模な使用のために、有効期限ポリシー、自動アーカイブ、またはデータベースバックエンドを追加する必要があります。
チームで中央メモリサーバーを共有できますか?はい。共有マシンまたはクラウドインスタンスでメモリサーバーを実行し、すべてのチームメンバーのクライアントがそれに接続するように設定し、プロジェクトと開発者ごとにメモリをタグ付けして、整理された取得を可能にします。
メモリの呼び出しで結果が多すぎる場合はどうなりますか?メモリを保存する際に、より具体的なタグを追加してください。呼び出しクエリでエージェント名でフィルタリングしてください。正確なフレーズを引用符で囲んで使用してください。よりスマートな取得のために、埋め込みを使用したセマンティック検索の実装を検討してください。
