Skip to main content
Glama
vltansky

Cursor Conversations MCP Server

list_conversations

Retrieve and filter Cursor chat history with summaries, titles, and metadata to find relevant conversations for project-specific analysis or review.

Instructions

Lists Cursor chats with summaries, titles, and metadata ordered by recency. HIGHLY RECOMMENDED: Use projectPath parameter to filter conversations by specific project/codebase - this dramatically improves relevance by finding conversations that actually worked on files in that project. Returns conversation IDs for use with get_conversation tool. WORKFLOW TIP: Start with projectPath filtering for project-specific analysis, then call get_conversation with specific IDs from results. Includes AI-generated summaries by default. Supports date range filtering (YYYY-MM-DD format).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of conversations to return (1-100)
minLengthNoMinimum conversation length in characters to include
hasCodeBlocksNoFilter to conversations that contain code blocks
keywordsNoFilter conversations containing any of these exact keywords (literal text matching)
projectPathNo**RECOMMENDED** Filter conversations by project/codebase name (e.g., "my-app") or full path (e.g., "/Users/name/Projects/my-app"). This finds conversations that actually worked on files in that project, dramatically improving relevance for project-specific analysis.
filePatternNoFilter conversations mentioning files matching this pattern (e.g., "*.tsx")
relevantFilesNoFilter conversations that reference any of these specific files
startDateNoStart date for filtering (YYYY-MM-DD). Note: Timestamps may be unreliable.
endDateNoEnd date for filtering (YYYY-MM-DD). Note: Timestamps may be unreliable.
includeEmptyNoInclude conversations with no messages
includeAiSummariesNoInclude AI-generated conversation summaries
includeRelevanceScoreNoInclude relevance scores when filtering by projectPath
outputModeNoOutput format: "json" for formatted JSON (default), "compact-json" for minified JSONjson

