Skip to main content
Glama
JaxonDigital

Optimizely DXP MCP Server

by JaxonDigital

discover_logs

Find available log containers across Optimizely DXP environments to identify which logs exist before downloading or analyzing them. Returns container names, log types, and environment availability.

Instructions

šŸ”Ž Discover available log containers across all environments. REAL-TIME: <2s. Returns container names, log types available (http, application), and environment availability. Use this before download_logs() or analyze_logs_streaming() to understand what logs exist. Useful for multi-environment projects to find which environments have logging enabled. Optional: project. Returns container inventory.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectNameNo
projectIdNo
apiKeyNo
apiSecretNo

Implementation Reference

  • Main handler function for the 'discover_logs' tool. Discovers and classifies log containers across all DXP environments (Production, Preproduction, Integration). Uses StorageTools to list containers, matches against log patterns, generates diagnostic report with recommendations.
    static async discoverLogContainers(args: DiscoveryArgs): Promise<any> {
        try {
            OutputLogger.info('šŸ” Discovering log containers across all environments...\n');
    
            // Resolve project configuration
            const resolved = ProjectTools.resolveCredentials(args);
            if (!resolved.success || !resolved.credentials) {
                return ResponseBuilder.invalidParams('Missing required project configuration');
            }
    
            const projectConfig = resolved.credentials;
            const projectName = resolved.project ? resolved.project.name : 'Unknown';
    
            OutputLogger.info(`šŸ“‹ Project: ${projectName}`);
            OutputLogger.info(`šŸ”‘ Using API key: ${projectConfig.apiKey?.substring(0, 8)}...`);
    
            // Test each environment
            const environments = ['Production', 'Preproduction', 'Integration'];
            const discoveryResults: DiscoveryResults = {};
    
            for (const env of environments) {
                OutputLogger.info(`\nšŸŒ Checking ${env} environment...`);
    
                try {
                    const envArgs = {
                        ...projectConfig,
                        environment: env
                    };
    
                    if (process.env.DEBUG === 'true') {
                        console.error(`[DISCOVER_LOGS] Calling handleListStorageContainers for ${env}`);
                        console.error(`[DISCOVER_LOGS] Args:`, {
                            hasApiKey: !!envArgs.apiKey,
                            hasApiSecret: !!envArgs.apiSecret,
                            hasProjectId: !!envArgs.projectId,
                            environment: envArgs.environment
                        });
                    }
    
                    // Get all containers for this environment
                    const containersResult = await StorageTools.handleListStorageContainers(envArgs as any);
    
                    if (process.env.DEBUG === 'true') {
                        console.error(`[DISCOVER_LOGS] Result type:`, typeof containersResult);
                        console.error(`[DISCOVER_LOGS] Result structure:`, Object.keys(containersResult || {}));
                    }
    
                    const containers = this.extractContainerList(containersResult);
    
                    if (process.env.DEBUG === 'true') {
                        console.error(`[DISCOVER_LOGS] Extracted ${containers.length} containers from result`);
                    }
    
                    if (!containers || containers.length === 0) {
                        OutputLogger.info(`  āš ļø  No containers accessible in ${env}`);
                        discoveryResults[env] = {
                            accessible: false,
                            logContainers: [],
                            otherContainers: []
                        };
                        continue;
                    }
    
                    // Identify log containers
                    const logContainers: LogContainerInfo[] = [];
                    const otherContainers: string[] = [];
    
                    for (const container of containers) {
                        const match = this.identifyLogContainer(container);
                        if (match) {
                            logContainers.push({
                                name: container,
                                type: match.type,
                                description: match.description
                            });
                        } else {
                            otherContainers.push(container);
                        }
                    }
    
                    discoveryResults[env] = {
                        accessible: true,
                        totalContainers: containers.length,
                        logContainers: logContainers,
                        otherContainers: otherContainers
                    };
    
                    // Display results for this environment
                    OutputLogger.info(`  āœ… Found ${containers.length} total containers`);
    
                    if (logContainers.length > 0) {
                        OutputLogger.info(`  šŸ“Š Log containers (${logContainers.length}):`);
                        for (const log of logContainers) {
                            OutputLogger.info(`     • ${log.name} (${log.description})`);
                        }
                    } else {
                        OutputLogger.info(`  āš ļø  No log containers found!`);
                    }
    
                    if (otherContainers.length > 0) {
                        OutputLogger.info(`  šŸ“¦ ALL other containers (${otherContainers.length}):`);
                        for (const container of otherContainers) {
                            OutputLogger.info(`     • ${container}`);
                        }
                    }
    
                } catch (error: any) {
                    if (process.env.DEBUG === 'true') {
                        console.error(`[DISCOVER_LOGS] Full error for ${env}:`, error);
                        console.error(`[DISCOVER_LOGS] Error stack:`, error.stack);
                    }
    
                    OutputLogger.error(`  āŒ Error accessing ${env}: ${error.message}`);
    
                    // Provide more context in the error
                    let errorDetail = error.message;
                    if (error.response) {
                        errorDetail += ` (HTTP ${error.response.statusCode || error.response.status})`;
                    }
                    if (error.code) {
                        errorDetail += ` [${error.code}]`;
                    }
    
                    discoveryResults[env] = {
                        accessible: false,
                        error: errorDetail
                    };
                }
            }
    
            // Generate diagnostic report
            return this.generateDiagnosticReport(discoveryResults, projectName);
    
        } catch (error: any) {
            return ResponseBuilder.error(`Log discovery failed: ${error.message}`);
        }
    }
  • LOG_CONTAINER_PATTERNS: Comprehensive regex patterns used to identify and classify different types of log containers across DXP environments.
    static LOG_CONTAINER_PATTERNS: LogContainerPattern[] = [
        // Standard DXP containers
        { pattern: /^appservicelogs$/, type: 'application', description: 'Application logs' },
        { pattern: /^webservicelogs$/, type: 'web', description: 'Web server logs' },
        { pattern: /^cloudflarelogpush$/, type: 'cloudflare', description: 'Cloudflare logs (beta)' },
    
        // App Service Insights containers
        { pattern: /^insights-logs-appserviceconsolelogs$/, type: 'application', description: 'App Service console logs' },
        { pattern: /^insights-logs-appservicehttplogs$/, type: 'web', description: 'App Service HTTP logs' },
        { pattern: /^insights-logs-/, type: 'insights', description: 'Application Insights logs' },
    
        // Alternative Azure containers (commonly found in Production)
        { pattern: /^azure-application-logs$/, type: 'application', description: 'Azure application logs' },
        { pattern: /^azure-web-logs$/, type: 'web', description: 'Azure web server logs' },
        { pattern: /^azure-logs-/, type: 'azure', description: 'Azure logs' },
    
        // Application Insights containers
        { pattern: /applicationinsights/, type: 'appinsights', description: 'Application Insights data' },
        { pattern: /appinsights/, type: 'appinsights', description: 'Application Insights data' },
    
        // Enhanced generic log patterns
        { pattern: /logs?$/, type: 'generic', description: 'Generic log container' },
        { pattern: /log-/, type: 'generic', description: 'Generic log container' },
        { pattern: /-logs?$/, type: 'generic', description: 'Log container (suffix)' },
        { pattern: /diagnostic.*logs?/i, type: 'diagnostic', description: 'Diagnostic logs' },
        { pattern: /inte-.*logs?/i, type: 'integration', description: 'Integration environment logs' },
        { pattern: /backup.*logs?/i, type: 'backup', description: 'Backup logs' },
        { pattern: /^db-backups?-/i, type: 'database', description: 'Database backup container' }
    ];
  • extractContainerList: Parses container lists from various response formats (structured MCP, legacy text, JSON, markdown tables, bullet lists). Handles DXP-86 structured responses and falls back to text parsing.
    static extractContainerList(response: any): string[] {
        if (!response) {
            return [];
        }
    
        // DXP-86: First check for structured data (preferred method)
        if (typeof response === 'object' && response !== null) {
            const structuredResponse = response as StructuredResponse;
    
            // Check for structuredContent.data.containers (MCP protocol format)
            if (structuredResponse.structuredContent?.data?.containers) {
                if (process.env.DEBUG === 'true') {
                    console.error('[DISCOVER_LOGS] Found containers in structuredContent.data.containers');
                }
                return structuredResponse.structuredContent.data.containers;
            }
    
            // Check for direct data.containers format
            if (structuredResponse.data?.containers) {
                if (process.env.DEBUG === 'true') {
                    console.error('[DISCOVER_LOGS] Found containers in data.containers');
                }
                return structuredResponse.data.containers;
            }
    
            // Check for error response
            if (structuredResponse.error) {
                OutputLogger.info(`Error in container list response: ${structuredResponse.error}`);
                return [];
            }
        }
    
        // Fall back to text parsing for legacy formats
        let text = '';
    
        if (typeof response === 'object' && response !== null) {
            const structuredResponse = response as StructuredResponse;
    
            if (structuredResponse.content && Array.isArray(structuredResponse.content) && structuredResponse.content[0]) {
                text = structuredResponse.content[0].text || '';
            } else if (structuredResponse.result && structuredResponse.result.content && Array.isArray(structuredResponse.result.content)) {
                const content = structuredResponse.result.content[0];
                if (content && content.text) {
                    text = content.text;
                }
            } else {
                text = JSON.stringify(response);
            }
        } else if (typeof response === 'string') {
            text = response;
        }
    
        if (!text) {
            return [];
        }
    
        if (process.env.DEBUG === 'true') {
            console.error('[DISCOVER_LOGS] Falling back to text parsing');
        }
    
        const containers: string[] = [];
        const lines = text.split('\n');
    
        for (const line of lines) {
            if (!line.trim()) continue;
    
            // Look for numbered emoji format: "1. šŸ“¦ container-name"
            let match = line.match(/^\d+\.\s*šŸ“¦\s*(.+)$/);
            if (match) {
                containers.push(match[1].trim());
                continue;
            }
    
            // Look for simple bullet format: "- container-name" or "• container-name"
            match = line.match(/^[\s\-•]\s*([^\s\-•].+)$/);
            if (match && !match[1].includes('Storage Container') && !match[1].includes('---')) {
                const name = match[1].trim();
                if (name && !name.includes('|') && !name.includes('Environment')) {
                    containers.push(name);
                }
                continue;
            }
    
            // Look for markdown table format: "| container-name |"
            match = line.match(/\|\s*([^|]+)\s*\|/);
            if (match && !match[1].includes('Storage Container') && !match[1].includes('---')) {
                containers.push(match[1].trim());
                continue;
            }
    
            // Try JSON parsing if it looks like container data
            if (line.includes('{') || line.includes('[')) {
                try {
                    const data = JSON.parse(line);
                    if (Array.isArray(data)) {
                        data.forEach((item: any) => {
                            const name = item.StorageContainer || item.Name || item.ContainerName;
                            if (name) containers.push(name);
                        });
                    } else if (data.StorageContainer || data.Name || data.ContainerName) {
                        containers.push(data.StorageContainer || data.Name || data.ContainerName);
                    }
                } catch (e) {
                    // Not JSON, continue
                }
            }
        }
    
        return containers.filter(Boolean);
    }
  • TOOL_MATRIX entry for 'discover_logs': Defines availability across hosting types, category, and description. Used for hosting-aware tool filtering and restriction messages.
    'discover_logs': {
        hostingTypes: ['dxp-paas', 'dxp-saas', 'self-hosted'],
        category: 'Storage & Downloads',
        description: 'Discover available log containers'
    },
