Skip to main content
Glama

Optimizely DXP MCP Server

by JaxonDigital
project-resolution-fix.js10.9 kB
/** * Project Resolution Fix * Fixes critical bug where wrong project's data is accessed * Part of DXP-4: Multiple project resolution issues */ const OutputLogger = require('../output-logger'); const ResponseBuilder = require('../response-builder'); class ProjectResolutionFix { /** * Enhanced project resolution that prevents wrong project selection * NEVER silently defaults to the wrong project */ static resolveProjectSafely(args, ProjectTools) { const { project, projectName, projectId, apiKey, apiSecret, connectionString, isSelfHosted } = args; // ENHANCED DEBUG: Log all environment variables that look like project configs if (process.env.DEBUG === 'true') { console.error('[PROJECT-DEBUG] Environment variables with project format:'); const relevantEnvVars = Object.keys(process.env).filter(key => { const value = process.env[key]; return value && typeof value === 'string' && (value.includes('id=') && value.includes('key=') && value.includes('secret=')); }); console.error(`[PROJECT-DEBUG] Found ${relevantEnvVars.length} relevant env vars:`, relevantEnvVars); // Show first relevant env var specifically (e.g. ACME, FABRIKAM, etc.) if (relevantEnvVars.length > 0) { const firstEnvVar = relevantEnvVars[0]; const envValue = process.env[firstEnvVar]; console.error(`[PROJECT-DEBUG] ${firstEnvVar} env var exists, contains:`); console.error(' - id=:', envValue.includes('id=')); console.error(' - key=:', envValue.includes('key=')); console.error(' - secret=:', envValue.includes('secret=')); console.error(' - logPath=:', envValue.includes('logPath=')); } else { console.error('[PROJECT-DEBUG] No project env vars found in process.env'); } } // 1. If this is a self-hosted project with connection string, use it directly if (isSelfHosted || connectionString) { return { success: true, project: { name: projectName || args.project || 'self-hosted', connectionString: connectionString, isSelfHosted: true }, credentials: { connectionString: connectionString, isSelfHosted: true }, source: 'self_hosted' }; } // 2. If full credentials provided, use them directly if (apiKey && apiSecret && projectId) { return { success: true, credentials: { apiKey, apiSecret, projectId }, source: 'direct_credentials' }; } // 2. If project explicitly specified, use it const requestedProject = project || projectName || projectId; if (requestedProject) { const result = ProjectTools.switchProject(requestedProject); if (result.success) { OutputLogger.info(`✅ Using explicitly requested project: ${result.project.name}`); return { success: true, credentials: result.credentials, project: result.project, source: 'explicit_request' }; } return { success: false, message: `Project '${requestedProject}' not found`, suggestion: 'Available projects: ' + ProjectTools.getConfiguredProjects().map(p => p.name).join(', ') }; } // 3. Check how many projects are configured const projects = ProjectTools.getConfiguredProjects(); // ENHANCED DEBUG: Log project resolution details if (process.env.DEBUG === 'true') { console.error(`[PROJECT-DEBUG] getConfiguredProjects() returned ${projects.length} projects`); projects.forEach((p, i) => { console.error(`[PROJECT-DEBUG] Project ${i + 1}: name="${p.name}", id="${p.projectId ? p.projectId.substring(0, 8) + '...' : 'none'}", hasLogPath=${!!p.logPath}`); if (p.logPath) { console.error(`[PROJECT-DEBUG] logPath: ${p.logPath}`); } }); } if (projects.length === 0) { // CRITICAL FIX: Check for any individual environment variables that look like projects // This handles the case where ProjectTools.getConfiguredProjects() fails to parse them const envProjectNames = Object.keys(process.env).filter(key => { const value = process.env[key]; return value && typeof value === 'string' && value.includes('id=') && value.includes('key=') && value.includes('secret='); }); if (envProjectNames.length === 1) { // Found exactly one project in environment - create a minimal project object const envName = envProjectNames[0]; const envValue = process.env[envName]; // Parse the environment variable manually const params = {}; const parts = envValue.split(';').filter(p => p.trim()); parts.forEach(param => { const equalIndex = param.indexOf('='); if (equalIndex !== -1) { const key = param.substring(0, equalIndex).trim(); const value = param.substring(equalIndex + 1).trim(); if (key && value) { params[key] = value; } } }); // Create a project object directly const fallbackProject = { name: envName, projectId: params.id, apiKey: params.key, apiSecret: params.secret, logPath: params.logPath, blobPath: params.blobPath, dbPath: params.dbPath }; OutputLogger.info(`✅ Using fallback single project: ${envName}`); OutputLogger.info(`📁 Project logPath: ${params.logPath || 'not configured'}`); return { success: true, credentials: { apiKey: params.key, apiSecret: params.secret, projectId: params.id }, project: fallbackProject, source: 'fallback_single_env' }; } return { success: false, message: 'No projects configured', suggestion: 'Configure project credentials in environment variables' }; } if (projects.length === 1) { // Only one project, safe to use it const project = projects[0]; OutputLogger.info(`✅ Using only configured project: ${project.name}`); OutputLogger.info(`🔍 Project details: ID=${project.projectId?.substring(0,8)}..., logPath=${project.logPath || 'NOT SET'}`); return { success: true, credentials: { apiKey: project.apiKey, apiSecret: project.apiSecret, projectId: project.projectId }, project: project, source: 'single_project' }; } // 4. Multiple projects - REQUIRE explicit selection // This is the critical fix - we NEVER silently choose the wrong project return { success: false, requiresSelection: true, availableProjects: projects.map(p => ({ name: p.name, projectId: p.projectId ? p.projectId.substring(0, 8) + '...' : 'unknown' })), message: `Multiple projects configured. Please specify which project to use.`, suggestion: `Use --project parameter with one of: ${projects.map(p => p.name).join(', ')}` }; } /** * Show project selection menu when multiple projects exist */ static showProjectSelection(projects) { let message = '🏢 **Multiple Projects Available**\n\n'; message += 'Please specify which project to use:\n\n'; projects.forEach((project, index) => { message += `${index + 1}. **${project.name}**\n`; if (project.projectId) { message += ` ID: ${project.projectId.substring(0, 8)}...\n`; } }); message += '\n**How to specify a project:**\n'; message += '• Add `--project ProjectName` to your command\n'; message += '• Example: `download-logs --project ACME_CORP --environment Production`\n'; return ResponseBuilder.error(message); } /** * Validate that operations are using the correct project */ static validateProjectMatch(requestedProject, actualProject) { if (!requestedProject || !actualProject) { return true; // No validation needed if not specified } const requested = requestedProject.toLowerCase(); const actual = actualProject.name ? actualProject.name.toLowerCase() : ''; const actualId = actualProject.projectId ? actualProject.projectId.toLowerCase() : ''; if (actual.includes(requested) || actualId.includes(requested)) { return true; } OutputLogger.warning(`⚠️ Project mismatch detected!`); OutputLogger.warning(` Requested: ${requestedProject}`); OutputLogger.warning(` Using: ${actualProject.name || actualProject.projectId}`); return false; } /** * Add project confirmation to download operations */ static addProjectConfirmation(message, project) { if (!project) return message; const projectInfo = `\n📋 **Project**: ${project.name || 'Unknown'}`; // Insert project info at the beginning of the message if (message.includes('**')) { // Find first section and add after it const lines = message.split('\n'); lines.splice(1, 0, projectInfo); return lines.join('\n'); } return projectInfo + '\n' + message; } } module.exports = ProjectResolutionFix;

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