Skip to main content
Glama

recall

Retrieve relevant memories via semantic search with keyword fallback, searching working and long-term memory tiers to support AI agent decision-making.

Instructions

Retrieve relevant memories via semantic search (falls back to keyword). Searches working + long_term tiers. Pass userId to blend in cross-session user facts.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sessionIdYesSession identifier
queryYesSearch query
limitNoMax results (default 10)
userIdNoOptional: also blend in this user's cross-session memories
tiersNoComma-separated tiers to search: working,long_term,archived (default: working,long_term)

Implementation Reference

  • src/index.ts:52-66 (registration)
    Tool registration for 'recall' - defines the tool name, description, and inputSchema with sessionId, query, limit, userId, and tiers parameters
    {
      name: 'recall',
      description: 'Retrieve relevant memories via semantic search (falls back to keyword). Searches working + long_term tiers. Pass userId to blend in cross-session user facts.',
      inputSchema: {
        type: 'object',
        properties: {
          sessionId:  { type: 'string', description: 'Session identifier' },
          query:      { type: 'string', description: 'Search query' },
          limit:      { type: 'number', description: 'Max results (default 10)', default: 10 },
          userId:     { type: 'string', description: 'Optional: also blend in this user\'s cross-session memories' },
          tiers:      { type: 'string', description: 'Comma-separated tiers to search: working,long_term,archived (default: working,long_term)' },
        },
        required: ['sessionId', 'query'],
      },
    },
  • Handler for 'recall' tool - parses tier arguments, calls memory.recall() with sessionId, query, limit, and options (tiers, userId), returns JSON results
    case 'recall': {
      const tiers = args.tiers
        ? (args.tiers as string).split(',').map(t => t.trim()) as any
        : undefined;
      const entries = await memory.recall(
        args.sessionId as string,
        args.query as string,
        (args.limit as number) || 10,
        {
          tiers,
          userId: args.userId as string | undefined,
        }
      );
      return { content: [{ type: 'text', text: JSON.stringify(entries, null, 2) }] };
    }
  • Core recall implementation in Engram class - performs semantic search using Ollama embeddings with cosine similarity ranking, falls back to keyword search if embeddings unavailable. Queries SQLite database for memories matching session.
    async recall(
      sessionId: string,
      query?: string,
      limit: number = 10,
      options: RecallOptions = {}
    ): Promise<MemoryEntry[]> {
      await this.init();
    
      // Build base query
      let sql = `
        SELECT id, session_id, content, role, timestamp, metadata, embedding
        FROM memories
        WHERE session_id = ?
      `;
      const params: (string | number)[] = [sessionId];
    
      if (options.role) {
        sql += ` AND role = ?`;
        params.push(options.role);
      }
      if (options.after) {
        sql += ` AND timestamp >= ?`;
        params.push(options.after.getTime());
      }
      if (options.before) {
        sql += ` AND timestamp <= ?`;
        params.push(options.before.getTime());
      }
    
      // Try semantic search
      if (query && query.trim() && this.semanticSearch) {
        const queryVector = await this.embed(query);
        if (queryVector) {
          sql += ` ORDER BY timestamp DESC`;
          const rows = await this.db.all(sql, params);
    
          // Score each row by cosine similarity
          const scored = rows
            .map((row: any) => {
              let similarity = 0;
              if (row.embedding) {
                try {
                  const vec: number[] = JSON.parse(row.embedding);
                  similarity = this.cosineSimilarity(queryVector, vec);
                } catch { /* malformed, skip */ }
              }
              return { row, similarity };
            })
            .sort((a, b) => b.similarity - a.similarity)
            .slice(0, limit);
    
          return scored.map(({ row, similarity }) => ({
            id: row.id,
            sessionId: row.session_id,
            content: row.content,
            role: row.role,
            timestamp: new Date(row.timestamp),
            metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
            similarity
          }));
        }
        // Ollama unreachable — fall through to keyword search
      }
    
      // Keyword fallback
      if (query && query.trim()) {
        const keywords = query.toLowerCase().split(/\s+/).filter(k => k.length > 2);
        if (keywords.length > 0) {
          sql += ` AND (` + keywords.map(() => `LOWER(content) LIKE ?`).join(' OR ') + `)`;
          params.push(...keywords.map(k => `%${k}%`));
        }
      }
    
      sql += ` ORDER BY timestamp DESC LIMIT ?`;
      params.push(limit);
    
      const rows = await this.db.all(sql, params);
      return rows.map((row: any) => ({
        id: row.id,
        sessionId: row.session_id,
        content: row.content,
        role: row.role,
        timestamp: new Date(row.timestamp),
        metadata: row.metadata ? JSON.parse(row.metadata) : undefined
      }));
    }
  • RecallOptions interface defining optional parameters: limit, before/after date filters, and role filter for memory retrieval
    export interface RecallOptions {
      limit?: number;
      before?: Date;
      after?: Date;
      role?: 'user' | 'assistant' | 'system';
    }

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/Cartisien/engram-mcp'

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