Behavior3/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. It provides useful context about performance ('REAL-TIME: <2s'), return format ('Returns container names, log types available, and environment availability'), and optional parameters ('Optional: project'). However, it doesn't cover authentication requirements (despite apiKey/apiSecret parameters), rate limits, or error conditions. The description adds value but doesn't fully compensate for the lack of annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with key information front-loaded (purpose, performance, returns). Every sentence adds value, though the final sentence ('Returns container inventory') is somewhat redundant with earlier information. The emoji adds visual distinction but doesn't detract from the content's efficiency.

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?

Given the tool's moderate complexity (discovery operation with 4 parameters), no annotations, and no output schema, the description provides good purpose and usage context but has significant gaps. It doesn't explain authentication requirements despite apiKey/apiSecret parameters, doesn't detail the return structure beyond high-level categories, and provides minimal parameter guidance. The description is adequate but incomplete for a tool with authentication parameters.

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 4 parameters, the description only mentions one parameter ('Optional: project') without specifying whether this refers to projectName or projectId. It provides no information about apiKey or apiSecret parameters. The description adds minimal semantic value beyond what the bare schema provides, failing to compensate for the complete lack of parameter documentation in the schema.

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 ('discover available log containers') and resources ('across all environments'), and distinguishes it from siblings by explicitly mentioning it should be used before download_logs() or analyze_logs_streaming(). It explains what information is returned (container names, log types, environment availability) and why it's useful (for multi-environment projects).

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 this tool ('Use this before download_logs() or analyze_logs_streaming() to understand what logs exist') and why ('Useful for multi-environment projects to find which environments have logging enabled'). It clearly positions this as a discovery/preparation step before other log-related operations.

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