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);
    }
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: it discloses performance ('REAL-TIME: <1s'), polling behavior ('Poll this every 5-10s'), completion criteria ('Download completes when status="completed"'), and return format ('Returns progress, speed, ETA, file path'). It doesn't mention error conditions or rate limits, keeping it from a perfect score.

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

Conciseness5/5

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

The description is efficiently structured: first sentence states purpose, second provides performance and return details, third gives usage frequency, fourth defines completion criteria, fifth lists requirements and returns. Every sentence adds value with zero waste, and key information is front-loaded.

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 status-checking tool with no annotations and no output schema, the description is quite complete: it covers purpose, usage, behavior, parameters, and return values. It lacks explicit error handling details and doesn't fully document the monitor parameter's output format, but otherwise provides sufficient context for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/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 value by explaining the downloadId parameter's purpose ('to check') and source ('from download_list'), and implicitly clarifies the monitor parameter's behavior by contrasting manual polling ('Poll this every 5-10s') with auto-monitoring. However, it doesn't fully detail the monitor parameter's combined report output.

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 tool's purpose with specific verbs ('Get real-time status of specific download') and resource ('download'), distinguishing it from sibling tools like download_list (lists downloads), download_cancel (cancels downloads), and download_blobs (downloads content). It explicitly mentions what information is returned (progress percentage, speed, ETA, file path).

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 usage guidance: 'Poll this every 5-10s to track long-running downloads' tells when to use it, 'Download completes when status="completed" and file path is provided' tells when to stop using it, and 'Required: downloadId' indicates prerequisites. It also distinguishes from the monitor parameter's auto-polling functionality.

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

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