Implementation Reference

  • Primary handler function implementing the core logic for listing Cursor conversations. Validates input, connects to Cursor database, applies filters (projectPath, keywords, dates, etc.), retrieves matching conversation IDs, generates summaries with AI summaries/titles, and returns paginated results with metadata.
    export async function listConversations(input: ListConversationsInput): Promise<ListConversationsOutput> {
      const validatedInput = listConversationsSchema.parse(input);
    
      const dbPath = process.env.CURSOR_DB_PATH || detectCursorDatabasePath();
      const reader = new CursorDatabaseReader({ dbPath });
    
      try {
        await reader.connect();
    
        const filters: ConversationFilters = {
          minLength: validatedInput.minLength,
          format: validatedInput.format,
          hasCodeBlocks: validatedInput.hasCodeBlocks,
          keywords: validatedInput.keywords,
          projectPath: validatedInput.projectPath,
          filePattern: validatedInput.filePattern,
          relevantFiles: validatedInput.relevantFiles
        };
    
        // Add date range filter if provided
        if (validatedInput.startDate || validatedInput.endDate) {
          const start = validatedInput.startDate ? new Date(validatedInput.startDate) : new Date('1970-01-01');
          const end = validatedInput.endDate ? new Date(validatedInput.endDate) : new Date();
          filters.dateRange = { start, end };
        }
    
        const conversationIds = await reader.getConversationIds(filters);
        let limitedIds = conversationIds.slice(0, validatedInput.limit);
    
        // Apply date filtering if specified (post-query filtering due to unreliable timestamps)
        if (validatedInput.startDate || validatedInput.endDate) {
          const filteredIds = [];
          for (const composerId of limitedIds) {
            try {
              const conversation = await reader.getConversationById(composerId);
              if (!conversation) continue;
    
              const hasValidDate = checkConversationDateRange(
                conversation,
                validatedInput.startDate,
                validatedInput.endDate
              );
    
              if (hasValidDate) {
                filteredIds.push(composerId);
              }
            } catch (error) {
              // Skip conversations that can't be processed
              continue;
            }
          }
          limitedIds = filteredIds;
        }
    
        const conversations = [];
        for (const composerId of limitedIds) {
          try {
            const summary = await reader.getConversationSummary(composerId, {
              includeFirstMessage: true,
              maxFirstMessageLength: 150,
              includeTitle: true,
              includeAIGeneratedSummary: validatedInput.includeAiSummaries
            });
    
            if (summary) {
              conversations.push({
                composerId: summary.composerId,
                format: summary.format,
                messageCount: summary.messageCount,
                hasCodeBlocks: summary.hasCodeBlocks,
                relevantFiles: summary.relevantFiles || [],
                attachedFolders: summary.attachedFolders || [],
                firstMessage: summary.firstMessage,
                title: summary.title,
                aiGeneratedSummary: summary.aiGeneratedSummary,
                size: summary.conversationSize
              });
            }
          } catch (error) {
            console.error(`Failed to get summary for conversation ${composerId}:`, error);
          }
        }
    
        return {
          conversations,
          totalFound: conversationIds.length,
          filters: {
            limit: validatedInput.limit ?? 10,
            minLength: validatedInput.minLength ?? 100,
            format: validatedInput.format ?? 'both',
            hasCodeBlocks: validatedInput.hasCodeBlocks,
            keywords: validatedInput.keywords,
            projectPath: validatedInput.projectPath,
            filePattern: validatedInput.filePattern,
            relevantFiles: validatedInput.relevantFiles,
            includeAiSummaries: validatedInput.includeAiSummaries
          }
        };
    
      } finally {
        // Always close the database connection
        reader.close();
      }
    }
  • src/server.ts:51-140 (registration)
    MCP tool registration for 'list_conversations' including description, Zod input schema with parameters like projectPath, limit, dates, and async handler wrapper that calls the core listConversations function (or getConversationsByProject for project relevance scoring).
    server.tool(
      'list_conversations',
      'Lists Cursor chats with summaries, titles, and metadata ordered by recency. **HIGHLY RECOMMENDED: Use projectPath parameter to filter conversations by specific project/codebase** - this dramatically improves relevance by finding conversations that actually worked on files in that project. Returns conversation IDs for use with get_conversation tool. WORKFLOW TIP: Start with projectPath filtering for project-specific analysis, then call get_conversation with specific IDs from results. Includes AI-generated summaries by default. Supports date range filtering (YYYY-MM-DD format).',
      {
        limit: z.number().min(1).max(100).optional().default(10).describe('Maximum number of conversations to return (1-100)'),
        minLength: z.number().min(0).optional().default(100).describe('Minimum conversation length in characters to include'),
        hasCodeBlocks: z.boolean().optional().describe('Filter to conversations that contain code blocks'),
        keywords: z.array(z.string()).optional().describe('Filter conversations containing any of these exact keywords (literal text matching)'),
        projectPath: z.string().optional().describe('**RECOMMENDED** Filter conversations by project/codebase name (e.g., "my-app") or full path (e.g., "/Users/name/Projects/my-app"). This finds conversations that actually worked on files in that project, dramatically improving relevance for project-specific analysis.'),
        filePattern: z.string().optional().describe('Filter conversations mentioning files matching this pattern (e.g., "*.tsx")'),
        relevantFiles: z.array(z.string()).optional().describe('Filter conversations that reference any of these specific files'),
        startDate: z.string().optional().describe('Start date for filtering (YYYY-MM-DD). Note: Timestamps may be unreliable.'),
        endDate: z.string().optional().describe('End date for filtering (YYYY-MM-DD). Note: Timestamps may be unreliable.'),
        includeEmpty: z.boolean().optional().default(false).describe('Include conversations with no messages'),
        includeAiSummaries: z.boolean().optional().default(true).describe('Include AI-generated conversation summaries'),
        includeRelevanceScore: z.boolean().optional().default(false).describe('Include relevance scores when filtering by projectPath'),
        outputMode: z.enum(['json', 'compact-json']).optional().default('json').describe('Output format: "json" for formatted JSON (default), "compact-json" for minified JSON')
      },
      async (input) => {
        try {
          if (input.projectPath && input.includeRelevanceScore) {
            const projectInput = {
              projectPath: input.projectPath,
              filePattern: input.filePattern,
              orderBy: 'recency' as const,
              limit: input.limit,
              fuzzyMatch: false
            };
            const result = await getConversationsByProject(projectInput);
    
            const transformedResult = {
              conversations: result.conversations.map(conv => ({
                ...conv,
                title: undefined,
                aiGeneratedSummary: undefined,
                relevanceScore: conv.relevanceScore
              })),
              totalFound: result.totalFound,
              filters: {
                limit: input.limit ?? 10,
                minLength: input.minLength ?? 100,
                hasCodeBlocks: input.hasCodeBlocks,
                keywords: input.keywords,
                projectPath: input.projectPath,
                filePattern: input.filePattern,
                relevantFiles: input.relevantFiles,
                includeAiSummaries: input.includeAiSummaries
              }
            };
    
            return {
              content: [{
                type: 'text',
                text: formatResponse(transformedResult, input.outputMode)
              }]
            };
          } else {
            const mappedInput = {
              limit: input.limit,
              minLength: input.minLength,
              format: 'both' as const,
              hasCodeBlocks: input.hasCodeBlocks,
              keywords: input.keywords,
              projectPath: input.projectPath,
              filePattern: input.filePattern,
              relevantFiles: input.relevantFiles,
              startDate: input.startDate,
              endDate: input.endDate,
              includeEmpty: input.includeEmpty,
              includeAiSummaries: input.includeAiSummaries
            };
    
            const result = await listConversations(mappedInput);
            return {
              content: [{
                type: 'text',
                text: formatResponse(result, input.outputMode)
              }]
            };
          }
        } catch (error) {
          return {
            content: [{
              type: 'text',
              text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
            }]
          };
        }
      }
    );
  • Internal Zod schema for validating inputs to the listConversations handler, defining parameters like limit, minLength, projectPath filtering, keywords, date ranges, and AI summary inclusion.
    export const listConversationsSchema = z.object({
      limit: z.number().min(1).max(1000).optional(),
      minLength: z.number().min(0).optional(),
      keywords: z.array(z.string()).optional(),
      hasCodeBlocks: z.boolean().optional(),
      format: z.enum(['legacy', 'modern', 'both']).optional(),
      includeEmpty: z.boolean().optional(),
      projectPath: z.string().optional(),
      filePattern: z.string().optional(),
      relevantFiles: z.array(z.string()).optional(),
      startDate: z.string().optional(),
      endDate: z.string().optional(),
      includeAiSummaries: z.boolean().optional().default(true)
    });
  • Supporting handler getConversationsByProject used in the list_conversations wrapper for project-specific filtering with relevance scoring based on file/folder path matching.
    export async function getConversationsByProject(input: GetConversationsByProjectInput): Promise<GetConversationsByProjectOutput> {
      // Validate input
      const validatedInput = getConversationsByProjectSchema.parse(input);
    
      // Create database reader
      const dbPath = process.env.CURSOR_DB_PATH || detectCursorDatabasePath();
      const reader = new CursorDatabaseReader({
        dbPath,
        minConversationSize: 5000 // Default minimum size for project conversations
      });
    
      try {
        // Connect to database
        await reader.connect();
    
        // Get conversation IDs with project-specific filtering
        const conversationResults = await reader.getConversationIdsByProject(
          validatedInput.projectPath,
          {
            filePattern: validatedInput.filePattern,
            exactFilePath: validatedInput.exactFilePath,
            orderBy: validatedInput.orderBy,
            limit: validatedInput.limit,
            format: 'both', // Support both legacy and modern formats
            fuzzyMatch: validatedInput.fuzzyMatch
          }
        );
    
        // Get conversation summaries
        const conversations = [];
        for (const result of conversationResults) {
          try {
            const summary = await reader.getConversationSummary(result.composerId, {
              includeFirstMessage: true,
              maxFirstMessageLength: 100,
              includeFileList: true,
              includeCodeBlockCount: true
            });
    
            if (summary) {
              conversations.push({
                composerId: summary.composerId,
                format: summary.format,
                messageCount: summary.messageCount,
                hasCodeBlocks: summary.hasCodeBlocks,
                relevantFiles: summary.relevantFiles,
                attachedFolders: summary.attachedFolders,
                firstMessage: summary.firstMessage,
                size: summary.conversationSize,
                relevanceScore: result.relevanceScore
              });
            }
          } catch (error) {
            console.error(`Failed to get summary for conversation ${result.composerId}:`, error);
            // Continue with other conversations
          }
        }
    
        return {
          conversations,
          totalFound: conversationResults.length,
          filters: {
            projectPath: validatedInput.projectPath,
            filePattern: validatedInput.filePattern,
            exactFilePath: validatedInput.exactFilePath,
            orderBy: validatedInput.orderBy,
            limit: validatedInput.limit
          }
        };
    
      } finally {
        // Always close the database connection
        reader.close();
      }
    }
