db_export_download
Download Optimizely DXP database exports to local storage. Initiates background downloads for large files and provides tracking IDs to monitor progress and retrieve file paths upon completion.
Instructions
📥 Download completed database export to local storage. BACKGROUND: returns immediately with download ID, actual download continues in background. Large files (>1GB) may take 5-20min. Use download_status() to monitor download progress and get local file path when complete. Required: downloadUrl. Optional: downloadPath, background (default true). Returns downloadId for tracking.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| exportId | No | Export ID to download (not required if downloadUrl provided) | |
| environment | No | Environment where export was created (not required if downloadUrl provided) | |
| downloadUrl | No | Direct SAS URL to download from (skips API authentication - useful for downloaded URLs from db_export_status) | |
| downloadPath | No | Directory to save downloaded export | |
| background | No | Download in background vs wait for completion (default: true) | |
| skipConfirmation | No | Skip file overwrite confirmation prompts | |
| monitor | No | Enable download monitoring - instructs AI to poll check_download_status until complete | |
| project | No | Project name (default: current project) | |
| projectName | No | Alternative to project parameter | |
| projectId | No | Project UUID (if providing inline credentials) | |
| apiKey | No | API key (if providing inline credentials) | |
| apiSecret | No | API secret (if providing inline credentials) |
Implementation Reference
- Main handler function executing the db_export_download tool logic. Supports both blocking and background downloads of database export files using the provided download URL.static async handleDownloadDatabaseExport(args: DownloadDatabaseExportArgs): Promise<any> { if (!args.downloadUrl) { return ResponseBuilder.invalidParams('Missing required parameter: downloadUrl'); } // DXP-81: Support new 'database' parameter const databaseName = args.database || args.databaseName; try { // DXP-183: Honor background parameter (defaults to true per schema) const useBackground = args.background !== false; // Default to true if not specified if (useBackground) { // Start background download, return immediately with downloadId const downloadId = await this.startBackgroundDatabaseDownload( args.downloadUrl, args.downloadPath, args.projectName, args.environment, databaseName ); const fileSize = await this.getRemoteFileSize(args.downloadUrl).catch(() => 0); const estimatedTime = this.estimateDownloadTime(fileSize); const message = `📥 **Background Database Download Started**\n\n` + `**Download Details:**\n` + `• Download ID: \`${downloadId}\`\n` + `• Project: ${args.projectName || 'Unknown'}\n` + `• Environment: ${args.environment || 'Production'}\n` + `• Database: ${databaseName || 'epicms'}\n` + `• Size: ${this.formatBytes(fileSize)}\n` + `• Estimated Time: ${estimatedTime}\n\n` + `**Monitor Progress:**\n` + `Use \`download_list()\` to check download status.\n` + `Use \`download_status({ downloadId: "${downloadId}", monitor: true })\` for live updates.\n\n` + `⚠️ **Note**: Background downloads skip confirmation and start immediately.`; return ResponseBuilder.success(message); } else { // Blocking download (background=false explicitly set) const result = await this.downloadFromUrl( args.downloadUrl, args.downloadPath, args.projectName, args.environment, databaseName, args.skipConfirmation, args.incremental, args.timeoutMinutes ); return ResponseBuilder.success(result); } } catch (error: any) { console.error('Download database export error:', error); return ResponseBuilder.internalError('Failed to download database export', error.message); } }
- TypeScript interface defining the input parameters (schema) for the db_export_download tool handler.interface DownloadDatabaseExportArgs { downloadUrl?: string; downloadPath?: string; projectName?: string; environment?: string; databaseName?: string; database?: string; skipConfirmation?: boolean; incremental?: boolean; timeoutMinutes?: number; }
- lib/utils/tool-availability-matrix.ts:211-215 (registration)Tool availability matrix entry registering 'db_export_download' with hosting restrictions, category, and description.'db_export_download': { hostingTypes: ['dxp-paas'], category: 'Database', description: 'Download completed export (background or synchronous)', restrictedMessage: 'Database export download is only available for DXP PaaS hosting.'
- Key helper function performing the synchronous file download from Azure Blob URL with progress monitoring, timeout handling, and state persistence.static async downloadFromUrl( downloadUrl: string, downloadPath: string | undefined, projectName: string | undefined, environment: string | undefined, databaseName: string | undefined, skipConfirmation?: boolean, incremental?: boolean, timeoutMinutes?: number ): Promise<string> { // DXP-186: Use DownloadConfig to respect dbPath configuration const basePath = await DownloadConfig.getDownloadPath( 'database', projectName || 'Unknown', downloadPath || null ); const safeProjectName = (projectName || 'project').replace(/[^a-zA-Z0-9-_]/g, '_'); const safeEnvironment = (environment || 'production').toLowerCase(); const safeDatabaseName = (databaseName || 'epicms').replace(/[^a-zA-Z0-9-_]/g, '_'); const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5); const filename = `${safeProjectName}-${safeEnvironment}-${safeDatabaseName}-${timestamp}.bacpac`; const filepath = path.join(basePath, filename); // Create download ID for tracking const downloadId = `${safeProjectName}-db-${safeEnvironment}-${safeDatabaseName}-${Date.now()}`; // Check if we should use incremental download if (incremental) { const existingBackups = await this.checkForExistingBackups( basePath, projectName || 'project', environment || 'Production', databaseName || 'epicms' ); if (existingBackups.length > 0) { const latest = existingBackups[0]; OutputLogger.info(`Found existing backup: ${latest.fileName} (${this.formatBytes(latest.fileSize)})`); OutputLogger.info('Incremental mode: Checking if download is needed...'); // For database exports, we can't do true incremental (they're full backups) // But we can check if we already have a recent backup if (latest.ageHours < 24) { return `Using existing backup: ${latest.fileName}\\n` + `Location: ${latest.filePath}\\n` + `Size: ${this.formatBytes(latest.fileSize)}\\n` + `Age: ${latest.formattedAge}\\n\\n` + `Backup is less than 24 hours old. Skipping download.`; } } } // Get remote file size for progress tracking const fileSize = await this.getRemoteFileSize(downloadUrl); const estimatedTime = this.estimateDownloadTime(fileSize); if (!skipConfirmation) { OutputLogger.info(`About to download database backup:`); OutputLogger.info(` File: ${filename}`); OutputLogger.info(` Size: ${this.formatBytes(fileSize)}`); OutputLogger.info(` Estimated time: ${estimatedTime}`); OutputLogger.info(` Destination: ${filepath}`); } // Register download with DownloadManager DownloadManager.registerDownload({ projectName: downloadId, containerName: databaseName || 'epicms', environment: environment || 'Production', dateRange: 'export', type: 'database', totalFiles: 1, totalSize: fileSize }); // Save download state const downloadState: DownloadState = { downloadId, projectName: projectName || 'Unknown', environment: environment || 'Production', databaseName: databaseName || 'epicms', downloadUrl, downloadPath: filepath, status: 'active', startedAt: new Date().toISOString(), fileSize }; await this.saveDownloadState(downloadState); try { // Download file with timeout protection const timeoutMs = (timeoutMinutes || 30) * 60 * 1000; // Default 30 minutes await this.downloadFile(downloadUrl, filepath, timeoutMs, downloadId); // Mark download complete DownloadManager.completeDownload(downloadId, { success: true }); downloadState.status = 'completed'; downloadState.completedAt = new Date().toISOString(); await this.saveDownloadState(downloadState); // Save backup info const backupInfo: BackupInfo = { projectName: projectName || 'Unknown', environment: environment || 'Production', databaseName: databaseName || 'epicms', exportId: downloadUrl.split('/').pop() || 'unknown', downloadUrl, downloadedAt: new Date().toISOString(), filePath: filepath, fileSize }; await this.storeBackupInfo(projectName || 'Unknown', backupInfo); return `✅ Database backup downloaded successfully\\n` + `File: ${filename}\\n` + `Size: ${this.formatBytes(fileSize)}\\n` + `Location: ${filepath}`; } catch (error: any) { // Mark download failed DownloadManager.failDownload(downloadId, error.message); downloadState.status = 'failed'; downloadState.error = error.message; downloadState.completedAt = new Date().toISOString(); await this.saveDownloadState(downloadState); throw error; } }
- Helper for initiating background (non-blocking) database downloads to avoid MCP timeouts, integrates with download management system.static async startBackgroundDatabaseDownload( downloadUrl: string, downloadPath: string | undefined, projectName: string | undefined, environment: string | undefined, databaseName: string | undefined ): Promise<string> { // DXP-186: Use DownloadConfig to respect dbPath configuration const basePath = await DownloadConfig.getDownloadPath( 'database', projectName || 'Unknown', downloadPath || null ); const safeProjectName = (projectName || 'project').replace(/[^a-zA-Z0-9-_]/g, '_'); const safeEnvironment = (environment || 'production').toLowerCase(); const safeDatabaseName = (databaseName || 'epicms').replace(/[^a-zA-Z0-9-_]/g, '_'); // Create download ID for tracking const downloadId = `${safeProjectName}-db-${safeEnvironment}-${safeDatabaseName}-${Date.now()}`; // Get remote file size for preview let fileSize = 0; try { fileSize = await this.getRemoteFileSize(downloadUrl); } catch (error: any) { OutputLogger.warn(`⚠️ Could not get file size: ${error.message}`); } // Register download with DownloadManager DownloadManager.registerDownload({ projectName: downloadId, containerName: databaseName || 'epicms', environment: environment || 'Production', dateRange: 'export', type: 'database', totalFiles: 1, totalSize: fileSize }); // Start download in background (don't await!) this.runDatabaseDownloadInBackground( downloadId, downloadUrl, downloadPath, projectName, environment, databaseName, fileSize ).catch(error => { OutputLogger.error(`Background database download ${downloadId} failed: ${error.message}`); DownloadManager.failDownload(downloadId, error.message); }); return downloadId;