Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

start_deployment

Initiate deployments between Optimizely DXP environments with automated monitoring. Supports code, content, or full deployments with configurable options for files and databases.

Instructions

🚀 Start new deployment from source to target environment. ASYNC: 5-30min. Initiates deployment and auto-monitors progress with real-time updates. CODE deployments flow upward (Integration→Preproduction→Production). CONTENT deployments flow downward (Production→Preproduction→Integration). Returns deploymentId immediately. Set includeBlob=true for static files, includeDB=true for database sync. When status reaches "AwaitingVerification", use get_deployment_status() to get slot URL for testing, then complete_deployment() to finalize. Required: sourceEnvironment, targetEnvironment.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sourceEnvironmentYes
targetEnvironmentYes
deploymentTypeNo
sourceAppsNo
includeBlobNo
includeDatabaseNo
directDeployNo
useMaintenancePageNo
webhookUrlNoHTTP endpoint to receive deployment events (HTTPS required in production)
webhookHeadersNoCustom headers to include in webhook requests (e.g., { "Authorization": "Bearer token" })
projectNameNo
projectIdNo
apiKeyNo
apiSecretNo

Implementation Reference

  • Primary handler for 'start_deployment' tool. Performs permissions check, validates path/type, calls DXP REST API to start deployment, formats response, emits events, registers webhooks/monitoring.
    class DeploymentActionOperations {
        /**
         * Start a new deployment
         */
        static async handleStartDeployment(args: StartDeploymentArgs): Promise<any> {
            // Check if this is a self-hosted project
            if (args.isSelfHosted || args.connectionString) {
                return ResponseBuilder.invalidParams('Deployments are not available for self-hosted projects. Self-hosted projects can only download existing backups and blobs.');
            }
    
            if (!args.apiKey || !args.apiSecret || !args.projectId) {
                return ResponseBuilder.invalidParams('Missing required parameters');
            }
    
            try {
                const result = await this.startDeployment(args);
    
                // Check if result is already a structured response with data and message
                if (result && typeof result === 'object' && 'data' in result && 'message' in result) {
                    return ResponseBuilder.successWithStructuredData(result.data, result.message);
                }
    
                // Fallback for legacy string responses
                return ResponseBuilder.success(result);
            } catch (error: any) {
                console.error('Start deployment error:', error);
                return ResponseBuilder.internalError('Failed to start deployment', error.message);
            }
        }
    
        static async startDeployment(args: StartDeploymentArgs): Promise<any> {
            const {
                apiKey, apiSecret, projectId, projectName,
                sourceEnvironment, targetEnvironment,
                deploymentType, sourceApps,
                includeBlob, includeDatabase,
                directDeploy, useMaintenancePage,
                webhookUrl, webhookHeaders
            } = args;
    
            // DXP-67: Defensive check for useMaintenancePage to prevent accidental production downtime
            if (useMaintenancePage === true) {
                console.error('⚠️  WARNING: useMaintenancePage is set to TRUE');
                console.error(`    This will show a maintenance page during deployment to ${targetEnvironment}`);
    
                if (targetEnvironment === 'Production' || targetEnvironment === 'Preproduction') {
                    console.error('    ⚠️  CAUTION: Deploying to Production/Preproduction with maintenance page!');
                    console.error('    This will cause site downtime for end users.');
                }
            }
    
            console.error(`Starting deployment from ${sourceEnvironment} to ${targetEnvironment} for project ${projectId}`);
    
            // Check permissions for both environments first
            const projectConfig: ProjectConfig = {
                apiKey: apiKey!,
                apiSecret: apiSecret!,
                projectId: projectId!,
                id: projectId!,
                name: projectName || 'Project'
            };
    
            const permissions: Permissions = await PermissionChecker.getOrCheckPermissionsSafe(projectConfig);
    
            // Check if user has access to both source and target
            const missingAccess: string[] = [];
            if (!permissions.accessible.includes(sourceEnvironment!)) {
                missingAccess.push(sourceEnvironment!);
            }
            if (!permissions.accessible.includes(targetEnvironment!)) {
                missingAccess.push(targetEnvironment!);
            }
    
            if (missingAccess.length > 0) {
                let response = `ℹ️ **Access Level Check**\n\n`;
                response += `Deployments require access to both source and target environments.\n\n`;
                response += `**Requested:** ${sourceEnvironment} → ${targetEnvironment}\n`;
                response += `**Your access level:** ${permissions.accessible.join(', ')} environment${permissions.accessible.length > 1 ? 's' : ''}\n`;
                response += `**Additional access needed:** ${missingAccess.join(', ')}\n\n`;
    
                // Suggest valid deployment paths based on what they have access to
                if (permissions.accessible.length >= 2) {
                    response += `**Available Deployment Options:**\n\n`;
    
                    // Check for valid deployment paths
                    const hasInt = permissions.accessible.includes('Integration');
                    const hasPre = permissions.accessible.includes('Preproduction');
                    const hasProd = permissions.accessible.includes('Production');
    
                    if (hasInt && hasPre) {
                        response += `• **Integration → Preproduction** (Code deployment)\n`;
                        response += `  \`start_deployment sourceEnvironment: "Integration" targetEnvironment: "Preproduction"\`\n\n`;
                    }
    
                    if (hasPre && hasProd) {
                        response += `• **Preproduction → Production** (Code deployment)\n`;
                        response += `  \`start_deployment sourceEnvironment: "Preproduction" targetEnvironment: "Production"\`\n\n`;
                    }
    
                    // For content copy (if they have the environments but trying wrong direction)
                    if (hasProd && hasPre) {
                        response += `• **Production → Preproduction** (Content copy - use copy_content instead)\n`;
                        response += `  \`copy_content sourceEnvironment: "Production" targetEnvironment: "Preproduction"\`\n\n`;
                    }
    
                    if (hasProd && hasInt) {
                        response += `• **Production → Integration** (Content copy - use copy_content instead)\n`;
                        response += `  \`copy_content sourceEnvironment: "Production" targetEnvironment: "Integration"\`\n\n`;
                    }
    
                    response += `\n💡 **Important:** Code deployments only work upward (Int→Pre→Prod).\n`;
                    response += `For downward content sync, use the \`copy_content\` tool instead.`;
                } else if (permissions.accessible.length === 1) {
                    response += `⚠️ You need access to at least 2 environments for deployments.\n`;
                    response += `Your API key only has access to ${permissions.accessible[0]}.`;
                }
    
                // Return as structured response
                return {
                    data: {
                        error: 'insufficient_permissions',
                        missingAccess: missingAccess,
                        availableEnvironments: permissions.accessible
                    },
                    message: response
                };
            }
    
            // Check for any running deployments before starting a new one
            console.error('Checking for active deployments...');
            try {
                const DeploymentListOperations = require('./deployment-list');
                const listResult = await DeploymentListOperations.listDeployments({
                    apiKey,
                    apiSecret,
                    projectId,
                    limit: 5,
                    offset: 0
                });
    
                // DEBUG: Log the actual result to troubleshoot blocking issue
                console.error('DEBUG: listResult type:', typeof listResult);
                console.error('DEBUG: listResult (first 500 chars):',
                    typeof listResult === 'string' ? listResult.substring(0, 500) : JSON.stringify(listResult).substring(0, 500));
    
                // Check if there's a deployment in progress
                // CRITICAL FIX (v3.17.2): First check if this is an error response, not deployment data
                // Previously, error messages containing "InProgress" text would falsely trigger
                // the "deployment already in progress" check, preventing all new deployments
                if (listResult && typeof listResult === 'string') {
                    // Skip check if this is an error message
                    const isError = listResult.includes('❌') ||
                                   listResult.includes('Error') ||
                                   listResult.includes('Failed') ||
                                   listResult.includes('Invalid') ||
                                   listResult.includes('Forbidden') ||
                                   listResult.includes('support@jaxondigital.com');
    
                    console.error('DEBUG: isError check result:', isError);
    
                    if (isError) {
                        // Log error but continue - we can't check deployment status but shouldn't block
                        console.error('DEBUG: Detected error in deployment list, continuing with deployment...');
                        console.error('Warning: Could not check for active deployments due to error:', listResult.substring(0, 200));
                    } else {
                        // Only check for in-progress deployments if this is actual deployment data
                        console.error('DEBUG: Checking for in-progress deployments in actual deployment data...');
    
                        // FIXED: Don't use 🔄 emoji as it appears in all deployments (even completed ones)
                        // Only look for actual status indicators of in-progress deployments
                        const hasInProgress = listResult.includes('InProgress') ||
                                             listResult.includes('Deploying') ||
                                             listResult.includes('Status: **InProgress**') ||
                                             listResult.includes('Status: **Deploying**');
    
                        console.error('DEBUG: hasInProgress result:', hasInProgress);
                        console.error('DEBUG: Contains InProgress:', listResult.includes('InProgress'));
                        console.error('DEBUG: Contains Deploying:', listResult.includes('Deploying'));
                        console.error('DEBUG: Contains Status: **InProgress**:', listResult.includes('Status: **InProgress**'));
                        console.error('DEBUG: Contains Status: **Deploying**:', listResult.includes('Status: **Deploying**'));
    
                        if (hasInProgress) {
                            console.error('DEBUG: Found in-progress deployment, blocking new deployment...');
                        // Extract details about the in-progress deployment if possible
                        const lines = listResult.split('\n');
                        let inProgressDetails = '';
    
                        for (let i = 0; i < lines.length; i++) {
                            if (lines[i].includes('InProgress') || lines[i].includes('🔄')) {
                                // Try to get the deployment ID and environments
                                const deploymentIdMatch = lines[i].match(/#([a-f0-9-]+)/);
                                if (deploymentIdMatch) {
                                    inProgressDetails = `Deployment ${deploymentIdMatch[1]} `;
                                }
    
                                // Look for environment info in nearby lines
                                for (let j = Math.max(0, i-2); j < Math.min(lines.length, i+3); j++) {
                                    if (lines[j].includes('→')) {
                                        const envMatch = lines[j].match(/(\w+)\s*→\s*(\w+)/);
                                        if (envMatch) {
                                            inProgressDetails += `(${envMatch[1]} → ${envMatch[2]})`;
                                            break;
                                        }
                                    }
                                }
                                break;
                            }
                        }
    
                        return `⚠️ **Deployment Already In Progress**\n\n` +
                               `Cannot start a new deployment while another is running.\n\n` +
                               (inProgressDetails ? `**Active Deployment:** ${inProgressDetails}\n\n` : '') +
                               `Please wait for the current deployment to complete or reset it before starting a new one.\n\n` +
                               `**Options:**\n` +
                               `• Use \`get_deployment_status\` to check progress\n` +
                               `• Use \`reset_deployment\` if the deployment is stuck\n` +
                               `• Wait for automatic completion (usually 10-30 minutes)`;
                        }
                    }
                }
            } catch (checkError: any) {
                // Don't fail the deployment if we can't check status
                console.error('Warning: Could not check for active deployments:', checkError.message);
            }
    
            // Validate deployment path
            const pathValidation: PathValidation = DeploymentValidator.validateDeploymentPath(sourceEnvironment!, targetEnvironment!);
            if (!pathValidation.valid) {
                // Check if this is a downward deployment that should use content copy
                if (sourceEnvironment === 'Production' ||
                    (sourceEnvironment === 'Preproduction' && targetEnvironment === 'Integration')) {
    
                    let response = `ℹ️ **Invalid Deployment Direction**\n\n`;
                    response += `You're trying to deploy from **${sourceEnvironment}** to **${targetEnvironment}**.\n\n`;
                    response += `❌ **Code deployments can only go upward:**\n`;
                    response += `• Integration → Preproduction\n`;
                    response += `• Preproduction → Production\n\n`;
                    response += `✅ **For downward content synchronization, use:**\n`;
                    response += `\`copy_content sourceEnvironment: "${sourceEnvironment}" targetEnvironment: "${targetEnvironment}"\`\n\n`;
                    response += `💡 **Why?** Code changes should flow through proper testing stages (Int→Pre→Prod),\n`;
                    response += `while content can be copied from production back to lower environments for testing.`;
    
                    // Return as structured response
                    return {
                        data: {
                            error: 'invalid_deployment_direction',
                            sourceEnvironment: sourceEnvironment,
                            targetEnvironment: targetEnvironment,
                            suggestion: 'use_copy_content'
                        },
                        message: response
                    };
                }
    
                // For other invalid paths (like Int→Prod), show the standard error
                return ResponseBuilder.error(
                    `❌ Invalid Deployment Path\n\n${pathValidation.error}\n\n💡 ${pathValidation.suggestion}`
                );
            }
    
            // Show warnings if any
            if (pathValidation.warnings && pathValidation.warnings.length > 0) {
                let warningMsg = '⚠️  **Deployment Warnings:**\n\n';
                pathValidation.warnings.forEach(warn => {
                    warningMsg += `${warn.message}\n`;
                    if (warn.suggestion) {
                        warningMsg += `   💡 ${warn.suggestion}\n`;
                    }
                    warningMsg += '\n';
                });
                console.error(warningMsg);
            }
    
            // Validate deployment parameters
            const paramValidation: ParamValidation = DeploymentValidator.validateDeploymentParams(args);
            if (!paramValidation.valid) {
                return ResponseBuilder.error(
                    `❌ Invalid Parameters\n\n${paramValidation.errors!.join('\n')}`
                );
            }
    
            // Use sanitized parameters
            const sanitizedArgs = paramValidation.sanitized;
    
            // Check deployment timing
            const timingCheck: TimingCheck = DeploymentValidator.validateDeploymentTiming({
                targetEnvironment
            });
            if (timingCheck.warnings && timingCheck.warnings.length > 0) {
                timingCheck.warnings.forEach(warn => {
                    console.error(`Timing warning: ${warn.message}`);
                });
            }
    
            // Determine if this is upward (code) or downward (content) deployment
            const isUpward = pathValidation.isUpward;
    
            // Apply smart defaults based on deployment direction
            let deployCode = false;
            let deployContent = false;
    
            if (sanitizedArgs.deploymentType) {
                // User specified deployment type
                if (sanitizedArgs.deploymentType === 'code') {
                    deployCode = true;
                } else if (sanitizedArgs.deploymentType === 'content') {
                    deployContent = true;
                } else if (sanitizedArgs.deploymentType === 'all') {
                    deployCode = true;
                    deployContent = true;
                }
            } else {
                // Apply smart defaults
                if (isUpward) {
                    deployCode = true; // Code flows up
                    console.error('Defaulting to CODE deployment (upward flow)');
                } else {
                    deployContent = true; // Content flows down
                    console.error('Defaulting to CONTENT deployment (downward flow)');
                }
            }
    
            // DXP-101: Build deployment parameters for REST API (replacing PowerShell)
            // Based on EpiCloud.psm1 Start-EpiDeployment SourceEnvironment parameter set
            const deploymentParams: DeploymentParams = {
                sourceEnvironment: sourceEnvironment!,  // Lowercase per PowerShell source
                targetEnvironment: targetEnvironment!   // Lowercase per PowerShell source
            };
    
            // Add deployment type parameters
            if (deployCode) {
                // SourceApp is required for code deployments
                const appsToUse = sourceApps && sourceApps.length > 0
                    ? sourceApps
                    : ['cms']; // Default to CMS app
                deploymentParams.sourceApps = appsToUse;  // Plural 'sourceApps' per PowerShell source
    
                console.error(`Deploying code with apps: ${appsToUse.join(', ')}`);
            }
    
            if (deployContent) {
                // Add content deployment flags - always include (per PowerShell source)
                deploymentParams.includeBlob = includeBlob !== false;
                deploymentParams.includeDB = includeDatabase !== false;
    
                console.error(`Deploying content with includeBlob=${deploymentParams.includeBlob}, includeDB=${deploymentParams.includeDB}`);
            }
    
            // Add optional parameters - only if explicitly set to true
            if (directDeploy === true) {
                deploymentParams.directDeploy = true;  // Lowercase per PowerShell source
            }
            if (useMaintenancePage === true) {
                deploymentParams.maintenancePage = true;  // 'maintenancePage' not 'UseMaintenancePage' per PowerShell source
            }
    
            console.error(`Starting deployment via REST API with payload:`);
            console.error(JSON.stringify(deploymentParams, null, 2));
    
            // DXP-101: Use REST API instead of PowerShell (3-10x faster, no PowerShell dependency)
            try {
                const result: DeploymentResult = await DXPRestClient.startDeployment(
                    projectId!,
                    apiKey!,
                    apiSecret!,
                    deploymentParams as any,
                    { apiUrl: args.apiUrl } // Support custom API URLs
                );
    
                // Format response
                if (result) {
                    const formatted = DeploymentFormatters.formatDeploymentStarted(result, args);
    
                    // Extract deployment ID from result and start monitoring
                    if (result.id) {
                        // DXP-136: Emit deployment started event
                        try {
                            DeploymentResourceHandler.emitStarted(result.id, {
                                project: projectName,
                                environment: targetEnvironment,
                                sourceEnvironment: sourceEnvironment,
                                targetEnvironment: targetEnvironment,
                                deploymentType: deploymentType,
                                status: result.status
                            });
                        } catch (eventError: any) {
                            console.error(`Failed to emit deployment event: ${eventError.message}`);
                            // Don't fail the deployment if event emission fails
                        }
    
                        // DXP-140: Register webhook if provided
                        if (webhookUrl) {
                            try {
                                const webhookManager = getGlobalWebhookManager();
                                const registrationResult = webhookManager.register(
                                    result.id, // operationId (deploymentId)
                                    webhookUrl,
                                    {
                                        headers: webhookHeaders || {},
                                        project: projectName,
                                        environment: targetEnvironment
                                    }
                                );
    
                                const logger = new StructuredLogger({
                                    context: {
                                        tool: 'start_deployment',
                                        deployment_id: result.id,
                                        project: projectName,
                                        environment: targetEnvironment
                                    }
                                });
    
                                if (registrationResult.success) {
                                    logger.info('Webhook registered for deployment', {
                                        webhook_url: webhookUrl,
                                        deployment_id: result.id
                                    });
                                    console.log(`🔔 Webhook registered for deployment ${result.id}`);
                                } else {
                                    logger.warn('Webhook registration failed', {
                                        error: registrationResult.error,
                                        deployment_id: result.id
                                    });
                                    console.log(`⚠️ Webhook registration failed: ${registrationResult.error}`);
                                }
                            } catch (webhookError: any) {
                                console.error(`Failed to register webhook: ${webhookError.message}`);
                                // Don't fail the deployment if webhook registration fails
                            }
                        }
    
                        try {
                            const monitor = getGlobalMonitor();
                            monitor.startMonitoring({
                                deploymentId: result.id,
                                projectId: args.projectId!,
                                apiKey: args.apiKey!,
                                apiSecret: args.apiSecret!,
                                interval: 60 * 1000 // 1 minute default
                            });
    
                            const logger = new StructuredLogger({
                                context: {
                                    tool: 'start_deployment',
                                    deployment_id: result.id
                                }
                            });
                            logger.info('Auto-monitoring started for deployment', {
                                deployment_id: result.id,
                                interval_ms: 60 * 1000
                            });
                            console.log(`🔄 Auto-monitoring started for deployment ${result.id}`);
                        } catch (monitorError: any) {
                            console.error(`Failed to start monitoring: ${monitorError.message}`);
                            // Don't fail the deployment if monitoring fails
                        }
                    }
    
                    // Return structured response with both data and message
                    return formatted;
                }
    
                return { data: null, message: ResponseBuilder.addFooter('Deployment started but no details available') };
    
            } catch (error: any) {
                // Handle REST API errors
                const errorDetails = {
                    operation: 'Start Deployment',
                    projectId,
                    projectName: args.projectName,
                    sourceEnvironment,
                    targetEnvironment,
                    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);
            }
        }
  • Input schema/arguments type for start_deployment tool.
    interface StartDeploymentArgs {
        apiKey?: string;
        apiSecret?: string;
        projectId?: string;
        projectName?: string;
        sourceEnvironment?: string;
        targetEnvironment?: string;
        deploymentType?: string;
        sourceApps?: string[];
        includeBlob?: boolean;
        includeDatabase?: boolean;
        directDeploy?: boolean;
        useMaintenancePage?: boolean;
        isSelfHosted?: boolean;
        connectionString?: string;
        apiUrl?: string;
        webhookUrl?: string;
        webhookHeaders?: Record<string, string>;
    }
  • Public DeploymentTools.handleStartDeployment - entrypoint delegated to action operations.
    static async handleStartDeployment(args: any): Promise<any> {
        return DeploymentActionOperations.handleStartDeployment(args);
  • Helper formatter for deployment started response used by handler.
    static formatDeploymentStarted(deployment: Deployment, args: StartDeploymentArgs): StructuredResult {
        const { FORMATTING: { STATUS_ICONS } } = Config;
    
        // Get project info from args, configured projects, or environment variables
        let projectId = args.projectId;
        let projectName = args.projectName;
    
        if (!projectId || !projectName) {
            try {
                const ProjectTools = require('../project-tools');
                const projects = ProjectTools.getConfiguredProjects();
                if (projects && projects.length > 0) {
                    const defaultProject = projects.find((p: any) => p.isDefault) || projects[0];
                    projectId = projectId || defaultProject.id;
                    projectName = projectName || defaultProject.name;
                }
            } catch (error) {
                // Fall back to environment variables if ProjectTools fails
                projectId = projectId || process.env.OPTIMIZELY_PROJECT_ID;
                projectName = projectName || process.env.OPTIMIZELY_PROJECT_NAME;
            }
        }
    
        // Determine deployment type
        let deploymentType = args.deploymentType;
        if (!deploymentType) {
            // Apply smart defaults
            const isUpward = this.isUpwardDeployment(args.sourceEnvironment, args.targetEnvironment);
            deploymentType = isUpward ? 'code' : 'content';
        }
    
        const needsVerification = args.targetEnvironment === 'Production' && !args.directDeploy;
        const previewUrl = needsVerification ? this.getPreviewUrl(args.targetEnvironment!, projectId || null) : null;
    
        // Build structured data for automation tools
        const structuredData = {
            deploymentId: deployment.id,
            status: deployment.status || 'InProgress',
            sourceEnvironment: args.sourceEnvironment,
            targetEnvironment: args.targetEnvironment,
            deploymentType: deploymentType,
            projectId: projectId,
            projectName: projectName,
            startTime: deployment.startTime || new Date().toISOString(),
            percentComplete: deployment.percentComplete || 0,
            needsVerification: needsVerification,
            previewUrl: previewUrl,
            sourceApps: args.sourceApps || [],
            includeBlob: args.includeBlob,
            includeDatabase: args.includeDatabase,
            directDeploy: args.directDeploy,
            useMaintenancePage: args.useMaintenancePage
        };
    
        // Build human-readable message
        let message = `${STATUS_ICONS.SUCCESS} **Deployment Started`;
        if (projectName) {
            message += ` - ${projectName}**\n\n`;
        } else {
            message += `**\n\n`;
        }
    
        message += `**Deployment ID**: ${deployment.id}\n`;
        message += `**From**: ${args.sourceEnvironment}\n`;
        message += `**To**: ${args.targetEnvironment}\n`;
        message += `**Type**: ${deploymentType!.charAt(0).toUpperCase() + deploymentType!.slice(1)}`;
    
        if (deploymentType === 'code' && args.sourceApps) {
            message += ` (${args.sourceApps.join(', ')})`;
        }
        message += '\n';
    
        if (deployment.status) {
            message += `**Status**: ${deployment.status}\n`;
        }
    
        // Always show preview URL for deployments that will need verification
        if (needsVerification) {
            if (previewUrl) {
                message += `\n**🔗 Preview URL (Slot)**: ${previewUrl}\n`;
                message += `_Your deployment will be available for preview at this slot URL once it enters verification state_\n`;
            } else {
                message += `\n**🔗 Verification URL Information**:\n`;
                message += `• When the deployment reaches verification state, the slot URL will be available in the DXP portal\n`;
                message += `• Expected format: https://[your-site-name]-slot.dxcloud.episerver.net/\n`;
                message += `• You'll need to check the DXP portal deployment details for the exact URL\n`;
            }
        }
    
        message += '\n## 🎯 **Monitoring Options**:\n';
        message += `### Option 1: **Continuous Monitoring** (Recommended)\n`;
        message += `Use \`monitor_deployment\` to automatically check progress every 30 seconds:\n`;
        message += `\`\`\`\nmonitor_deployment({ deploymentId: "${deployment.id}" })\n\`\`\`\n`;
    
        message += `### Option 2: **Manual Status Checks**\n`;
        message += `Use \`get_deployment_status\` to check progress on demand:\n`;
        message += `\`\`\`\nget_deployment_status({ deploymentId: "${deployment.id}" })\n\`\`\`\n`;
    
        message += '\n**💡 Important**: Use the MCP monitoring tools above instead of bash loops.\n';
        message += 'The \`monitor_deployment\` tool provides intelligent progress tracking and automatic notifications.\n';
    
        if (needsVerification) {
            message += '\n**📋 Deployment Stages**:\n';
            message += '1. **In Progress** - Deployment is running\n';
            message += '2. **Verification** - Review changes at preview URL\n';
            message += '3. **Complete** - After you run `complete_deployment`\n';
        }
    
        message = ResponseBuilder.addFooter(message);
    
        // Return both structured data and message
        return { data: structuredData, message: message };
    }
  • Tool listed in no-cache operations and invalidation map, indicating registration awareness.
    'start_deployment',
    'complete_deployment',

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