recall
Search persistent memory to retrieve relevant past information, user preferences, or previous session context using semantic queries.
Instructions
Search persistent memory for relevant past information. Use when you need to check if you've encountered something before, recall past context, retrieve user preferences, or access what was learned in previous sessions.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | What to search for. Use natural language — the search is semantic, not keyword-based. | |
| agent_id | No | Identifier for this agent instance | default |
| user_id | No | User identifier to include user-scoped memories | |
| scope | No | Search scope: agent (only this agent's memories), user (include user-scoped), org (include org-scoped) | |
| tags | No | Filter by tags | |
| limit | No | Maximum number of memories to return |
Implementation Reference
- packages/mcp-server/src/index.ts:139-155 (handler)MCP tool handler for "recall" that calls the internal API endpoint.
async ({ query, agent_id, user_id, scope, tags, limit }) => { const result = await apiCall("/memories/recall", "POST", { agent_id, user_id, query, scope, tags, limit, }); const memories = ( result as { memories: Array<{ id: string; content: string; relevance_score: number; tags: string[]; - packages/mcp-server/src/index.ts:104-138 (registration)Definition and registration of the "recall" tool using the MCP server SDK.
server.tool( "recall", "Search persistent memory for relevant past information. Use when you need to check if you've encountered something before, recall past context, retrieve user preferences, or access what was learned in previous sessions.", { query: z .string() .describe( "What to search for. Use natural language — the search is semantic, not keyword-based.", ), agent_id: z .string() .default("default") .describe("Identifier for this agent instance"), user_id: z .string() .optional() .describe("User identifier to include user-scoped memories"), scope: z .enum(["agent", "user", "org"]) .optional() .describe( "Search scope: agent (only this agent's memories), user (include user-scoped), org (include org-scoped)", ), tags: z .array(z.string()) .optional() .describe("Filter by tags"), limit: z .number() .int() .min(1) .max(20) .default(5) .describe("Maximum number of memories to return"), }, - Validation schema for the /recall API route.
const recallSchema = z.object({ agent_id: z.string().min(1).max(200), user_id: z.string().max(200).optional(), query: z.string().min(1).max(5000), scope: z.enum(["agent", "user", "org"]).optional(), tags: z.array(z.string().max(100)).max(20).optional(), limit: z.number().int().min(1).max(50).default(10), }); - Service function that executes the core recall logic, including vector embedding search.
export async function recall(params: RecallParams): Promise<MemoryWithScore[]> { const { apiKeyId, agentId, userId, orgId, query, scope, tags, limit = 10, } = params; const queryVector = await embed(query); // Fetch candidate memories with parameterized queries (no sql.unsafe!) const candidates = await sql` SELECT id, agent_id, user_id, org_id, scope, content, tags, embedding, created_at, updated_at FROM memories WHERE api_key_id = ${apiKeyId} AND deleted_at IS NULL AND embedding IS NOT NULL AND agent_id = ${agentId} ORDER BY created_at DESC LIMIT 500 `; // Also fetch scope-expanded memories if needed let scopeMemories: any[] = []; if (scope === "org" && orgId) { scopeMemories = await sql` SELECT id, agent_id, user_id, org_id, scope, content, tags, embedding, created_at, updated_at FROM memories WHERE api_key_id = ${apiKeyId} AND deleted_at IS NULL AND embedding IS NOT NULL AND scope = 'org' AND org_id = ${orgId} AND agent_id != ${agentId} ORDER BY created_at DESC LIMIT 200 `; } else if (scope === "user" && userId) { scopeMemories = await sql` SELECT id, agent_id, user_id, org_id, scope, content, tags, embedding, created_at, updated_at FROM memories WHERE api_key_id = ${apiKeyId} AND deleted_at IS NULL AND embedding IS NOT NULL AND scope = 'user' AND user_id = ${userId} AND agent_id != ${agentId} ORDER BY created_at DESC LIMIT 200 `; } const allCandidates = [...(candidates as any[]), ...(scopeMemories as any[])]; // Filter by tags if specified let filtered = allCandidates; if (tags && tags.length > 0) { filtered = allCandidates.filter((m: any) => { const memTags = m.tags || []; return tags.some((t) => memTags.includes(t)); }); }