Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

copy_content

Copy database and blob content between Optimizely DXP environments to refresh staging/test environments with production data or promote content changes.

Instructions

📋 Copy database and/or blob content between environments. ASYNC: 30-90min depending on content size. Use for refreshing staging/test environments with production data or promoting content changes. Set includeBlob=true to copy static files/media (slower). Set includeDB=true to copy CMS/Commerce databases. CONTENT typically flows downward (Production→Preproduction→Integration). This is a heavy operation - verify target environment before starting. Required: sourceEnvironment, targetEnvironment. Returns operation ID for tracking. Use status() tool to monitor progress.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sourceEnvironmentYes
targetEnvironmentYes
projectNameNo
projectIdNo
apiKeyNo
apiSecretNo

Implementation Reference

  • Main handler for 'copy_content' tool: validates input parameters, checks self-hosted restrictions, verifies permissions for source and target environments, provides helpful access suggestions if insufficient permissions, and delegates to copyContent implementation.
    static async handleCopyContent(args: ContentCopyArgs): Promise<any> {
        // Check if this is a self-hosted project
        if (args.isSelfHosted || args.connectionString) {
            return ResponseBuilder.invalidParams('Content copy is not available for self-hosted projects. Self-hosted projects can only download existing backups and blobs.');
        }
    
        // Validate parameters
        if (!args.apiKey || !args.apiSecret || !args.projectId ||
            !args.sourceEnvironment || !args.targetEnvironment) {
            return ResponseBuilder.invalidParams('Missing required parameters for content copy');
        }
    
        // Validate that source and target are different
        if (args.sourceEnvironment === args.targetEnvironment) {
            return ResponseBuilder.invalidParams('Source and target environments must be different');
        }
    
        // Check permissions for both environments
        const PermissionChecker = require('./permission-checker');
        const projectConfig: ProjectConfig = {
            apiKey: args.apiKey,
            apiSecret: args.apiSecret,
            projectId: args.projectId,
            id: args.projectId,
            name: args.projectName || 'Project'
        };
    
        const permissions = await PermissionChecker.getOrCheckPermissionsSafe(projectConfig);
    
        // Check if user has access to both source and target
        const missingAccess: string[] = [];
        if (!permissions.accessible.includes(args.sourceEnvironment)) {
            missingAccess.push(args.sourceEnvironment);
        }
        if (!permissions.accessible.includes(args.targetEnvironment)) {
            missingAccess.push(args.targetEnvironment);
        }
    
        if (missingAccess.length > 0) {
            let response = `ℹ️ **Access Level Check**\n\n`;
            response += `Content copy requires access to both source and target environments.\n\n`;
            response += `**Requested:** ${args.sourceEnvironment} → ${args.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 alternatives based on what they have access to
            if (permissions.accessible.length >= 2) {
                response += `**Available Content Copy Options:**\n`;
    
                // Show valid copy directions based on their access
                const copyOptions = PermissionChecker.getContentCopyDefaults(permissions);
                if (copyOptions) {
                    response += `• ${copyOptions.description}: \`copy_content sourceEnvironment: "${copyOptions.source}" targetEnvironment: "${copyOptions.target}"\`\n`;
                }
    
                response += `\n💡 **Tip:** Content typically flows from higher to lower environments (Prod→Pre→Int).`;
            } else if (permissions.accessible.length === 1) {
                response += `⚠️ You need access to at least 2 environments to copy content.\n`;
                response += `Your API key only has access to ${permissions.accessible[0]}.`;
            }
    
            return ResponseBuilder.success(response);
        }
    
        try {
            const result = await this.copyContent(args);
    
            // DXP-66: Check if result is 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('Content copy error:', error);
            return ResponseBuilder.internalError('Content copy failed', error.message);
        }
    }
  • TypeScript interface defining input parameters for the copy_content tool handler.
    export interface ContentCopyArgs {
        apiKey?: string;
        apiSecret?: string;
        projectId?: string;
        projectName?: string;
        sourceEnvironment?: string;
        targetEnvironment?: string;
        isSelfHosted?: boolean;
        connectionString?: string;
        apiUrl?: string;
    }
  • Core implementation of content copy: initiates a special deployment via DXP REST API with database and blobs included (no code), handles errors, supports custom API URLs.
    static async copyContent(args: ContentCopyArgs): Promise<ContentCopyResult> {
        const { apiKey, apiSecret, projectId, sourceEnvironment, targetEnvironment } = args;
    
        console.error(`Starting content copy from ${sourceEnvironment} to ${targetEnvironment}`);
    
        // DXP-101: Use REST API instead of PowerShell (3-10x faster, no PowerShell dependency)
        // Content copy is essentially a deployment with includeBlob=true and includeDB=true
        try {
            const deploymentParams = {
                sourceEnvironment: sourceEnvironment!,
                targetEnvironment: targetEnvironment!,
                includeBlob: true,  // Include blob storage
                includeDB: true     // Include database
                // Note: No sourceApps = content only (no code deployment)
            };
    
            const options = args.apiUrl ? { apiUrl: args.apiUrl as string } : {};
            const result: DeploymentResponse = await DXPRestClient.startDeployment(
                projectId!,
                apiKey!,
                apiSecret!,
                deploymentParams as any,
                options // Support custom API URLs
            );
    
            // Format and return response
            if (result) {
                return this.formatContentCopyResponse(result, sourceEnvironment!, targetEnvironment!);
            }
    
            return {
                data: {
                    deploymentId: null,
                    sourceEnvironment: sourceEnvironment!,
                    targetEnvironment: targetEnvironment!,
                    type: 'content',
                    status: 'InProgress',
                    includesCode: false,
                    includesDatabase: true,
                    includesBlobs: true
                },
                message: ResponseBuilder.addFooter(
                    `Content copy initiated from ${sourceEnvironment} to ${targetEnvironment}.`,
                    true
                )
            };
    
        } catch (error: any) {
            // Handle REST API errors
            const errorDetails = {
                operation: 'Content Copy',
                projectId,
                sourceEnvironment,
                targetEnvironment,
                error: error.message
            };
    
            // 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'
                } as any, errorDetails) as any;
            }
    
            // Generic error handling
            throw new Error(`Content copy failed: ${error.message}`);
        }
    }
  • Output type for copy_content result, including structured data and formatted message.
    export interface ContentCopyResult {
        data: ContentCopyData;
        message: string;
    }
  • Structured data returned in the copy_content tool response.
    export interface ContentCopyData {
        deploymentId: string | null;
        sourceEnvironment: string;
        targetEnvironment: string;
        type: string;
        status: string;
        includesCode: boolean;
        includesDatabase: boolean;
        includesBlobs: boolean;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure and does so effectively. It reveals the operation is ASYNC (30-90min), describes content flow direction ('CONTENT typically flows downward'), warns it's a 'heavy operation', specifies required parameters, and explains the return value ('Returns operation ID for tracking'). It doesn't cover all possible edge cases like error handling, but provides substantial behavioral context.

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 appropriately sized and front-loaded, starting with the core purpose and key behavioral traits. Every sentence adds value: the first states the purpose and timing, the second provides usage context, the third explains parameter semantics, the fourth gives flow direction and warnings, and the fifth covers requirements and monitoring. No wasted words.

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 async operation with 6 parameters, no annotations, and no output schema, the description is largely complete. It covers purpose, timing, usage scenarios, parameter guidance, operational warnings, and monitoring instructions. The main gap is lack of explicit error handling or rollback information, but given the context, it provides sufficient guidance for effective tool 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?

With 0% schema description coverage for 6 parameters, the description compensates well by explaining the semantics of key parameters: it clarifies that 'includeBlob=true' copies static files/media and is slower, 'includeDB=true' copies CMS/Commerce databases, and 'sourceEnvironment' and 'targetEnvironment' are required. It doesn't explain 'projectName', 'projectId', 'apiKey', or 'apiSecret', but provides meaningful context for the most critical parameters.

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 ('copy database and/or blob content between environments') and distinguishes it from siblings by focusing on environment-to-environment content copying, which is unique among the listed tools that primarily handle logs, deployments, monitoring, and downloads.

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 the tool ('Use for refreshing staging/test environments with production data or promoting content changes') and includes important exclusions/warnings ('This is a heavy operation - verify target environment before starting'). It also references an alternative tool ('Use status() tool to monitor progress'), making usage context comprehensive.

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