UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

190 lines 6.16 kB
/** * POC: Metrics Scope Validator * * This proof of concept validates the critical orchestration rule: * - Campaigns MUST use metrics scope: "session" * - Experiments MUST use metrics scope: "visitor" * * This single file proves the entire validation architecture concept. */ import * as fs from 'fs'; // Orchestration-specific rules that extend the base OpenAPI fields const ORCHESTRATION_RULES = { web: { campaign: { metrics: { scope: { required: 'session', error: 'Web campaigns MUST use metrics scope: "session", not "visitor"' } } }, experiment: { metrics: { scope: { required: 'visitor', error: 'Web experiments MUST use metrics scope: "visitor", not "session"' } } } }, feature: { // Feature experimentation doesn't have the same metrics scope restrictions } }; /** * Extract entity type from system_template_id * Examples: * - "optimizely_experiment_simple" -> "experiment" * - "optimizely_campaign_basic" -> "campaign" */ function extractEntityType(systemTemplateId) { const patterns = [ { regex: /optimizely_experiment_/, type: 'experiment' }, { regex: /optimizely_campaign_/, type: 'campaign' }, { regex: /optimizely_audience_/, type: 'audience' }, { regex: /optimizely_event_/, type: 'event' }, { regex: /optimizely_flag_/, type: 'flag' }, { regex: /optimizely_ruleset_/, type: 'ruleset' } ]; for (const pattern of patterns) { if (pattern.regex.test(systemTemplateId)) { return pattern.type; } } return null; } /** * Validate metrics scope for a single step */ function validateMetricsScope(step, platform = 'web') { // Only validate template steps if (step.type !== 'template') return null; const entityType = extractEntityType(step.template.system_template_id); if (!entityType) return null; // Get orchestration rules for this entity const platformRules = ORCHESTRATION_RULES[platform]; if (!platformRules) return null; const rules = platformRules[entityType]; if (!rules || typeof rules !== 'object' || !('metrics' in rules)) return null; const metricsRules = rules.metrics; if (!metricsRules || typeof metricsRules !== 'object' || !('scope' in metricsRules)) return null; // Check if step has metrics const metrics = step.template.inputs?.metrics; if (!metrics || !Array.isArray(metrics)) return null; // Validate each metric's scope const scopeRule = metricsRules.scope; if (!scopeRule || typeof scopeRule !== 'object') return null; for (let i = 0; i < metrics.length; i++) { const metric = metrics[i]; if (metric.scope !== scopeRule.required) { return { stepId: step.id, entityType, field: `metrics[${i}].scope`, message: scopeRule.error, found: metric.scope, expected: scopeRule.required, fix: { path: `steps.${step.id}.template.inputs.metrics[${i}].scope`, value: scopeRule.required } }; } } return null; } /** * Validate an entire orchestration template */ export function validateTemplate(template) { const errors = []; const warnings = []; const platform = template.platform || 'web'; let validSteps = 0; // Validate each step for (const step of template.steps) { const error = validateMetricsScope(step, platform); if (error) { errors.push(error); } else { validSteps++; } } return { valid: errors.length === 0, errors, warnings, summary: { totalSteps: template.steps.length, validSteps, errorCount: errors.length } }; } /** * Format validation results for display */ export function formatResults(result) { const lines = []; lines.push('ORCHESTRATION TEMPLATE VALIDATION'); lines.push('=================================\n'); if (result.valid) { lines.push('✅ Template is valid!'); lines.push(` ${result.summary.totalSteps} steps validated successfully`); } else { lines.push(`❌ Found ${result.errors.length} validation error(s):\n`); for (const error of result.errors) { lines.push(`Error in step "${error.stepId}" (${error.entityType}):`); lines.push(` Field: ${error.field}`); lines.push(` Issue: ${error.message}`); lines.push(` Found: "${error.found}"`); lines.push(` Expected: "${error.expected}"`); if (error.fix) { lines.push(` Fix: Set ${error.fix.path} to "${error.fix.value}"`); } lines.push(''); } } lines.push('\nSUMMARY:'); lines.push(` Total steps: ${result.summary.totalSteps}`); lines.push(` Valid steps: ${result.summary.validSteps}`); lines.push(` Errors: ${result.summary.errorCount}`); return lines.join('\n'); } /** * CLI entry point for testing */ function main() { const args = process.argv.slice(2); if (args.length === 0) { console.log('Usage: ts-node validate-metrics-scope.ts <template.json>'); process.exit(1); } const templatePath = args[0]; try { const templateContent = fs.readFileSync(templatePath, 'utf8'); const template = JSON.parse(templateContent); const result = validateTemplate(template); console.log(formatResults(result)); process.exit(result.valid ? 0 : 1); } catch (error) { console.error(`Error: ${error.message}`); process.exit(1); } } // Run if called directly if (import.meta.url === `file://${process.argv[1]}`) { main(); } //# sourceMappingURL=validate-metrics-scope.js.map