Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

complete_deployment

Finalize verified deployments in Optimizely DXP by transitioning them from AwaitingVerification to completion, with automatic progress monitoring and real-time event notifications.

Instructions

✅ Finalize deployment after verification. ASYNC: 2-15min. For AI Agents: Subscribe to deployment://{deploymentId} resource for real-time completion events (preferred method). Alternatively, set monitor=true (default) for background polling fallback. Deployment transitions: AwaitingVerification → Completing → Succeeded/Failed. After completing, monitor automatically tracks progress and emits events. Production deployments take 2-15min, other environments <1min. Required: deploymentId. Optional: monitor (default true).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
deploymentIdYes
monitorNo
projectNameNo
projectIdNo
apiKeyNo
apiSecretNo

Implementation Reference

  • Primary handler function for the 'complete_deployment' tool. Validates args, checks deployment state, calls DXP REST API to complete deployment, emits events, starts monitoring, and formats response.
    static async handleCompleteDeployment(args: CompleteDeploymentArgs): Promise<any> {
        // Check if this is a self-hosted project
        if (args.isSelfHosted || args.connectionString) {
            return ResponseBuilder.invalidParams('Deployment completion is not available for self-hosted projects. Self-hosted projects can only download existing backups and blobs.');
        }
    
        if (!args.apiKey || !args.apiSecret || !args.projectId || !args.deploymentId) {
            return ResponseBuilder.invalidParams('Missing required parameters');
        }
    
        try {
            const result = await this.completeDeployment(args);
    
            // Check if result is already a structured response with data and message
            if (result && typeof result === 'object' && 'data' in result && 'message' in result) {
                // DXP-68: If result contains an error field, return as error response
                if (result.data.error) {
                    return {
                        error: result.message,
                        data: result.data
                    };
                }
    
                return ResponseBuilder.successWithStructuredData(result.data, result.message);
            }
    
            // Fallback for legacy string responses
            return ResponseBuilder.success(result);
        } catch (error: any) {
            console.error('Complete deployment error:', error);
            return ResponseBuilder.internalError('Failed to complete deployment', error.message);
        }
    }
    
    static async completeDeployment(args: CompleteDeploymentArgs): Promise<any> {
        const { apiKey, apiSecret, projectId, deploymentId } = args;
    
        console.error(`Completing deployment ${deploymentId} for project ${projectId}`);
    
        // DXP-68: Validate deployment state before attempting completion
        console.error('Checking deployment state before completion...');
        const DeploymentListOperations = require('./deployment-list');
    
        try {
            const statusResult = await DeploymentListOperations.getDeploymentStatus({
                apiKey,
                apiSecret,
                projectId,
                deploymentId
            });
    
            // Extract current status from the response
            let currentStatus = 'Unknown';
    
            // Check if this is a structured response (new format from DXP-66)
            if (statusResult && typeof statusResult === 'object' && 'data' in statusResult && 'message' in statusResult) {
                currentStatus = statusResult.data.status || 'Unknown';
            } else if (typeof statusResult === 'string' && statusResult.includes('Status:')) {
                const statusMatch = statusResult.match(/Status:\s*\*\*([^*]+)\*\*/);
                if (statusMatch) {
                    currentStatus = statusMatch[1].trim();
                }
            }
    
            console.error(`Current deployment status: ${currentStatus}`);
    
            // Check if deployment is in the correct state for completion (DXP-69: Use status constants)
            if (!isAwaitingVerification(currentStatus)) {
                // Return structured error response
                const errorMessage = `❌ **Cannot Complete Deployment**\n\n` +
                    `Deployment must be in **${DEPLOYMENT_STATUS.AWAITING_VERIFICATION}** state to complete.\n\n` +
                    `**Current State**: ${currentStatus}\n` +
                    `**Deployment ID**: ${deploymentId}\n\n` +
                    `**Next Steps:**\n` +
                    (isInProgress(currentStatus)
                        ? `• Wait for deployment to reach verification state\n` +
                          `• Use \`get_deployment_status\` to check progress\n` +
                          `• Use \`monitor_deployment\` for continuous updates`
                        : isSucceeded(currentStatus)
                        ? `• Deployment is already completed - no action needed`
                        : isFailed(currentStatus)
                        ? `• Deployment has failed - use \`reset_deployment\` to rollback`
                        : `• Check deployment status with \`get_deployment_status\``
                    );
    
                return {
                    data: {
                        error: 'INVALID_STATE',
                        deploymentId: deploymentId,
                        currentStatus: currentStatus,
                        requiredStatus: DEPLOYMENT_STATUS.AWAITING_VERIFICATION,
                        canComplete: false
                    },
                    message: ResponseBuilder.addFooter(errorMessage)
                };
            }
    
            console.error('✓ Deployment is in valid state for completion');
    
        } catch (statusError: any) {
            console.error('Warning: Could not check deployment status before completion:', statusError.message);
            // Continue with completion attempt even if status check fails
            // The REST API itself will fail if state is wrong
        }
    
        // DXP-101: Use REST API instead of PowerShell (3-10x faster, no PowerShell dependency)
        try {
            const result: DeploymentResult = await DXPRestClient.completeDeployment(
                projectId!,
                apiKey!,
                apiSecret!,
                deploymentId!,
                { apiUrl: args.apiUrl } // Support custom API URLs
            );
    
            // DXP-47: The completion API returns immediately with status "Completing" (transitional),
            // not "Succeeded" (final). Format the response with the ACTUAL current status.
            if (result) {
                // DXP-136: Emit deployment completing event
                try {
                    if (result.status === 'Completing') {
                        DeploymentResourceHandler.emitCompleting(deploymentId!, {
                            project: args.projectName,
                            status: result.status,
                            slotUrl: result.deploymentSlotUrl || result.DeploymentSlotUrl
                        });
                    } else if (result.status === 'Succeeded') {
                        DeploymentResourceHandler.emitSucceeded(deploymentId!, {
                            project: args.projectName,
                            status: result.status,
                            slotUrl: result.deploymentSlotUrl || result.DeploymentSlotUrl
                        });
                    }
                } catch (eventError: any) {
                    console.error(`Failed to emit deployment completion event: ${eventError.message}`);
                    // Don't fail the operation if event emission fails
                }
    
                // DXP-121: Auto-start background monitoring if enabled (default true)
                if (args.monitor !== false && result && result.id) {
                    try {
                        const monitor = getGlobalMonitor();
                        monitor.startMonitoring({
                            deploymentId: result.id || deploymentId!,
                            projectId: args.projectId!,
                            apiKey: args.apiKey!,
                            apiSecret: args.apiSecret!,
                            interval: 30 * 1000 // 30 seconds (faster polling for completion phase)
                        });
                        const logger = new StructuredLogger({
                            context: {
                                tool: 'complete_deployment',
                                deployment_id: result.id || deploymentId!
                            }
                        });
                        logger.info('Auto-monitoring started for deployment completion', {
                            deployment_id: result.id || deploymentId!,
                            interval_ms: 30 * 1000
                        });
                        console.log('🔄 Auto-monitoring started for deployment completion');
                    } catch (monitorError: any) {
                        console.error(`Failed to start monitoring: ${monitorError.message}`);
                        // Don't fail the operation if monitoring fails
                    }
                }
    
                return DeploymentFormatters.formatDeploymentCompleted(result, args.projectName, projectId);
            } else {
                // Fallback if no result returned
                return {
                    data: { deploymentId: deploymentId, status: 'Unknown' },
                    message: ResponseBuilder.addFooter('Deployment completion initiated but status unavailable')
                };
            }
    
        } catch (error: any) {
            // Handle REST API errors
            const errorDetails = {
                operation: 'Complete Deployment',
                projectId,
                projectName: args.projectName,
                deploymentId,
                apiKey
            };
    
            // Check if this is an access denied error
            if (error.statusCode === 401 || error.statusCode === 403) {
                return ErrorHandler.formatError({
                    type: 'ACCESS_DENIED',
                    message: 'Access denied to deployment API',
                    statusCode: error.statusCode
                } as any, errorDetails);
            }
    
            // Generic error handling
            return ErrorHandler.formatError({
                type: 'API_ERROR',
                message: error.message,
                statusCode: error.statusCode
            } as any, errorDetails);
        }
    }
  • DeploymentTools aggregator entry point that delegates 'complete_deployment' handling to DeploymentActionOperations.
        static async handleCompleteDeployment(args: any): Promise<any> {
            return DeploymentActionOperations.handleCompleteDeployment(args);
        }
    
        static async handleResetDeployment(args: any): Promise<any> {
            return DeploymentActionOperations.handleResetDeployment(args);
        }
    
        static async handleMonitorDeployment(args: any): Promise<any> {
            return DeploymentActionOperations.handleMonitorDeployment(args);
        }
    }
  • Low-level REST API client method that performs the actual POST request to complete the deployment via Optimizely DXP API.
    static async completeDeployment(
        projectId: string,
        clientKey: string,
        clientSecret: string,
        deploymentId: string,
        options: RequestOptions = {}
    ): Promise<any> {
        const uriEnding = `projects/${projectId}/deployments/${deploymentId}/complete`;
        return await this.makeRequest(clientKey, clientSecret, uriEnding, 'POST', {}, options);
    }
  • Tool registration/definition in availability matrix, specifying it's for DXP PaaS, category, description, and restriction message.
    'complete_deployment': {
        hostingTypes: ['dxp-paas'],
        category: 'Deployments',
        description: 'Complete an in-progress deployment',
        restrictedMessage: 'Completing deployments is only available for DXP PaaS hosting.'

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