Skip to main content
Glama

search_conversations

Search past conversations and project discussions using full-text queries. Find mentions of topics, decisions, or code changes with support for AND, OR, NOT, and exact phrases.

Instructions

Search conversations using full-text search. Defaults to the current project. Set allProjects=true to search across all projects, or pass a specific projectId. Use this when the user asks "did we discuss X before?" or references past work, decisions, or code changes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query (supports FTS5: AND, OR, NOT, "exact phrases")
projectIdNoSearch a specific project (defaults to current)
allProjectsNoSearch across ALL projects instead of just the current one
limitNoMax results (default 10)

Implementation Reference

  • The main tool handler for 'search_conversations'. Registers the tool with server.tool(), defines the Zod schema (query, projectId, allProjects, limit), calls searchDb.search() with the resolved project scope, formats results as structured JSON with session info and snippets, and returns them as text content. On error, returns an error response.
    // ─── search_conversations ────────────────────────────────────────
    server.tool(
      'search_conversations',
      'Search conversations using full-text search. Defaults to the current project. Set allProjects=true to search across all projects, or pass a specific projectId. Use this when the user asks "did we discuss X before?" or references past work, decisions, or code changes.',
      {
        query: z.string().describe('Search query (supports FTS5: AND, OR, NOT, "exact phrases")'),
        projectId: z.string().optional().describe('Search a specific project (defaults to current)'),
        allProjects: z.boolean().default(false).optional().describe('Search across ALL projects instead of just the current one'),
        limit: z.number().min(1).max(50).default(10).optional().describe('Max results (default 10)'),
      },
      async ({ query, projectId, allProjects = false, limit = 10 }) => {
        try {
          const { searchDb, currentProjectId } = await getServices()
    
          // Determine search scope
          const searchProjectId = allProjects ? undefined : (projectId || currentProjectId)
          const scopeLabel = allProjects
            ? 'all projects'
            : `project "${searchProjectId}"`
    
          const results = await searchDb.search({
            query,
            projectId: searchProjectId,
            limit,
            offset: 0,
          })
    
          if (!results.hits || results.hits.length === 0) {
            return {
              content: [{
                type: 'text',
                text: `No results found for "${query}" in ${scopeLabel}. ${!allProjects ? 'Try allProjects=true to search everywhere.' : ''}`,
              }],
            }
          }
    
          const formatted = results.hits.map(hit => ({
            project: hit.projectName,
            session: hit.sessionTitle || hit.sessionId,
            sessionId: hit.sessionId,
            projectId: hit.projectId,
            role: hit.role,
            snippet: hit.snippet,
            timestamp: hit.timestamp,
            score: hit.score,
          }))
    
          return {
            content: [{
              type: 'text',
              text: `Found ${results.total} results for "${query}" in ${scopeLabel} (showing ${formatted.length}):\n\n${JSON.stringify(formatted, null, 2)}`,
            }],
          }
        } catch (error) {
          return {
            content: [{ type: 'text', text: `Search error: ${error.message}` }],
            isError: true,
          }
        }
      }
    )
  • Zod schema for search_conversations input: 'query' (required string with FTS5 support), 'projectId' (optional string), 'allProjects' (optional boolean, default false), 'limit' (optional number 1-50, default 10).
    // ─── search_conversations ────────────────────────────────────────
    server.tool(
      'search_conversations',
      'Search conversations using full-text search. Defaults to the current project. Set allProjects=true to search across all projects, or pass a specific projectId. Use this when the user asks "did we discuss X before?" or references past work, decisions, or code changes.',
      {
        query: z.string().describe('Search query (supports FTS5: AND, OR, NOT, "exact phrases")'),
        projectId: z.string().optional().describe('Search a specific project (defaults to current)'),
        allProjects: z.boolean().default(false).optional().describe('Search across ALL projects instead of just the current one'),
        limit: z.number().min(1).max(50).default(10).optional().describe('Max results (default 10)'),
      },
  • The tool is registered via registerTools(server, getServices) call in the main MCP server entry point (index.js). This wires the tools.js module into the McpServer instance.
    registerTools(server, getServices)
  • The SearchDatabase.search() method that performs the actual FTS5 full-text search on the SQLite messages_fts table. Supports filtering by projectId, role, date range, and returns BM25-relevance-scored results with snippets.
    async search({ query, projectId, role, from, to, limit = 50, offset = 0 }) {
      // Escape FTS5 query to handle terms like "ETL" that could be interpreted as column names
      const escapedQuery = this.escapeFTS5Query(query);
    
      let searchSQL = `
        SELECT
          project_id,
          project_name,
          session_id,
          session_title,
          message_id,
          role,
          snippet(messages_fts, 6, '<mark>', '</mark>', '...', 64) as snippet,
          timestamp,
          file_path,
          line_number,
          template,
          bm25(messages_fts) as relevance_score
        FROM messages_fts
        WHERE messages_fts MATCH ?
      `
    
      const params = [escapedQuery]
      
      // Add filters
      if (projectId) {
        searchSQL += ` AND project_id = ?`
        params.push(projectId)
      }
      
      if (role) {
        searchSQL += ` AND role = ?`
        params.push(role)
      }
      
      if (from) {
        searchSQL += ` AND datetime(timestamp) >= datetime(?)`
        params.push(from)
      }
      
      if (to) {
        searchSQL += ` AND datetime(timestamp) <= datetime(?)`
        params.push(to)
      }
      
      // Order by relevance
      searchSQL += ` ORDER BY bm25(messages_fts) LIMIT ? OFFSET ?`
      params.push(limit, offset)
      
      const results = await this.db.all(searchSQL, params)
      
      // Get total count
      let countSQL = `SELECT COUNT(*) as total FROM messages_fts WHERE messages_fts MATCH ?`
      const countParams = [escapedQuery]
      
      if (projectId) {
        countSQL += ` AND project_id = ?`
        countParams.push(projectId)
      }
      
      if (role) {
        countSQL += ` AND role = ?`
        countParams.push(role)
      }
      
      const countResult = await this.db.get(countSQL, countParams)
      
      return {
        hits: results.map(row => ({
          projectId: row.project_id,
          projectName: row.project_name,
          sessionId: row.session_id,
          sessionTitle: row.session_title,
          messageId: row.message_id,
          role: row.role,
          snippet: row.snippet.replace(/<mark>/g, '**').replace(/<\/mark>/g, '**'), // Convert to markdown
          timestamp: row.timestamp,
          score: Math.abs(row.relevance_score), // BM25 scores are negative
          line: row.line_number,
          template: row.template
        })),
        total: countResult.total,
        query,
        filters: { projectId, role, from, to }
      }
    }
  • CLI help text listing 'search_conversations' as one of the available tools, confirming the tool is exposed via the MCP server.
    search_conversations     Full-text search across conversations
Behavior4/5

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

No annotations are provided, so the description fully covers behavioral traits: it defaults to current project, supports full-text search syntax. Read-only nature is implied but not explicitly stated.

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?

Three sentences, each essential and front-loaded with purpose. No redundant or filler content.

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

Completeness4/5

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

Adequately covers search behavior and parameters, but lacks detail on return format or sorting. Given no output schema, minor gap, but sufficient for typical use.

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

Parameters4/5

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

Schema coverage is 100%, but description adds meaningful context for query (FTS5 syntax), projectId (defaults to current), and allProjects/limit behavior, exceeding the schema alone.

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 searches conversations using full-text search, with a specific verb and resource. It distinguishes itself from siblings which are memory and project management tools.

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

Usage Guidelines5/5

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

Explicit examples are given for when to use this tool ('did we discuss X before?'), and it clarifies defaults and options like allProjects and projectId.

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/kunwar-shah/claudex'

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