Skip to main content
Glama
database-utils.ts9.74 kB
import { homedir, platform } from 'os'; import { join, resolve } from 'path'; import { existsSync } from 'fs'; import type { CursorDatabasePaths, DatabaseConfig } from '../database/types.js'; // Platform-specific database paths (lazy-loaded to support testing) export function getCursorDatabasePaths(): CursorDatabasePaths { return { macOS: join(homedir(), 'Library/Application Support/Cursor/User/globalStorage/state.vscdb'), windows: join(homedir(), 'AppData/Roaming/Cursor/User/globalStorage/state.vscdb'), linux: join(homedir(), '.config/Cursor/User/globalStorage/state.vscdb') }; } /** * Detect the current operating system * @returns The detected operating system as a string */ export function detectOperatingSystem(): 'macOS' | 'windows' | 'linux' | 'unknown' { const currentPlatform = platform(); switch (currentPlatform) { case 'darwin': return 'macOS'; case 'win32': return 'windows'; case 'linux': return 'linux'; default: return 'unknown'; } } /** * Get the default database path for a specific operating system * @param os The operating system identifier * @returns The default database path for the OS */ export function getDefaultDatabasePath(os: string): string { const paths = getCursorDatabasePaths(); switch (os) { case 'macOS': case 'darwin': return paths.macOS; case 'windows': case 'win32': return paths.windows; case 'linux': return paths.linux; default: // Fallback to Linux path for unknown operating systems return paths.linux; } } /** * Check if the database file exists at the specified path * @param path The path to verify * @returns Object with verification result and optional error message */ export function verifyDatabasePath(path: string): { exists: boolean; error?: string } { try { if (!path) { return { exists: false, error: 'Database path is empty' }; } const resolvedPath = resolve(path); const exists = existsSync(resolvedPath); if (!exists) { console.warn(`Database file not found at: ${resolvedPath}`); return { exists: false, error: `Database file not found at: ${resolvedPath}. Make sure Cursor is installed and has been used to create conversations.` }; } return { exists: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { exists: false, error: `Error verifying database path: ${errorMessage}` }; } } /** * Get user-configured database path from environment variables or configuration * @returns The user-configured path if found, null otherwise */ export function getUserConfiguredDatabasePath(): string | null { // Check environment variable first const envPath = process.env.CURSOR_DB_PATH; if (envPath) { const resolvedPath = resolve(envPath.replace(/^~/, homedir())); const verification = verifyDatabasePath(resolvedPath); if (verification.exists) { return resolvedPath; } else { console.warn(`User-configured database path is invalid: ${verification.error}`); } } return null; } /** * Main function to detect the appropriate Cursor database path * Combines all detection mechanisms with proper fallback handling * @returns The resolved database path * @throws Error if no valid database path can be determined */ export function detectDatabasePath(): string { // 1. Check for user-configured path first const userConfiguredPath = getUserConfiguredDatabasePath(); if (userConfiguredPath) { return userConfiguredPath; } // 2. Detect OS and use default path const os = detectOperatingSystem(); const defaultPath = getDefaultDatabasePath(os); const resolvedPath = resolve(defaultPath); // 3. Verify the default path exists const verification = verifyDatabasePath(resolvedPath); if (verification.exists) { return resolvedPath; } // 4. Implement fallback mechanisms console.warn(`Default database path verification failed: ${verification.error}`); // Try alternative common locations as fallbacks const fallbackPaths = getFallbackDatabasePaths(os); for (const fallbackPath of fallbackPaths) { const resolvedFallback = resolve(fallbackPath); const fallbackVerification = verifyDatabasePath(resolvedFallback); if (fallbackVerification.exists) { console.log(`Using fallback database path: ${resolvedFallback}`); return resolvedFallback; } } // If no valid path found, throw descriptive error throw new Error( `Unable to locate Cursor database file. Tried:\n` + `- User configured: ${process.env.CURSOR_DB_PATH || 'Not set'}\n` + `- Default (${os}): ${resolvedPath}\n` + `- Fallback paths: ${fallbackPaths.join(', ')}\n\n` + `Please ensure Cursor is installed and has been used to create conversations, ` + `or set the CURSOR_DB_PATH environment variable to the correct database location.` ); } /** * Get fallback database paths for the given operating system * @param os The operating system identifier * @returns Array of fallback paths to try */ function getFallbackDatabasePaths(os: string): string[] { const fallbacks: string[] = []; switch (os) { case 'macOS': case 'darwin': fallbacks.push( join(homedir(), 'Library/Application Support/Cursor/cursor.db'), join(homedir(), 'Library/Application Support/Cursor/User/cursor.db'), join(homedir(), 'Library/Application Support/Cursor/state.vscdb') ); break; case 'windows': case 'win32': fallbacks.push( join(homedir(), 'AppData/Roaming/Cursor/cursor.db'), join(homedir(), 'AppData/Roaming/Cursor/User/cursor.db'), join(homedir(), 'AppData/Roaming/Cursor/state.vscdb') ); break; case 'linux': fallbacks.push( join(homedir(), '.config/Cursor/cursor.db'), join(homedir(), '.config/Cursor/User/cursor.db'), join(homedir(), '.config/Cursor/state.vscdb') ); break; default: // For unknown OS, try Linux-style paths fallbacks.push( join(homedir(), '.config/Cursor/cursor.db'), join(homedir(), '.config/Cursor/User/cursor.db'), join(homedir(), '.config/Cursor/state.vscdb') ); } return fallbacks; } /** * Automatically detect the Cursor database path for the current platform * @deprecated Use detectDatabasePath() instead for more robust detection */ export function detectCursorDatabasePath(): string { return detectDatabasePath(); } /** * Validate that the database path exists and is accessible * @deprecated Use verifyDatabasePath() instead for consistent error handling */ export function validateDatabasePath(dbPath: string): { valid: boolean; error?: string } { const verification = verifyDatabasePath(dbPath); return { valid: verification.exists, error: verification.error }; } /** * Create default database configuration */ export function createDefaultDatabaseConfig(customDbPath?: string): DatabaseConfig { const dbPath = customDbPath || detectDatabasePath(); return { dbPath, maxConversations: 1000, cacheEnabled: true, minConversationSize: 100, // Reduced from 5000 to capture more conversations resolveBubblesAutomatically: true }; } /** * Extract composer ID from a composerData key */ export function extractComposerIdFromKey(key: string): string | null { const match = key.match(/^composerData:(.+)$/); return match ? match[1] : null; } /** * Extract bubble ID components from a bubbleId key */ export function extractBubbleIdComponents(key: string): { composerId: string; bubbleId: string } | null { const match = key.match(/^bubbleId:([^:]+):(.+)$/); return match ? { composerId: match[1], bubbleId: match[2] } : null; } /** * Generate a bubbleId key for modern format message lookup */ export function generateBubbleIdKey(composerId: string, bubbleId: string): string { return `bubbleId:${composerId}:${bubbleId}`; } /** * Check if a key is a composerData key */ export function isComposerDataKey(key: string): boolean { return key.startsWith('composerData:'); } /** * Check if a key is a bubbleId key */ export function isBubbleIdKey(key: string): boolean { return key.startsWith('bubbleId:'); } /** * Sanitize and validate conversation size filter */ export function sanitizeMinConversationSize(size?: number): number { if (typeof size !== 'number' || size < 0) { return 100; // Default minimum size (reduced from 5000) } return Math.floor(size); } /** * Sanitize and validate limit parameter */ export function sanitizeLimit(limit?: number, maxLimit: number = 1000): number { if (typeof limit !== 'number' || limit <= 0) { return maxLimit; // Default to max limit instead of 10 } return Math.min(Math.floor(limit), maxLimit); } /** * Create SQL LIKE pattern for file pattern matching */ export function createFilePatternLike(pattern: string): string { // Escape SQL special characters and convert glob patterns return pattern .replace(/[%_]/g, '\\$&') // Escape SQL wildcards .replace(/\*/g, '%') // Convert * to SQL % .replace(/\?/g, '_'); // Convert ? to SQL _ } /** * Validate and sanitize search query */ export function sanitizeSearchQuery(query: string): string { if (typeof query !== 'string') { throw new Error('Search query must be a string'); } const trimmed = query.trim(); if (trimmed.length === 0) { throw new Error('Search query cannot be empty'); } if (trimmed.length > 1000) { throw new Error('Search query is too long (max 1000 characters)'); } return trimmed; }

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