Skip to main content
Glama

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(); } }

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