Behavior4/5

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

With no annotations provided, the description carries full burden and does well by disclosing key behaviors: ordering ('ordered by recency'), default inclusion of AI-generated summaries, date format requirements ('YYYY-MM-DD format'), and a warning about timestamp reliability. It doesn't mention pagination, rate limits, or authentication needs, but covers substantial operational context.

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

Conciseness4/5

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

The description is appropriately sized and front-loaded with core functionality. The bold recommendation about projectPath is prominent, and the workflow tip provides practical guidance. Some sentences could be more concise, but overall it's well-structured with zero wasted 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?

For a 13-parameter tool with no annotations and no output schema, the description provides substantial context about usage patterns, filtering importance, and relationship to other tools. It doesn't describe the return format in detail (though mentions conversation IDs), but given the rich parameter documentation in the schema, it's reasonably complete for guiding effective tool selection.

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 baseline is 3. The description adds some value by emphasizing the importance of 'projectPath' parameter and providing workflow context, but doesn't significantly enhance understanding of individual parameters beyond what the schema already documents thoroughly.

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 verb ('Lists') and resource ('Cursor chats') with specific attributes ('summaries, titles, and metadata ordered by recency'). It distinguishes from siblings like 'search_conversations' by emphasizing comprehensive listing with filtering rather than search functionality, and explicitly mentions the relationship to 'get_conversation' tool.

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?

The description provides explicit guidance on when to use this tool ('HIGHLY RECOMMENDED: Use projectPath parameter...'), includes a workflow tip ('Start with projectPath filtering... then call get_conversation'), and implicitly distinguishes from alternatives like 'search_conversations' by focusing on filtered listing rather than search operations.

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/vltansky/cursor-conversations-mcp'

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