Skip to main content
Glama

search_memory

Search an agent's memory across archival passages and conversation messages using text queries with optional date filtering to retrieve relevant information.

Instructions

Search an agent's memory across both archival passages and conversation messages. Supports text search with optional date filtering. Use source parameter to search only archival or messages for better performance.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idYesID of the agent whose memory to search
queryYesText to search for in memory content
sourceNoWhich memory source to search: "archival" (passages), "messages" (conversation history), or "both" (default)both
start_dateNoFilter results after this datetime (ISO 8601 format)
end_dateNoFilter results before this datetime (ISO 8601 format)
limitNoMaximum number of results per source (default: 50)

Implementation Reference

  • Main handler function executing the search_memory tool: validates args, searches archival memory via API, searches messages via client-side helper, returns JSON results.
    /** * Unified tool handler for searching both archival memory and messages */ export async function handleSearchMemory(server, args) { if (!args?.agent_id) { throw new Error('Missing required argument: agent_id'); } if (!args?.query) { throw new Error('Missing required argument: query'); } const results = { archival: null, messages: null, }; const source = args.source || 'both'; const headers = server.getApiHeaders(); const agentId = encodeURIComponent(args.agent_id); try { // Search archival memory if (source === 'archival' || source === 'both') { const archivalParams = { query: args.query, }; if (args.limit) archivalParams.top_k = args.limit; if (args.start_date) archivalParams.start_datetime = args.start_date; if (args.end_date) archivalParams.end_datetime = args.end_date; const archivalResponse = await server.api.get( `/agents/${agentId}/archival-memory/search`, { headers, params: archivalParams, }, ); let passages = archivalResponse.data.results || []; // Remove embeddings by default // eslint-disable-next-line no-unused-vars passages = passages.map(({ embedding, ...rest }) => rest); results.archival = { passages, count: passages.length, }; } // Search messages if (source === 'messages' || source === 'both') { results.messages = await searchMessages( server, agentId, headers, args.query, args.start_date, args.end_date, args.limit || 50, ); } return { content: [ { type: 'text', text: JSON.stringify(results), }, ], }; } catch (error) { return server.createErrorResponse(error); } }
  • Tool definition including name, description, and input schema for validation.
    /** * Tool definition for search_memory */ export const searchMemoryDefinition = { name: 'search_memory', description: "Search an agent's memory across both archival passages and conversation messages. Supports text search with optional date filtering. Use source parameter to search only archival or messages for better performance.", inputSchema: { type: 'object', properties: { agent_id: { type: 'string', description: 'ID of the agent whose memory to search', }, query: { type: 'string', description: 'Text to search for in memory content', }, source: { type: 'string', enum: ['archival', 'messages', 'both'], description: 'Which memory source to search: "archival" (passages), "messages" (conversation history), or "both" (default)', default: 'both', }, start_date: { type: 'string', format: 'date-time', description: 'Filter results after this datetime (ISO 8601 format)', }, end_date: { type: 'string', format: 'date-time', description: 'Filter results before this datetime (ISO 8601 format)', }, limit: { type: 'integer', description: 'Maximum number of results per source (default: 50)', default: 50, }, }, required: ['agent_id', 'query'], }, };
  • Registration of the tool handler in the central switch statement for tool dispatch.
    case 'search_memory': return handleSearchMemory(server, request.params.arguments);
  • Key helper function implementing client-side search of agent messages with date filtering and optimized pagination direction.
    async function searchMessages(server, agentId, headers, query, startDate, endDate, limit) { // First, get boundary timestamps to determine optimal pagination direction const [firstMsg, lastMsg] = await Promise.all([ fetchFirstMessage(server, agentId, headers), fetchLastMessage(server, agentId, headers), ]); if (!firstMsg || !lastMsg) { return { messages: [], count: 0 }; } const firstTime = new Date(firstMsg.date).getTime(); const lastTime = new Date(lastMsg.date).getTime(); // Determine date range const rangeStart = startDate ? new Date(startDate).getTime() : firstTime; const rangeEnd = endDate ? new Date(endDate).getTime() : lastTime; const rangeCenter = (rangeStart + rangeEnd) / 2; // Choose pagination direction based on which end is closer to range center const distToFirst = Math.abs(rangeCenter - firstTime); const distToLast = Math.abs(rangeCenter - lastTime); const paginateAsc = distToFirst < distToLast; const queryLower = query.toLowerCase(); const matchingMessages = []; let cursor = null; let hasMore = true; const pageSize = 100; while (hasMore && matchingMessages.length < limit) { const params = { limit: pageSize, order: paginateAsc ? 'asc' : 'desc', }; if (cursor) { params[paginateAsc ? 'after' : 'before'] = cursor; } const response = await server.api.get(`/agents/${agentId}/messages`, { headers, params, }); const messages = response.data || []; if (messages.length === 0) { hasMore = false; break; } for (const msg of messages) { const msgTime = new Date(msg.date).getTime(); // Check if we've passed the date range boundary if (paginateAsc && msgTime > rangeEnd) { hasMore = false; break; } if (!paginateAsc && msgTime < rangeStart) { hasMore = false; break; } // Skip if outside date range if (msgTime < rangeStart || msgTime > rangeEnd) { continue; } // Check if content matches query const content = msg.content || ''; if (content.toLowerCase().includes(queryLower)) { matchingMessages.push({ id: msg.id, date: msg.date, message_type: msg.message_type, content: content.length > 500 ? content.substring(0, 500) + '...' : content, }); if (matchingMessages.length >= limit) { break; } } } // Update cursor for next page cursor = messages[messages.length - 1].id; // If we got fewer messages than requested, we've reached the end if (messages.length < pageSize) { hasMore = false; } } // Sort results by date (oldest first) for consistent output matchingMessages.sort((a, b) => new Date(a.date) - new Date(b.date)); return { messages: matchingMessages, count: matchingMessages.length, }; }
  • Inclusion of searchMemoryDefinition in the allTools array used for tool listing and registration.
    searchMemoryDefinition,

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oculairmedia/Letta-MCP-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server