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
| Name | Required | Description | Default |
|---|---|---|---|
| projectName | No | ||
| projectId | No |
Implementation Reference
- lib/tools/project-tools.ts:1435-1463 (handler)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); }
- lib/tools/project-tools.ts:186-557 (helper)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; }
- lib/tools/project-tools.ts:108-115 (schema)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; }
- lib/utils/tool-availability-matrix.ts:106-109 (registration)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'
- lib/tools/project-tools.ts:667-712 (helper)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}`); } }