Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

download_status

Check real-time progress of Optimizely DXP downloads by tracking percentage, speed, ETA, and file path. Poll every 5-10 seconds to monitor ongoing transfers.

Instructions

πŸ“Š Get real-time status of specific download. REAL-TIME: <1s. Returns current progress percentage, download speed (MB/s), estimated time remaining, and local file path when download completes. Poll this every 5-10s to track long-running downloads. Download completes when status="completed" and file path is provided. Required: downloadId. Returns progress, speed, ETA, file path.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
downloadIdYesDownload ID to check (from download_list)
monitorNoDXP-3: Auto-monitor download - polls every 10s and shows live progress updates until complete. Returns combined progress report. Default: false (single status check)

Implementation Reference

  • Main handler function executing the download_status tool. Handles both log and database downloads, supports live progress display, structured data output, and optional monitoring mode.
    static async handleDownloadStatus(args: DownloadStatusArgs): Promise<any> {
        if (!args.downloadId) {
            return ResponseBuilder.invalidParams('downloadId is required.');
        }
    
        // DXP-3: Check if auto-monitoring is enabled
        const shouldMonitor = args.monitor === true;
    
        try {
            // DXP-3: If monitoring enabled, poll until complete
            if (shouldMonitor) {
                return await this.monitorDownloadProgress(args.downloadId);
            }
    
            // First check log download system (active + history)
            let download = downloadManager.getDownloadOrHistory(args.downloadId);
    
            // If not found, check database export system
            if (!download) {
                const DatabaseSimpleTools = require('./database-simple-tools');
                // DXP-178 FIX: Use .backgroundDownloads.get() instead of non-existent .getDownloadStatus()
                download = DatabaseSimpleTools.backgroundDownloads.get(args.downloadId);
    
                // If found in database system, return database-specific status
                if (download) {
                    return this._formatDatabaseStatusResponse(download, args.downloadId);
                }
    
                // Not found in either system
                return ResponseBuilder.error(
                    `Download ${args.downloadId} not found.\n\n` +
                    `**View active downloads:** \`download_list({ status: "active" })\`\n` +
                    `**View recent history:** \`download_list({ status: "all" })\``
                );
            }
    
            // DXP-3: Get live progress from ProgressMonitor if available
            const liveProgress: LiveProgress | null = downloadManager.getLiveProgress(args.downloadId) as LiveProgress | null;
    
            // Format log download status
            const elapsed = Date.now() - download.startTime;
            const elapsedMinutes = Math.floor(elapsed / 60000);
            const elapsedSeconds = Math.floor((elapsed % 60000) / 1000);
    
            let message = `# πŸ“Š Download Status\n\n`;
            message += `**ID**: ${download.key}\n`;
            message += `**Type**: ${download.containerName} logs\n`;
            message += `**Project**: ${download.projectName} (${download.environment})\n`;
            message += `**Status**: ${download.status}\n`;
    
            // DXP-3: Show detailed progress if ProgressMonitor is available
            if (liveProgress && liveProgress.totalFiles) {
                message += `**Progress**: ${liveProgress.percentage}% (${liveProgress.filesDownloaded}/${liveProgress.totalFiles} files)\n`;
    
                if (liveProgress.bytesDownloaded > 0) {
                    const formatBytes = (bytes: number): string => {
                        if (bytes === 0) return '0 B';
                        const k = 1024;
                        const sizes = ['B', 'KB', 'MB', 'GB'];
                        const i = Math.floor(Math.log(bytes) / Math.log(k));
                        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
                    };
    
                    message += `**Downloaded**: ${formatBytes(liveProgress.bytesDownloaded)}`;
                    if (liveProgress.totalBytes > 0) {
                        message += ` / ${formatBytes(liveProgress.totalBytes)}`;
                    }
                    message += `\n`;
    
                    if (liveProgress.speed > 0) {
                        message += `**Speed**: ${formatBytes(liveProgress.speed)}/s\n`;
    
                        if (liveProgress.eta && liveProgress.eta > 0) {
                            const etaMinutes = Math.floor(liveProgress.eta / 60);
                            const etaSeconds = Math.round(liveProgress.eta % 60);
                            message += `**ETA**: ${etaMinutes}m ${etaSeconds}s\n`;
                        }
                    }
                }
    
                if (liveProgress.currentFile) {
                    const displayFile = liveProgress.currentFile.length > 60
                        ? '...' + liveProgress.currentFile.substring(liveProgress.currentFile.length - 57)
                        : liveProgress.currentFile;
                    message += `**Current File**: ${displayFile}\n`;
                }
            } else {
                message += `**Progress**: ${download.progress}%\n`;
            }
    
            message += `**Runtime**: ${elapsedMinutes}m ${elapsedSeconds}s\n`;
    
            if (download.dateRange && download.dateRange !== 'all-time') {
                message += `**Date Range**: ${download.dateRange}\n`;
            }
    
            if (download.pid) {
                message += `**Process ID**: ${download.pid}\n`;
            }
    
            // DXP-190: Show download path if completed
            if (download.status === 'completed' && download.result && download.result.downloadPath) {
                message += `\nπŸ“ **Files Location:**\n`;
                message += `\`\`\`\n${download.result.downloadPath}\n\`\`\`\n`;
                if (download.result.actualFilesOnDisk !== undefined) {
                    message += `βœ… Verified ${download.result.actualFilesOnDisk} files on disk\n`;
                }
            }
    
            // Show error if download failed
            if (download.error) {
                message += `\n**❌ Error**: ${download.error}\n`;
            }
    
            message += `\n**Actions**:\n`;
            if (download.status === 'failed') {
                message += `β€’ Retry: Start a new download with same parameters\n`;
                message += `β€’ Debug: Set DEBUG=true environment variable for detailed logs\n`;
            } else {
                message += `β€’ Cancel: \`download_cancel({ downloadId: "${download.key}" })\`\n`;
            }
            message += `β€’ View all: \`download_list({ status: "active" })\`\n`;
    
            // DXP-3: Add structured data with live progress
            const structuredData: any = {
                downloadId: download.key,
                type: 'logs',
                containerName: download.containerName,
                projectName: download.projectName,
                environment: download.environment,
                status: download.status,
                progress: download.progress,
                dateRange: download.dateRange || 'all-time',
                elapsedMs: elapsed,
                pid: download.pid || null
            };
    
            // DXP-190: Include download path and verified file count if completed
            if (download.status === 'completed' && download.result) {
                if (download.result.downloadPath) {
                    structuredData.downloadPath = download.result.downloadPath;
                }
                if (download.result.actualFilesOnDisk !== undefined) {
                    structuredData.actualFilesOnDisk = download.result.actualFilesOnDisk;
                }
            }
    
            // Include live progress data if available
            if (liveProgress && liveProgress.totalFiles) {
                structuredData.liveProgress = liveProgress;
            }
    
            return ResponseBuilder.successWithStructuredData(structuredData, message);
    
        } catch (error: any) {
            OutputLogger.error(`Get download status error: ${error}`);
            return ResponseBuilder.internalError('Failed to get download status', error.message);
        }
    }
  • Input schema defining required downloadId and optional monitor flag for the download_status tool.
    interface DownloadStatusArgs {
        downloadId: string;
        monitor?: boolean;
    }
  • Helper function for monitoring download progress when monitor=true. Polls every 10s for up to 30min, providing live updates.
    static async monitorDownloadProgress(downloadId: string): Promise<any> {
        const updates: string[] = [];
        const startTime = Date.now();
        let pollCount = 0;
        const MAX_POLLS = 180; // 30 minutes max (180 * 10s)
    
        OutputLogger.info(`πŸ“Š Monitoring download: ${downloadId}`);
        OutputLogger.info(`Will poll every 10 seconds until complete...`);
    
        // Wait 2 seconds before first poll to give download time to start
        if (process.env.DEBUG === 'true') {
            console.error('[DEBUG] Waiting 2 seconds before first poll...');
        }
        await new Promise(resolve => setTimeout(resolve, 2000));
    
        while (pollCount < MAX_POLLS) {
            pollCount++;
    
            // Get current status (check active and history)
            const download = downloadManager.getDownloadOrHistory(downloadId);
    
            // Check if download no longer exists
            if (!download) {
                return ResponseBuilder.error(`Download ${downloadId} not found`);
            }
    
            // Check if download is in final state (completed/failed)
            if (download.status === 'completed' || download.status === 'failed' || download.status === 'cancelled') {
                updates.push(`\nβœ… **Download Complete**`);
                if (download.status === 'completed') {
                    updates.push(`Final status: Completed successfully`);
                } else if (download.status === 'failed') {
                    updates.push(`Final status: Failed - ${download.error || 'Unknown error'}`);
                } else {
                    updates.push(`Final status: ${download.status}`);
                }
                break;
            }
    
            // Get live progress
            const liveProgress: LiveProgress | null = downloadManager.getLiveProgress(downloadId) as LiveProgress | null;
    
            // Format update
            let update = `\nπŸ“₯ **Progress Update #${pollCount}** (${Math.floor((Date.now() - startTime) / 1000)}s elapsed)`;
    
            if (liveProgress && liveProgress.totalFiles) {
                update += `\n   ${liveProgress.percentage}% - ${liveProgress.filesDownloaded}/${liveProgress.totalFiles} files`;
    
                if (liveProgress.bytesDownloaded > 0) {
                    const formatBytes = (bytes: number): string => {
                        if (bytes === 0) return '0 B';
                        const k = 1024;
                        const sizes = ['B', 'KB', 'MB', 'GB'];
                        const i = Math.floor(Math.log(bytes) / Math.log(k));
                        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
                    };
    
                    update += ` (${formatBytes(liveProgress.bytesDownloaded)}`;
                    if (liveProgress.totalBytes > 0) {
                        update += ` / ${formatBytes(liveProgress.totalBytes)}`;
                    }
                    update += `)`;
    
                    if (liveProgress.speed > 0) {
                        update += ` - ${formatBytes(liveProgress.speed)}/s`;
    
                        if (liveProgress.eta && liveProgress.eta > 0) {
                            const etaMin = Math.floor(liveProgress.eta / 60);
                            const etaSec = Math.round(liveProgress.eta % 60);
                            update += ` - ETA: ${etaMin}m ${etaSec}s`;
                        }
                    }
                }
            } else {
                update += `\n   ${download.progress}% complete`;
            }
    
            updates.push(update);
    
            // Check if download is complete
            const status = download.status as string;
            if (status === 'completed' || status === 'failed' || status === 'cancelled') {
                updates.push(`\nβœ… **Download ${status}**`);
                break;
            }
    
            // Wait 10 seconds before next poll
            await new Promise(resolve => setTimeout(resolve, 10000));
        }
    
        if (pollCount >= MAX_POLLS) {
            updates.push(`\n⚠️ **Monitoring timeout** - download still running after 30 minutes`);
            updates.push(`Check status manually: \`download_status({ downloadId: "${downloadId}" })\``);
        }
    
        const message = `# πŸ“Š Download Monitoring Complete\n\n` +
            `**Download ID**: ${downloadId}\n` +
            `**Total monitoring time**: ${Math.floor((Date.now() - startTime) / 1000)}s\n` +
            `**Updates**: ${pollCount}\n` +
            updates.join('\n');
    
        return ResponseBuilder.success(message);
    }
  • Tool availability registration defining download_status as available across all hosting types in the Download Management category.
    'download_status': {
        hostingTypes: ['dxp-paas', 'dxp-saas', 'self-hosted'],
        category: 'Download Management',
        description: 'Get download status with optional auto-monitoring'
    },
  • Private helper formatting status response specifically for database export downloads.
    static _formatDatabaseStatusResponse(download: any, downloadId: string): any {
        const elapsed = Date.now() - download.startTime;
        const elapsedMinutes = Math.floor(elapsed / 60000);
        const elapsedSeconds = Math.floor((elapsed % 60000) / 1000);
    
        let message = `# πŸ“Š Database Export Download Status\n\n`;
        message += `**ID**: ${downloadId}\n`;
        message += `**Type**: Database Export\n`;
        message += `**Status**: ${download.status}\n`;
    
        if (download.percent !== undefined) {
            message += `**Progress**: ${download.percent}%\n`;
        }
    
        if (download.bytesDownloaded && download.totalBytes) {
            const downloadedMB = (download.bytesDownloaded / (1024 * 1024)).toFixed(2);
            const totalMB = (download.totalBytes / (1024 * 1024)).toFixed(2);
            message += `**Downloaded**: ${downloadedMB} MB / ${totalMB} MB\n`;
        }
    
        message += `**Runtime**: ${elapsedMinutes}m ${elapsedSeconds}s\n`;
    
        if (download.filePath) {
            message += `**File Path**: ${download.filePath}\n`;
        }
    
        if (download.error) {
            message += `**Error**: ${download.error}\n`;
        }
    
        message += `\n**Actions**:\n`;
        message += `β€’ Check again: \`download_status({ downloadId: "${downloadId}" })\`\n`;
        message += `β€’ View all: \`download_list({ status: "active" })\`\n`;
    
        return ResponseBuilder.successWithStructuredData({
            downloadId: downloadId,
            type: 'database_export',
            status: download.status,
            progress: download.percent || 0,
            bytesDownloaded: download.bytesDownloaded || 0,
            totalBytes: download.totalBytes || 0,
            filePath: download.filePath || null,
            elapsedMs: elapsed,
            error: download.error || null
        }, message);
    }

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/JaxonDigital/optimizely-dxp-mcp'

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