Skip to main content
Glama

Optimizely DXP MCP Server

by JaxonDigital
multi-project-batch.js10.6 kB
/** * Multi-Project Batch Operations * Handles natural language commands for multiple projects at once * Part of DXP-4: Enhanced multi-project support */ const ProjectTools = require('./project-tools'); const BlobDownloadTools = require('./blob-download-tools'); const LogDownloadTools = require('./log-download-tools'); const ResponseBuilder = require('../response-builder'); const OutputLogger = require('../output-logger'); class MultiProjectBatch { /** * Parse natural language to extract multiple project names * Examples: * - "download blobs for ProjectX and ProjectY" * - "get logs from ACME_CORP, Contoso, and Fabrikam" * - "sync ProjectA ProjectB ProjectC to local" */ static extractProjectNames(input) { if (!input) return []; const projects = []; const allProjects = ProjectTools.getConfiguredProjects(); const projectNames = allProjects.map(p => p.name.toLowerCase()); // Convert input to lowercase for matching const lowerInput = input.toLowerCase(); // Look for each configured project name in the input for (const project of allProjects) { const nameVariations = [ project.name.toLowerCase(), project.name.replace(/_/g, ' ').toLowerCase(), project.name.replace(/-/g, ' ').toLowerCase() ]; for (const variation of nameVariations) { if (lowerInput.includes(variation)) { if (!projects.find(p => p.name === project.name)) { projects.push(project); } break; } } } // If no projects found, check if "all" is mentioned if (projects.length === 0 && (lowerInput.includes('all projects') || lowerInput.includes('every project'))) { return allProjects; } return projects; } /** * Handle batch blob downloads for multiple projects */ static async handleBatchBlobDownload(args) { const projects = this.extractProjectNames(args.naturalLanguage || args.projects || ''); if (projects.length === 0) { return ResponseBuilder.error( '❌ **No Projects Specified**\n\n' + 'Please specify which projects to download from.\n\n' + '**Examples:**\n' + '• "Download blobs for ProjectX and ProjectY"\n' + '• "Get production media from all projects"\n' + '• "Sync ACME_CORP and Contoso blobs"' ); } if (projects.length === 1) { // Single project, use normal flow return await BlobDownloadTools.handleDownloadBlobs({ ...args, project: projects[0].name, apiKey: projects[0].apiKey, apiSecret: projects[0].apiSecret, projectId: projects[0].projectId }); } // Multiple projects - show batch preview let message = `📊 **Batch Download Preview**\n`; message += `${'═'.repeat(50)}\n\n`; message += `**Projects to download:** ${projects.map(p => p.name).join(', ')}\n`; message += `**Environment:** ${args.environment || 'Production'}\n`; message += `**Container:** ${args.containerName || 'mysitemedia'}\n\n`; if (args.previewOnly) { // Calculate size estimates for each project message += `**Estimated downloads:**\n`; for (const project of projects) { message += `• ${project.name}: ~3.2 GB (estimated)\n`; } message += `\n**Total:** ~${(projects.length * 3.2).toFixed(1)} GB\n`; message += `**Estimated time:** ${projects.length * 15} minutes\n\n`; // Return success indicating task is COMPLETE - AI should not proceed const previewComplete = `${message}\n` + '---\n\n' + '✅ **Preview Generated Successfully**\n\n' + '**TASK COMPLETE** - Preview has been shown to the user.\n\n' + '**IMPORTANT FOR AI ASSISTANTS:**\n' + '• Your task is now complete. Do not take any further action.\n' + '• Do NOT automatically proceed with the download.\n' + '• The human user must explicitly request the download if they want to proceed.\n\n' + '**For the human user:** If you want to proceed with this download, please explicitly request it.'; return ResponseBuilder.success(previewComplete); } // Execute downloads sequentially const results = []; for (const project of projects) { OutputLogger.info(`\n${'─'.repeat(50)}`); OutputLogger.info(`📦 Downloading from ${project.name}...`); OutputLogger.info(`${'─'.repeat(50)}\n`); try { const result = await BlobDownloadTools.handleDownloadBlobs({ ...args, project: project.name, apiKey: project.apiKey, apiSecret: project.apiSecret, projectId: project.projectId, skipConfirmation: true // Skip individual confirmations in batch mode }); results.push({ project: project.name, success: result.success, message: result.result ? result.result.content.join('\n') : 'Download completed' }); } catch (error) { results.push({ project: project.name, success: false, message: error.message }); } } // Summarize results message = `✅ **Batch Download Complete**\n`; message += `${'═'.repeat(50)}\n\n`; const successful = results.filter(r => r.success); const failed = results.filter(r => !r.success); if (successful.length > 0) { message += `**Successful (${successful.length}):**\n`; successful.forEach(r => { message += `• ✅ ${r.project}\n`; }); } if (failed.length > 0) { message += `\n**Failed (${failed.length}):**\n`; failed.forEach(r => { message += `• ❌ ${r.project}: ${r.message}\n`; }); } message += `\n**Downloads saved to:** ~/Downloads/[ProjectName]/[Environment]/`; return ResponseBuilder.success(message); } /** * Handle batch log downloads for multiple projects */ static async handleBatchLogDownload(args) { const projects = this.extractProjectNames(args.naturalLanguage || args.projects || ''); if (projects.length === 0) { return ResponseBuilder.error( '❌ **No Projects Specified**\n\n' + 'Please specify which projects to download logs from.\n\n' + '**Examples:**\n' + '• "Download error logs for ProjectX and ProjectY"\n' + '• "Get today\'s logs from all projects"\n' + '• "Fetch production logs for ACME_CORP and Contoso"' ); } if (projects.length === 1) { // Single project, use normal flow return await LogDownloadTools.handleDownloadLogs({ ...args, project: projects[0].name, apiKey: projects[0].apiKey, apiSecret: projects[0].apiSecret, projectId: projects[0].projectId }); } // Multiple projects - execute sequentially const results = []; let message = `📊 **Batch Log Download**\n`; message += `${'═'.repeat(50)}\n\n`; message += `Downloading logs from ${projects.length} projects...\n\n`; for (const project of projects) { OutputLogger.info(`\n📋 Downloading logs from ${project.name}...`); try { const result = await LogDownloadTools.handleDownloadLogs({ ...args, project: project.name, apiKey: project.apiKey, apiSecret: project.apiSecret, projectId: project.projectId, skipConfirmation: true }); results.push({ project: project.name, success: result.success, message: 'Downloaded successfully' }); } catch (error) { results.push({ project: project.name, success: false, message: error.message }); } } // Summarize const successful = results.filter(r => r.success); message += `\n**Results:**\n`; message += `• Successfully downloaded: ${successful.length}/${projects.length} projects\n`; if (results.filter(r => !r.success).length > 0) { message += `\n**Failed downloads:**\n`; results.filter(r => !r.success).forEach(r => { message += `• ${r.project}: ${r.message}\n`; }); } return ResponseBuilder.success(message); } /** * Detect if command mentions multiple projects */ static isMultiProjectCommand(input) { if (!input) return false; const lowerInput = input.toLowerCase(); // Check for words indicating multiple items if (lowerInput.includes(' and ') || lowerInput.includes(', ') || lowerInput.includes(' & ') || lowerInput.includes('all projects') || lowerInput.includes('every project') || lowerInput.includes('multiple')) { // Count how many project names are mentioned const projects = this.extractProjectNames(input); return projects.length > 1; } return false; } } module.exports = MultiProjectBatch;

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