complete_deployment
Finalize a deployment in Verification state on Optimizely DXP MCP Server. Use this tool to complete the process by providing the required deployment ID, ensuring the deployment moves to the next stage.
Instructions
Complete a deployment that is in Verification state
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| apiKey | No | ||
| apiSecret | No | ||
| deploymentId | Yes | ||
| projectId | No | ||
| projectName | No |
Implementation Reference
- Main entry point handler for the 'complete_deployment' MCP tool. Validates arguments, checks hosting type, calls core completeDeployment logic, handles structured responses and errors.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); } }
- Core implementation logic for completing deployment: validates state, calls DXP REST API, emits events, starts monitoring, formats response.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); } }
- TypeScript interface defining input parameters/schema for complete_deployment tool.interface CompleteDeploymentArgs { apiKey?: string; apiSecret?: string; projectId?: string; projectName?: string; deploymentId?: string; monitor?: boolean; isSelfHosted?: boolean; connectionString?: string; apiUrl?: string; }
- lib/tools/deployment/index.ts:26-27 (registration)DeploymentTools aggregator registers/delegates handleCompleteDeployment to the implementation in deployment-actions.ts.static async handleCompleteDeployment(args: any): Promise<any> { return DeploymentActionOperations.handleCompleteDeployment(args);
- lib/utils/tool-availability-matrix.ts:129-133 (registration)Tool availability registration defining 'complete_deployment' availability, category, description for different hosting types.'complete_deployment': { hostingTypes: ['dxp-paas'], category: 'Deployments', description: 'Complete an in-progress deployment', restrictedMessage: 'Completing deployments is only available for DXP PaaS hosting.'
- lib/dxp-rest-client.ts:564-573 (helper)Underlying REST API client method called by the handler to perform the actual complete 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); }