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,
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses key behavioral traits such as supporting text search with optional date filtering and performance implications of the source parameter. However, it lacks details on permissions, rate limits, error handling, or result format, leaving gaps for a search tool with no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences that are front-loaded with the core purpose and efficiently convey key usage guidance. Every sentence earns its place by adding value without redundancy, making it highly concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (6 parameters, no annotations, no output schema), the description is adequate but incomplete. It covers the search scope and performance tips but lacks details on authentication, result structure, pagination, or error cases, which are important for a search tool without annotations or output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds marginal value by mentioning the source parameter's impact on performance and the optional date filtering, but doesn't provide additional semantics beyond what's in the schema, meeting the baseline for high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('search an agent's memory') and resources ('across both archival passages and conversation messages'), distinguishing it from siblings like 'search_archival_memory' which only searches one source. It explicitly mentions the dual memory sources, making its scope unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for usage with the recommendation to 'use source parameter to search only archival or messages for better performance,' which guides when to use specific parameter values. However, it doesn't explicitly state when to use this tool versus alternatives like 'search_archival_memory' or 'list_messages,' missing explicit sibling differentiation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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