Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

get_project

Retrieve detailed configuration for Optimizely DXP projects including environments, hosting type, and credential status to inspect settings or troubleshoot issues.

Instructions

šŸ“‹ Get detailed project configuration. REAL-TIME: <1s. Returns project ID, name, configured environments, hosting type (DXP/self-hosted), and credential validation status. Use to inspect project settings or troubleshoot configuration issues. Required: projectName. Returns full project config object.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectNameNo
projectIdNo

Implementation Reference

  • Handler function for the 'get_project' tool. Resolves a specific project by name or ID from configured projects, handles defaults, and returns formatted project information including type, credentials status, and configuration details.
    static getProject(args: { projectName?: string; projectId?: string }): any {
        const { projectName, projectId } = args;
        const projects = this.getConfiguredProjects();
    
        // Find the requested project
        let project: ProjectConfig | undefined;
        if (projectName || projectId) {
            project = projects.find(p =>
                p.name === projectName ||
                p.name?.toLowerCase() === projectName?.toLowerCase() ||
                p.projectId === projectId
            );
        } else {
            // Get default/current project
            project = this.getCurrentProject() || undefined;
        }
    
        if (!project) {
            const availableNames = projects.map(p => p.name).join(', ');
            return ResponseBuilder.formatResponse({
                success: false,
                message: `Project '${projectName || projectId}' not found`,
                details: availableNames ? `Available projects: ${availableNames}` : 'No projects configured'
            });
        }
    
        return this.formatProjectInfo(project, projects.length);
    }
  • Core helper method that discovers and parses all configured projects from environment variables and dynamic in-memory configs. Handles DXP PaaS, self-hosted, and unknown projects.
    static getConfiguredProjects(): ProjectConfig[] {
        // Dynamic configurations are kept in memory only for current session
    
        const projects: ProjectConfig[] = [];
        const configErrors: ConfigError[] = [];
    
        // Check ALL environment variables for our specific format
        // Any env var with format: "id=uuid;key=value;secret=value" is treated as an API key configuration
        // Examples:
        //   ACME="id=uuid;key=value;secret=value"
        //   PRODUCTION="id=uuid;key=value;secret=value"
        //   CLIENT_A_STAGING="id=uuid;key=value;secret=value"
    
        // DEBUG: Log all environment variables that contain our 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=')) ||
                    value.startsWith('DefaultEndpointsProtocol=') ||
                    ((value.includes('blobPath=') || value.includes('logPath=')) &&
                     !value.includes('id=') && !value.includes('key=') && !value.includes('secret=')));
        });
        OutputLogger.debug(`Checking environment variables...`);
        OutputLogger.debug(`Found ${relevantEnvVars.length} relevant env vars (DXP, self-hosted, and unknown):`, relevantEnvVars);
    
        Object.keys(process.env).forEach(key => {
            const value = process.env[key];
    
            // Skip if not a string
            if (typeof value !== 'string') {
                return;
            }
    
            // Check if empty string (placeholder project)
            // Only treat as project if name looks like a project name
            if (value === '') {
                // Skip if this doesn't look like a project name
                // Project names typically follow patterns like:
                // - "ACME-int", "CONTOSO-prod", "FABRIKAM-staging" (project-environment)
                // - "DEMO-self", "TEST-local" (project-type)
                // - Single short words like "zilch", "test", "demo"
    
                const hasProjectPattern =
                    // Pattern: WORD-env (like ACME-int, CONTOSO-prod)
                    key.match(/^[A-Z0-9]+-(?:int|prod|staging|test|dev|qa|uat|demo|local|self)$/i) ||
                    // Pattern: Short single word (max 10 chars)
                    (key.match(/^[A-Z0-9]+$/i) && key.length <= 10) ||
                    // Explicitly starts with common project prefixes
                    key.match(/^(?:TEST|DEMO|DEV|PROD|STAGING|QA)[-_]/i);
    
                if (!hasProjectPattern) {
                    return;
                }
    
                // Create Unknown placeholder project
                const projectName = key.replace(/_/g, ' ');
                const projectConfig: ProjectConfig = {
                    name: projectName,
                    projectId: `unknown-${projectName.toLowerCase().replace(/\s+/g, '-')}`,
                    isUnknown: true,
                    projectType: 'unknown',
                    needsConfiguration: true,
                    configurationHint: 'Empty project - add connectionString for self-hosted or id/key/secret for DXP',
                    environments: ['Unknown'],
                    configSource: 'environment'
                };
                projects.push(projectConfig);
                return;
            }
    
            // Check if this looks like our API key format OR a connection string OR self-hosted paths
            // Must contain either:
            // 1. DXP format: (id=, key=, secret=)
            // 2. Azure connection string: (DefaultEndpointsProtocol=)
            // 3. Self-hosted with paths only: (blobPath= or logPath=) but no id/key/secret
            const hasDxpFormat = value.includes('id=') && value.includes('key=') && value.includes('secret=');
            const hasConnectionString = value.startsWith('DefaultEndpointsProtocol=');
            const hasSelfHostedPaths = (value.includes('blobPath=') || value.includes('logPath=')) &&
                                       !value.includes('id=') && !value.includes('key=') && !value.includes('secret=');
    
            const hasCorrectFormat = hasDxpFormat || hasConnectionString || hasSelfHostedPaths;
    
            if (!hasCorrectFormat) {
                return;
            }
    
            // Use the environment variable name as the project name (underscores become spaces)
            const projectName = key.replace(/_/g, ' ');
    
            try {
                // Check if this is a raw Azure connection string (self-hosted mode)
                if (value.startsWith('DefaultEndpointsProtocol=')) {
                    // Extract connection string and any additional parameters
                    // Format: DefaultEndpointsProtocol=...;EndpointSuffix=core.windows.net;blobPath=/path;logPath=/path
    
                    // Find where the connection string ends (after EndpointSuffix)
                    const endpointMatch = value.match(/EndpointSuffix=[^;]+/);
                    let connectionString = value;
                    let additionalParams: Record<string, string> = {};
    
                    if (endpointMatch) {
                        const endIndex = value.indexOf(endpointMatch[0]) + endpointMatch[0].length;
                        connectionString = value.substring(0, endIndex);
    
                        // Parse any additional parameters after the connection string
                        const remaining = value.substring(endIndex);
                        if (remaining) {
                            const extraParts = remaining.split(';').filter(p => p.trim());
                            extraParts.forEach(part => {
                                const [key, val] = part.split('=');
                                if (key && val) {
                                    additionalParams[key] = val;
                                }
                            });
                        }
                    }
    
                    const projectConfig: ProjectConfig = {
                        name: projectName,
                        projectId: `self-hosted-${projectName.toLowerCase().replace(/\s+/g, '-')}`,
                        apiKey: '',
                        apiSecret: '',
                        connectionString: connectionString,
                        isSelfHosted: true,
                        environments: ['Production'], // Self-hosted typically has one environment
                        configSource: 'environment'
                    };
    
                    // Add optional paths if provided
                    if (additionalParams.blobPath) {
                        projectConfig.blobPath = additionalParams.blobPath;
                    }
                    if (additionalParams.logPath) {
                        projectConfig.logPath = additionalParams.logPath;
                    }
                    if (additionalParams.dbPath) {
                        projectConfig.dbPath = additionalParams.dbPath;
                    }
    
                    projects.push(projectConfig);
                    return;
                }
    
                // Otherwise parse semicolon-separated key=value pairs for DXP projects
                const params: Record<string, string> = {};
                const parts = value.split(';').filter(p => p.trim());
    
                if (parts.length === 0) {
                    // Empty configuration - treat as Unknown project placeholder
                    const projectConfig: ProjectConfig = {
                        name: projectName,
                        projectId: `unknown-${projectName.toLowerCase().replace(/\s+/g, '-')}`,
                        isUnknown: true,
                        projectType: 'unknown',
                        needsConfiguration: true,
                        configurationHint: 'Empty project - add connectionString for self-hosted or id/key/secret for DXP',
                        environments: ['Unknown'],
                        configSource: 'environment'
                    };
                    projects.push(projectConfig);
                    return;
                }
    
                parts.forEach(param => {
                    const equalIndex = param.indexOf('=');
                    if (equalIndex === -1) {
                        configErrors.push({
                            project: projectName,
                            error: `Invalid parameter format: "${param}" (expected key=value)`,
                            variable: key
                        });
                        return;
                    }
    
                    const paramKey = param.substring(0, equalIndex).trim();
                    const paramValue = param.substring(equalIndex + 1).trim();
    
                    if (!paramKey || !paramValue) {
                        configErrors.push({
                            project: projectName,
                            error: `Empty key or value in parameter: "${param}"`,
                            variable: key
                        });
                        return;
                    }
    
                    params[paramKey] = paramValue;
                });
    
                // Extract credentials using standard format
                let projectId = params.id;  // Changed to let to allow reassignment
                const apiKey = params.key;
                const apiSecret = params.secret;
                let connectionString = params.connectionString || params.connStr;
    
                // Check if this is a path-only project (has paths but no DXP credentials or connection string)
                const isPathOnlyProject = (params.blobPath || params.logPath || params.dbPath) &&
                                          !params.id && !params.key && !params.secret && !connectionString;
    
                // Determine project type and handle accordingly
                if (connectionString) {
                    // Self-hosted mode with connection string
                    if (!projectId) {
                        // Generate a simple ID from the project name
                        const projectIdBase = projectName.toLowerCase().replace(/\s+/g, '-');
                        projectId = `self-hosted-${projectIdBase}`;
                    }
                } else if (isPathOnlyProject) {
                    // Unknown type - has paths but no clear indication of type
                    if (!projectId) {
                        // Generate a simple ID from the project name
                        const projectIdBase = projectName.toLowerCase().replace(/\s+/g, '-');
                        projectId = `unknown-${projectIdBase}`;
                    }
                    OutputLogger.debug(`Unknown project type "${projectName}" configured with paths only`);
                } else if (!params.id || !params.key || !params.secret) {
                    // DXP mode - needs full API credentials
                    const missingFields: string[] = [];
                    if (!projectId) missingFields.push('id');
                    if (!apiKey) missingFields.push('key');
                    if (!apiSecret) missingFields.push('secret');
    
                    if (missingFields.length > 0) {
                        configErrors.push({
                            project: projectName,
                            error: `Missing required fields: ${missingFields.join(', ')}`,
                            variable: key,
                            hint: `Format: "id=<uuid>;key=<key>;secret=<secret>"`
                        });
                        return;
                    }
                }
    
                // Validate UUID format for project ID (skip for self-hosted and unknown)
                const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
                const isSelfHostedId = projectId && projectId.startsWith('self-hosted-');
                const isUnknownId = projectId && projectId.startsWith('unknown-');
                if (!isSelfHostedId && !isUnknownId && !uuidRegex.test(projectId!)) {
                    configErrors.push({
                        project: projectName,
                        error: `Invalid project ID format: "${projectId}"`,
                        variable: key,
                        hint: `Project ID should be a UUID like: abc12345-1234-5678-9abc-def123456789`
                    });
                }
    
                // Validate environments if specified
                if (params.environments) {
                    const validEnvs = ['Integration', 'Preproduction', 'Production'];
                    const envs = params.environments.split(',').map(e => e.trim());
                    const invalidEnvs = envs.filter(e => !validEnvs.includes(e));
    
                    if (invalidEnvs.length > 0) {
                        configErrors.push({
                            project: projectName,
                            error: `Invalid environments: ${invalidEnvs.join(', ')}`,
                            variable: key,
                            hint: `Valid environments are: Integration, Preproduction, Production`
                        });
                    }
                }
    
                // Add project if validation passed
                const projectConfig: ProjectConfig = {
                    name: projectName,
                    projectId: projectId!,
                    apiKey: apiKey || '',
                    apiSecret: apiSecret || '',
                    environments: params.environments
                        ? params.environments.split(',').map(e => e.trim())
                        : ['Integration', 'Preproduction', 'Production'],
                    configSource: 'environment'
                };
    
                // Determine project type based on available credentials
                if (connectionString) {
                    // Self-hosted with connection string
                    projectConfig.connectionString = connectionString;
                    projectConfig.isSelfHosted = true;
                    projectConfig.projectType = 'self-hosted';
                } else if (isPathOnlyProject) {
                    // Unknown type - has paths but no credentials
                    projectConfig.isUnknown = true;
                    projectConfig.projectType = 'unknown';
                    projectConfig.needsConfiguration = true;
                    // For unknown projects, we need to guide users to add credentials
                    projectConfig.configurationHint = 'Add connectionString for self-hosted or id/key/secret for DXP';
                } else if (apiKey && apiSecret) {
                    // DXP PaaS project
                    projectConfig.projectType = 'dxp-paas';
                }
    
                // Add compact configuration fields if present
                if (params.blobPath) {
                    projectConfig.blobPath = params.blobPath;
                }
                if (params.dbPath) {
                    projectConfig.dbPath = params.dbPath;
                }
                if (params.logPath) {
                    projectConfig.logPath = params.logPath;
                }
                if (params.telemetry) {
                    projectConfig.telemetry = params.telemetry.toLowerCase() === 'true';
                }
    
                projects.push(projectConfig);
    
            } catch (error: any) {
                configErrors.push({
                    project: projectName,
                    error: `Failed to parse configuration: ${error.message}`,
                    variable: key
                });
            }
        });
    
        // Log configuration errors if any
        if (configErrors.length > 0) {
            console.error('\nāš ļø  Configuration Errors Found:');
            configErrors.forEach(err => {
                console.error(`\n  Project: ${err.project}`);
                console.error(`  Variable: ${err.variable}`);
                console.error(`  Error: ${err.error}`);
                if (err.hint) {
                    console.error(`  Hint: ${err.hint}`);
                }
                if (err.value) {
                    console.error(`  Value: ${err.value.substring(0, 50)}...`);
                }
            });
            console.error('\n');
        }
    
        // Add dynamically added configurations
        this.dynamicConfigurations.forEach(dynConfig => {
            // Check if this dynamic config should replace an existing project
            const existingIndex = projects.findIndex(p => {
                // Match by original name (for renames)
                if (dynConfig.originalName && p.name === dynConfig.originalName) {
                    return true;
                }
                // Match by current name
                if (p.name === dynConfig.name || p.name.toLowerCase() === dynConfig.name.toLowerCase()) {
                    return true;
                }
                // Match by project ID
                if (p.projectId === dynConfig.projectId) {
                    return true;
                }
                return false;
            });
    
            if (existingIndex >= 0) {
                // Replace existing project with dynamic configuration (upgrade/rename scenario)
                projects[existingIndex] = dynConfig;
            } else if (!projects.find(p => p.projectId === dynConfig.projectId)) {
                // Only add if not already in list (avoid duplicates by ID)
                projects.push(dynConfig);
            }
        });
    
        // First project is always the default (simplified logic)
    
        // DEBUG: Log final projects found
        OutputLogger.debug('Final projects found:');
        projects.forEach((p, i) => {
            OutputLogger.debug(`  ${i + 1}. Name: "${p.name}", ID: ${p.projectId || 'undefined'}, Source: ${p.configSource || 'unknown'}`);
        });
    
        return projects;
    }
  • Type definition for arguments accepted by project tools, including get_project.
    interface GetProjectArgs {
        projectName?: string;
        projectId?: string;
        apiKey?: string;
        apiSecret?: string;
        connectionString?: string;
        renameTo?: string;
    }
  • Tool availability registration for 'get_project' - defines supported hosting types and category.
    'get_project': {
        hostingTypes: ['dxp-paas', 'dxp-saas', 'self-hosted', 'unknown'],
        category: 'Project Management',
        description: 'Get project information and configuration details'
  • Helper to show current/active project configuration, used in project resolution.
    static async showCurrentProject(): Promise<any> {
        try {
            // Check for last used project first (from switch_project)
            let currentProject: ProjectConfig | null = null;
            const lastUsed = process.env.MCP_LAST_USED_PROJECT;
    
            if (lastUsed) {
                const projects = this.getConfiguredProjects();
                currentProject = projects.find(p =>
                    p.name === lastUsed ||
                    p.name.toLowerCase() === lastUsed.toLowerCase()
                ) || null;
            }
    
            // Fall back to default (first project)
            if (!currentProject) {
                currentProject = this.getCurrentProject();
            }
    
            if (!currentProject) {
                return ResponseBuilder.error('No project currently selected');
            }
    
            let response = `šŸ“Œ **Current Project: ${currentProject.name}**\n\n`;
            response += `• Project ID: ${currentProject.projectId}\n`;
    
            // Show project type
            if (currentProject.isSelfHosted) {
                response += `• Type: Self-hosted Azure\n`;
                response += `• Environment: Production\n`;
            } else if (currentProject.isUnknown) {
                response += `• Type: Unconfigured\n`;
                response += `• Status: Needs API credentials or connection string\n`;
            } else {
                response += `• Type: DXP PaaS\n`;
                response += `• Configured Environments: ${currentProject.environments ? currentProject.environments.join(', ') : 'N/A'}\n`;
                response += `• Note: Use \`test_connection\` to check actual permissions\n`;
            }
    
            response += `${currentProject.isDefault ? '• Default: Yes ⭐\n' : ''}`;
    
            return ResponseBuilder.success(response);
        } catch (error: any) {
            return ResponseBuilder.error(`Failed to get current project: ${error.message}`);
        }
    }
Behavior4/5

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

With no annotations provided, the description carries full burden and does well by disclosing key behavioral traits: real-time performance (<1s), specific return data fields, and that it returns a 'full project config object'. However, it doesn't mention error conditions, authentication requirements, or rate limits which would be valuable for a tool with no annotation coverage.

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 efficiently structured with key information front-loaded: purpose, performance characteristics, return data, usage context, and parameter requirement. Every sentence adds value with no redundant information. The emoji adds visual distinction without compromising clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only tool with no annotations and no output schema, the description provides adequate coverage of purpose and basic behavior but has significant gaps. It doesn't explain the parameter semantics adequately (critical with 0% schema coverage), doesn't describe error conditions, and while it mentions return fields, doesn't provide structure details for the 'full project config object'.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage for 2 parameters, the description must compensate but provides minimal parameter guidance. It mentions 'Required: projectName' but doesn't explain the relationship between projectName and projectId parameters, nor clarifies if both can be used together or are alternatives. The schema shows both are optional (no required parameters), creating potential confusion.

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 verb ('Get') and resource ('detailed project configuration'), and distinguishes it from siblings like 'list_projects' by specifying it returns detailed configuration rather than just listing. It explicitly mentions what information is returned (project ID, name, environments, hosting type, credential status).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use the tool ('to inspect project settings or troubleshoot configuration issues'), but doesn't explicitly state when NOT to use it or name specific alternatives. While it distinguishes from 'list_projects' by implication, it doesn't explicitly compare with other sibling tools like 'current_project' or 'update_project'.

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