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.'
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 an excellent job disclosing behavioral traits: it specifies async timing (2-15min), state transitions, monitoring behavior, event emission, and environment-specific differences. It doesn't mention error handling or auth requirements, 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 with front-loaded key information (purpose, timing, methods), uses clear sections with bullet-like formatting, and every sentence adds value without redundancy. It balances detail with brevity for a complex tool.

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 complex deployment tool with no annotations, no output schema, and 6 parameters, the description provides strong context about behavior, timing, and usage. It falls short of perfect because it doesn't explain return values or error cases, and leaves some parameters undocumented.

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?

With 0% schema description coverage for 6 parameters, the description compensates well by explaining the purpose of 'deploymentId' (required) and 'monitor' (optional with default behavior). However, it doesn't mention 'projectName', 'projectId', 'apiKey', or 'apiSecret', leaving 4 parameters undocumented.

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 ('Finalize deployment after verification') and distinguishes it from siblings like 'start_deployment' and 'reset_deployment' by focusing on completion. It explicitly mentions the resource (deployment) and the action (finalize/completion).

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 guidance on when to use this tool ('after verification'), preferred methods (subscribe to resource vs. monitor fallback), and distinguishes it from alternatives by describing deployment state transitions. It also mentions prerequisites ('Required: deploymentId') and context for different environments.

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