Skip to main content
Glama

Optimizely DXP MCP Server

by JaxonDigital
smart-executor.js10.1 kB
/** * Smart Executor Module * Provides intelligent command execution with automatic retry, caching, and error handling * Part of Jaxon Digital Optimizely DXP MCP Server */ const PowerShellHelper = require('./powershell-helper'); const RetryHelper = require('./retry-helper'); const ErrorHandler = require('./error-handler'); class SmartExecutor { /** * Execute an EpiCloud command with all enterprise features * Includes: retry logic, caching, rate limiting, enhanced errors * * @param {string} command - The EpiCloud command to execute * @param {Object} credentials - API credentials {apiKey, apiSecret, projectId} * @param {Object} options - Execution options * @returns {Promise<Object>} Enhanced result object */ static async execute(command, credentials, options = {}) { const { operation = 'api_call', useCache = true, useRetry = true, maxAttempts = 3, parseJson = true, timeout = 120000, cacheArgs = {}, verbose = false } = options; // Build context for error messages const context = { operation: operation, projectId: credentials.projectId, projectName: credentials.projectName }; try { // Determine if we should use caching const shouldCache = useCache && !PowerShellHelper.isWriteOperation(operation); // Create the execution function const executeFn = async () => { // Try with cache if applicable if (shouldCache) { const result = await PowerShellHelper.executeEpiCommandDirectWithCredentials( command, credentials, { timeout, parseJson, operation, cacheArgs } ); if (result.fromCache && verbose) { console.error(`📦 Using cached result (age: ${Math.round(result.cacheAge / 1000)}s)`); } return result; } else { // Write operations use invalidation return await PowerShellHelper.executeEpiCommandWithInvalidation( command, credentials, { timeout, parseJson, operation, cacheArgs } ); } }; // Execute with or without retry let result; if (useRetry) { result = await RetryHelper.retryPowerShell( executeFn, context, { maxAttempts, verbose, initialDelay: 2000, maxDelay: 30000, backoffMultiplier: 2, jitter: true } ); } else { result = await executeFn(); } // Enhance successful result if (result.success) { return { ...result, operation, projectId: credentials.projectId, projectName: credentials.projectName, timestamp: new Date().toISOString() }; } // Handle failures with enhanced error messages const error = ErrorHandler.detectError(result.stderr, context); if (error) { result.formattedError = ErrorHandler.formatError(error, context); result.errorType = error.type; result.suggestion = error.suggestion; } return result; } catch (error) { // Handle exceptions with comprehensive error information const detectedError = ErrorHandler.detectError( error.message || error.stderr || error.toString(), context ); if (detectedError) { return { success: false, error: error.message, stderr: error.stderr || '', formattedError: ErrorHandler.formatError(detectedError, context), errorType: detectedError.type, suggestion: detectedError.suggestion, operation, projectId: credentials.projectId, timestamp: new Date().toISOString() }; } // Fallback for unknown errors return { success: false, error: error.message || error.toString(), stderr: error.stderr || '', operation, projectId: credentials.projectId, timestamp: new Date().toISOString() }; } } /** * Execute a batch of commands with optimized handling * @param {Array<Object>} commands - Array of {command, credentials, options} * @param {Object} batchOptions - Batch execution options * @returns {Promise<Array<Object>>} Array of results */ static async executeBatch(commands, batchOptions = {}) { const { parallel = true, maxConcurrency = 3, stopOnError = false, verbose = false } = batchOptions; if (parallel) { // Execute in parallel with concurrency limit const results = []; const executing = []; for (const cmd of commands) { const promise = this.execute(cmd.command, cmd.credentials, cmd.options) .then(result => { if (verbose) { const status = result.success ? '✅' : '❌'; console.error(`${status} ${cmd.options?.operation || 'Command'} completed`); } return result; }); executing.push(promise); if (executing.length >= maxConcurrency) { const result = await Promise.race(executing); results.push(result); executing.splice(executing.indexOf(promise), 1); if (stopOnError && !result.success) { throw new Error(`Batch execution stopped: ${result.error || result.stderr}`); } } } // Wait for remaining operations const remaining = await Promise.all(executing); results.push(...remaining); return results; } else { // Execute sequentially const results = []; for (const cmd of commands) { if (verbose) { console.error(`⏳ Executing ${cmd.options?.operation || 'command'}...`); } const result = await this.execute(cmd.command, cmd.credentials, cmd.options); results.push(result); if (stopOnError && !result.success) { throw new Error(`Batch execution stopped: ${result.error || result.stderr}`); } } return results; } } /** * Execute with streaming progress updates * @param {string} command - The EpiCloud command to execute * @param {Object} credentials - API credentials * @param {Object} options - Execution options including onProgress callback * @returns {Promise<Object>} Result object */ static async executeWithProgress(command, credentials, options = {}) { const { onProgress, ...execOptions } = options; // If no progress callback, use regular execution if (!onProgress) { return this.execute(command, credentials, execOptions); } try { // Use streaming execution from PowerShellHelper const result = await PowerShellHelper.executeEpiCommandStreaming( command, credentials, { ...execOptions, onProgress } ); // Enhance the result return { ...result, operation: execOptions.operation, projectId: credentials.projectId, projectName: credentials.projectName, timestamp: new Date().toISOString() }; } catch (error) { const context = { operation: execOptions.operation, projectId: credentials.projectId, projectName: credentials.projectName }; const detectedError = ErrorHandler.detectError(error.message, context); if (detectedError) { error.formattedError = ErrorHandler.formatError(detectedError, context); } throw error; } } /** * Get cache statistics for monitoring * @returns {Object} Cache statistics */ static getCacheStats() { return PowerShellHelper.getCacheStats(); } /** * Clear cache for a project or all projects * @param {string} projectId - Optional project ID */ static clearCache(projectId) { PowerShellHelper.clearCache(projectId); } } module.exports = SmartExecutor;

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