agent_memory
Store and recall persistent memory across agents, sessions, and tools. Enable context retention and data sharing between different AI agents for consistent interactions.
Instructions
Cross-agent persistent memory — store and recall across sessions and tools
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- The main execute function for the agent_memory tool. Handles four actions: store, recall, recall_by_tags, and get_recent. Includes size limits (10KB per entry, 1000 per agent) and SQLite-backed persistence.
export function executeAgentMemory( db: FourDADatabase, params: AgentMemoryParams, ): object { const rawDb = db.getRawDb(); switch (params.action) { // ======================================================================== // STORE // ======================================================================== case "store": { if (!params.subject || !params.content) { return { error: "subject and content are required for store action" }; } // --- Size limit: max 10KB per entry --- const contentBytes = new TextEncoder().encode(params.content).length; if (contentBytes > MAX_CONTENT_BYTES) { return { error: `Content too large: ${contentBytes} bytes exceeds ${MAX_CONTENT_BYTES} byte limit (10KB)`, }; } // --- Storage quota: max 1000 entries per agent --- const agentType = params.agent_type || "unknown"; try { const countRow = rawDb .prepare( `SELECT COUNT(*) as cnt FROM agent_memory WHERE agent_type = ?`, ) .get(agentType) as { cnt: number } | undefined; const currentCount = countRow?.cnt ?? 0; if (currentCount >= MAX_ENTRIES_PER_AGENT) { return { error: `Storage quota exceeded: agent "${agentType}" has ${currentCount} entries (max ${MAX_ENTRIES_PER_AGENT}). Delete old entries before storing new ones.`, }; } } catch (quotaErr) { return { error: `Failed to check storage quota: ${quotaErr instanceof Error ? quotaErr.message : String(quotaErr)}`, }; } try { const stmt = rawDb.prepare( `INSERT INTO agent_memory (session_id, agent_type, memory_type, subject, content, context_tags, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)`, ); const result = stmt.run( params.session_id || "unknown", params.agent_type || "unknown", params.memory_type || "context", params.subject, params.content, JSON.stringify(params.context_tags || []), params.expires_at || null, ); return { success: true, id: result.lastInsertRowid, message: `Memory stored: ${params.subject}`, }; } catch (error) { return { error: `Failed to store memory: ${error instanceof Error ? error.message : String(error)}`, }; } } // ======================================================================== // RECALL // ======================================================================== case "recall": { if (!params.query) { return { error: "query is required for recall action" }; } try { const pattern = `%${params.query.toLowerCase()}%`; let sql = `SELECT id, session_id, agent_type, memory_type, subject, content, context_tags, created_at, expires_at, promoted_to_decision_id FROM agent_memory WHERE (LOWER(subject) LIKE ? OR LOWER(context_tags) LIKE ?) AND (expires_at IS NULL OR expires_at > datetime('now'))`; const sqlParams: (string | number)[] = [pattern, pattern]; if (params.filter_agent) { sql += ` AND agent_type = ?`; sqlParams.push(params.filter_agent); } sql += ` ORDER BY created_at DESC LIMIT ?`; sqlParams.push(params.limit || 20); const rows = rawDb.prepare(sql).all(...sqlParams) as AgentMemoryRow[]; return { memories: rows.map(parseMemoryRow), count: rows.length, }; } catch (error) { return { memories: [], count: 0, error: `Failed to recall memories: ${error instanceof Error ? error.message : String(error)}`, }; } } // ======================================================================== // RECALL BY TAGS // ======================================================================== case "recall_by_tags": { if (!params.tags || params.tags.length === 0) { return { error: "tags array is required for recall_by_tags action" }; } try { // Build OR conditions for each tag const conditions = params.tags.map( () => `LOWER(context_tags) LIKE ?`, ); const tagParams = params.tags.map((t) => `%${t.toLowerCase()}%`); const sql = `SELECT id, session_id, agent_type, memory_type, subject, content, context_tags, created_at, expires_at, promoted_to_decision_id FROM agent_memory WHERE (${conditions.join(" OR ")}) AND (expires_at IS NULL OR expires_at > datetime('now')) ORDER BY created_at DESC LIMIT ?`; const rows = rawDb .prepare(sql) .all(...tagParams, params.limit || 20) as AgentMemoryRow[]; return { memories: rows.map(parseMemoryRow), count: rows.length, }; } catch (error) { return { memories: [], count: 0, error: `Failed to recall by tags: ${error instanceof Error ? error.message : String(error)}`, }; } } // ======================================================================== // GET RECENT // ======================================================================== case "get_recent": { const since = params.since || new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); try { let sql = `SELECT id, session_id, agent_type, memory_type, subject, content, context_tags, created_at, expires_at, promoted_to_decision_id FROM agent_memory WHERE created_at > ?`; const sqlParams: (string | number)[] = [since]; if (params.filter_agent) { sql += ` AND agent_type = ?`; sqlParams.push(params.filter_agent); } sql += ` ORDER BY created_at DESC LIMIT ?`; sqlParams.push(params.limit || 50); const rows = rawDb.prepare(sql).all(...sqlParams) as AgentMemoryRow[]; return { memories: rows.map(parseMemoryRow), count: rows.length, since, }; } catch (error) { return { memories: [], count: 0, since, error: `Failed to get recent memories: ${error instanceof Error ? error.message : String(error)}`, }; } } default: return { error: `Unknown action: ${params.action}` }; } } - The tool definition with name 'agent_memory' and full inputSchema describing all parameters (action enum, session_id, agent_type, memory_type, subject, content, context_tags, expires_at, query, filter_agent, limit, tags, since).
export const agentMemoryTool = { name: "agent_memory", description: "Cross-agent persistent memory. Actions: store (save a memory), recall (search by subject), recall_by_tags (search by tags), get_recent (memories since timestamp). What one agent learns, all agents can access.", inputSchema: { type: "object" as const, properties: { action: { type: "string", enum: ["store", "recall", "recall_by_tags", "get_recent"], description: "Action to perform", }, session_id: { type: "string", description: "Session identifier (for store)", }, agent_type: { type: "string", description: "Agent identifier, e.g. claude_code, cursor, windsurf (for store)", }, memory_type: { type: "string", enum: ["discovery", "decision", "context", "warning", "preference"], description: "Type of memory (for store). Default: context", }, subject: { type: "string", description: "Short subject line for the memory (for store)", }, content: { type: "string", description: "Full memory content (for store)", }, context_tags: { type: "array", items: { type: "string" }, description: "Tags for categorization (for store)", }, expires_at: { type: "string", description: "ISO datetime when this memory expires (for store, optional)", }, query: { type: "string", description: "Search term to match against subject and tags (for recall)", }, filter_agent: { type: "string", description: "Filter results to a specific agent type (for recall, get_recent)", }, limit: { type: "number", description: "Max results to return (default 20)", }, tags: { type: "array", items: { type: "string" }, description: "Tags to search for (for recall_by_tags)", }, since: { type: "string", description: "ISO datetime to get memories after (for get_recent)", }, }, required: ["action"], }, - mcp-4da-server/src/tool-dispatch.ts:59-64 (registration)Registration of 'agent_memory' in the dispatch map, mapping the tool name to the executeAgentMemory handler.
// Agent agent_memory: executeAgentMemory, // Identity developer_dna: executeDeveloperDna, }; - mcp-4da-server/src/schema-registry.ts:129-136 (registration)Schema registry entry for agent_memory, categorized under 'agent' with tags and reference to agent-memory.json schema file.
// --- Agent (standalone) --- agent_memory: { summary: "Cross-agent persistent memory — store and recall across sessions and tools", schemaFile: "agent-memory.json", category: "agent", tags: ["agent", "memory", "persistent", "cross-session"], standalone: true, }, - Helper function parseMemoryRow that converts a raw AgentMemoryRow (with JSON-stringified context_tags) into a structured object.
function parseMemoryRow(row: AgentMemoryRow) { return { id: row.id, session_id: row.session_id, agent_type: row.agent_type, memory_type: row.memory_type, subject: row.subject, content: row.content, context_tags: JSON.parse(row.context_tags || "[]") as string[], created_at: row.created_at, expires_at: row.expires_at, promoted_to_decision_id: row.promoted_to_decision_id, }; }