UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

630 lines 18.6 kB
/** * Field Pattern Definitions for RobustIntentParser * * IMPLEMENTATION STATUS: * COMPLETE: Common fields and entity-specific fields (50+ fields) * * Last Updated: July 3, 2025 */ export const FIELD_PATTERNS = { // Common fields across entities id: { canonical: 'id', patterns: [ /\bid\b/i, /identifier/i, /unique\s+id/i ], dataType: 'string' }, key: { canonical: 'key', patterns: [ /\bkey\b/i, /unique\s+key/i, /identifier/i, /slug/i ], dataType: 'string' }, name: { canonical: 'name', patterns: [ /\bname\b/i, /title/i, /label/i, /display\s+name/i ], dataType: 'string' }, description: { canonical: 'description', patterns: [ /description/i, /desc/i, /details?/i, /about/i, /summary/i ], dataType: 'string' }, status: { canonical: 'status', patterns: [ /status/i, /state/i, /condition/i, /active|inactive/i, // REMOVED: /enabled|disabled/i - conflicts with specific enabled field /disabled/i // Keep disabled but not enabled ], dataType: 'string' }, created_time: { canonical: 'created_time', patterns: [ /created(?:\s+time)?/i, /creation(?:\s+date)?/i, /created\s+at/i, /date\s+created/i, /when\s+created/i ], dataType: 'date' }, updated_time: { canonical: 'updated_time', patterns: [ /updated(?:\s+time)?/i, /modified(?:\s+time)?/i, /last\s+updated/i, /updated\s+at/i, /last\s+modified/i ], dataType: 'date' }, archived: { canonical: 'archived', patterns: [ /archived/i, /deleted/i, /removed/i, /inactive/i, /deactivated/i ], dataType: 'boolean' }, // Flag-specific fields enabled: { canonical: 'enabled', patterns: [ /enabled/i, /active/i, /\bon\b/i, /turned\s+on/i, /activated/i ], entity: 'flags', jsonPath: 'environments.*.enabled', dataType: 'boolean' }, environment: { canonical: 'environment_key', patterns: [ /environment/i, /env/i, /stage/i, /production|prod/i, /development|dev/i, /staging/i ], entity: 'flags', dataType: 'string' }, percentage: { canonical: 'percentage_included', patterns: [ /percentage/i, /percent/i, /traffic(?:\s+allocation)?/i, /rollout(?:\s+percentage)?/i, /allocation/i, /%/ ], entity: 'rules', dataType: 'number' }, // Experiment-specific fields traffic_allocation: { canonical: 'traffic_allocation', patterns: [ /traffic\s+allocation/i, /traffic\s+split/i, /allocation/i, /distribution/i ], entity: 'experiments', jsonPath: 'data_json.traffic_allocation', dataType: 'json' }, variations_count: { canonical: 'variations_count', patterns: [ /variations?\s+count/i, /number\s+of\s+variations?/i, /how\s+many\s+variations?/i, /variant\s+count/i ], entity: 'experiments', dataType: 'number' }, metrics: { canonical: 'metrics', patterns: [ /metrics?/i, /goals?/i, /conversions?/i, /kpis?/i, /measures?/i ], entity: 'experiments', jsonPath: 'data_json.metrics', dataType: 'json' }, // 🚨 PERFORMANCE METRICS - From experiment_results table visitors: { canonical: 'visitors', patterns: [ /visitors?/i, /users?/i, /customers?/i, /people/i, /traffic/i, /unique\s+visitors?/i, /total\s+users?/i, /site\s+visitors?/i, /page\s+views?/i, /sessions?/i ], entity: 'experiment_results', dataType: 'number' }, conversion_rate: { canonical: 'conversion_rate', patterns: [ /conversion\s+rates?/i, /cvr/i, /conversion\s+%/i, /conversion\s+percentage/i, /success\s+rates?/i, /conversion\s+ratio/i, /goal\s+completion\s+rate/i, /conversion\s+performance/i ], entity: 'experiment_results', dataType: 'number' }, confidence: { canonical: 'confidence', patterns: [ /confidence/i, /confidence\s+level/i, /confidence\s+threshold/i, /certainty/i, /confidence\s+interval/i, /confidence\s+score/i, /statistical\s+confidence/i ], entity: 'experiment_results', dataType: 'number' }, statistical_significance: { canonical: 'statistical_significance', patterns: [ /statistical\s+significance/i, /stat\s+sig/i, /significance/i, /p[\s-]?value/i, /significant/i, /statistically\s+significant/i, /sig\s+level/i ], entity: 'experiment_results', dataType: 'number' }, lift: { canonical: 'lift', patterns: [ /lift/i, /uplift/i, /improvement/i, /increase/i, /gain/i, /boost/i, /performance\s+lift/i, /relative\s+improvement/i, /percentage\s+change/i ], entity: 'experiment_results', dataType: 'number' }, conversions: { canonical: 'conversions', patterns: [ /conversions?/i, /total\s+conversions?/i, /conversion\s+count/i, /goals?\s+achieved/i, /successful\s+conversions?/i, /completed\s+goals?/i ], entity: 'experiment_results', dataType: 'number' }, unique_conversions: { canonical: 'unique_conversions', patterns: [ /unique\s+conversions?/i, /unique\s+conversion\s+count/i, /distinct\s+conversions?/i, /unique\s+visitors?\s+converted/i, /unique\s+users?\s+converted/i ], entity: 'experiment_results', dataType: 'number' }, baseline_visitors: { canonical: 'baseline_visitors', patterns: [ /baseline\s+visitors?/i, /control\s+visitors?/i, /baseline\s+count/i, /control\s+group\s+size/i, /baseline\s+users?/i, /original\s+visitors?/i ], entity: 'experiment_results', dataType: 'number' }, treatment_visitors: { canonical: 'treatment_visitors', patterns: [ /treatment\s+visitors?/i, /variation\s+visitors?/i, /treatment\s+count/i, /variant\s+users?/i, /test\s+group\s+size/i, /challenger\s+visitors?/i ], entity: 'experiment_results', dataType: 'number' }, winner: { canonical: 'winner', patterns: [ /winner/i, /winning\s+variation/i, /best\s+performing/i, /top\s+performer/i, /winning\s+variant/i, /champion/i ], entity: 'experiment_results', dataType: 'string' }, error_rate: { canonical: 'error_rate', patterns: [ /error\s+rates?/i, /javascript\s+errors?/i, /js\s+errors?/i, /page\s+errors?/i, /error\s+percentage/i, /failure\s+rates?/i ], entity: 'experiment_results', dataType: 'number' }, // Conditions field (used by pages ONLY - URL targeting conditions) conditions: { canonical: 'conditions', patterns: [ /url\s+conditions?/i, /page\s+conditions?/i, /url\s+targeting/i, /page\s+targeting/i ], entity: 'pages', // CONSTRAINT TO PAGES ONLY dataType: 'json' }, // Audience conditions field (used by rules and audiences) audience_conditions: { canonical: 'audience_conditions', patterns: [ /audience\s+conditions?/i, /audience\s+targeting/i, /targeting/i, // MOVED HERE - targeting refers to audience targeting /qualifications?/i, /targeting\s+rules?/i, /targeting\s+conditions?/i, /who\s+qualifies/i, /visitor\s+targeting/i ], entity: 'rules', // CONSTRAINT TO RULES (primary usage in L3-1) dataType: 'json' }, // Additional audience conditions pattern for audiences table conditions_audience: { canonical: 'conditions', patterns: [ /audience\s+conditions?/i, /audience\s+targeting/i, /who\s+qualifies/i, /qualification\s+rules?/i ], entity: 'audiences', dataType: 'json' }, // Variation-specific fields weight: { canonical: 'weight', patterns: [ /weight/i, /allocation/i, /percentage/i, /distribution/i ], entity: 'variations', dataType: 'number' }, is_control: { canonical: 'is_control', patterns: [ /control/i, /baseline/i, /original/i, /default/i ], entity: 'variations', dataType: 'boolean' }, // Event-specific fields event_type: { canonical: 'event_type', patterns: [ /event\s+type/i, /type\s+of\s+event/i, /click|page\s+view|custom/i, /category/i ], entity: 'events', dataType: 'string' }, selector: { canonical: 'selector', patterns: [ /selector/i, /css\s+selector/i, /element/i, /target/i ], entity: 'events', dataType: 'string' }, // Project-specific fields platform: { canonical: 'platform', patterns: [ /platform/i, /type/i, /web|feature/i, /experimentation\s+type/i ], entity: 'projects', dataType: 'string' }, account_id: { canonical: 'account_id', patterns: [ /account(?:\s+id)?/i, /organization/i, /org(?:\s+id)?/i ], entity: 'projects', dataType: 'string' }, // Page-specific fields activation_type: { canonical: 'activation_type', patterns: [ /activation(?:\s+type)?/i, /activation\s+mode/i, /trigger(?:\s+type)?/i, /page\s+activation/i, /immediate|manual|polling|callback|dom_changed|url_changed/i ], entity: 'pages', dataType: 'string' }, 'url_targeting_pages': { canonical: 'conditions', patterns: [ /url\s+targeting(?:\s+activation)?/i, /url\s+trigger/i, /targeting\s+activation/i, /pages?\s+(?:that\s+)?use\s+url\s+targeting/i, /url\s+based\s+targeting/i, /url\s+conditions?/i ], entity: 'pages', dataType: 'json', jsonPath: '$[*].type' }, edit_url: { canonical: 'edit_url', patterns: [ /edit\s+url/i, /page\s+url/i, /target\s+url/i, /url/i ], entity: 'pages', dataType: 'string' }, category: { canonical: 'category', patterns: [ /category/i, /page\s+category/i, /type/i, /kind/i ], entity: 'pages', dataType: 'string' }, // Complex JSON path fields 'environments.production.enabled': { canonical: 'environments.production.enabled', patterns: [ /production\s+enabled/i, /enabled\s+in\s+production/i, /prod\s+status/i ], entity: 'flags', jsonPath: '$.environments.production.enabled', dataType: 'boolean' }, 'rules[0].percentage': { canonical: 'rules[0].percentage_included', patterns: [ /first\s+rule(?:'s)?\s+percentage/i, /rule\s+1\s+percentage/i, /initial\s+rule\s+traffic/i ], entity: 'flags', jsonPath: '$.rules[0].percentage_included', dataType: 'number' } }; /** * Common English stop words to ignore when extracting fields */ const STOP_WORDS = new Set([ 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'been', 'their', 'them', 'they', 'this', 'that', 'these', 'those', 'my', 'your', 'his', 'her', 'its', 'our', 'their', 'what', 'which', 'who', 'whom', 'whose', 'where', 'when', 'why', 'how', 'all', 'each', 'every', 'some', 'few', 'more', 'most', 'other', 'such', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 'just', 'being' ]); /** * Find matching fields in a query */ export function findFields(query, primaryEntity) { const matches = []; let normalizedQuery = query.toLowerCase(); // CRITICAL FIX: Handle compound phrases that refer to specific fields const compoundPhraseMap = { 'enabled status': 'enabled', 'activation status': 'enabled', 'active status': 'enabled', 'flag status': 'enabled', // In flag context, status usually means enabled/disabled 'environment status': 'enabled', 'rollout status': 'enabled', 'feature status': 'enabled' }; // Track which parts of the query we've already matched const matchedRanges = []; // First, check for compound phrases for (const [phrase, fieldName] of Object.entries(compoundPhraseMap)) { const phraseRegex = new RegExp(`\\b${phrase}\\b`, 'i'); const phraseMatch = normalizedQuery.match(phraseRegex); if (phraseMatch && phraseMatch.index !== undefined) { // Find the pattern for this field const pattern = FIELD_PATTERNS[fieldName]; if (pattern) { matches.push({ field: pattern.canonical, confidence: 0.95, // High confidence for explicit compound phrases match: phrase }); // Track this range as matched matchedRanges.push({ start: phraseMatch.index, end: phraseMatch.index + phrase.length }); } } } for (const [fieldName, pattern] of Object.entries(FIELD_PATTERNS)) { // Skip fields that are specific to other entities if (pattern.entity && primaryEntity && pattern.entity !== primaryEntity) { continue; } for (const regex of pattern.patterns) { const match = normalizedQuery.match(regex); if (match && match.index !== undefined) { // CRITICAL: Skip if this match overlaps with a compound phrase we already handled const matchStart = match.index; const matchEnd = match.index + match[0].length; let isOverlapping = false; for (const range of matchedRanges) { // Check if this match overlaps with any matched compound phrase if ((matchStart >= range.start && matchStart < range.end) || (matchEnd > range.start && matchEnd <= range.end) || (matchStart <= range.start && matchEnd >= range.end)) { isOverlapping = true; break; } } if (isOverlapping) { continue; } // Check if the matched text is a stop word const matchedText = match[0].trim(); const words = matchedText.split(/\s+/); // Skip if ANY word in the match is a stop word (more aggressive filtering) if (words.some(word => STOP_WORDS.has(word.toLowerCase()))) { continue; } // Higher confidence if entity matches const entityBonus = pattern.entity === primaryEntity ? 0.1 : 0; const confidence = 0.8 + entityBonus; matches.push({ field: pattern.canonical, confidence, match: match[0] }); break; // Only match once per field } } } return matches.sort((a, b) => b.confidence - a.confidence); } /** * Get canonical field name from various aliases */ export function getCanonicalFieldName(field, entity) { const normalizedField = field.toLowerCase(); // Direct match on canonical names for (const [_, pattern] of Object.entries(FIELD_PATTERNS)) { if (pattern.canonical.toLowerCase() === normalizedField) { return pattern.canonical; } } // Pattern matching for (const [_, pattern] of Object.entries(FIELD_PATTERNS)) { if (pattern.entity && entity && pattern.entity !== entity) { continue; } for (const regex of pattern.patterns) { if (regex.test(normalizedField)) { return pattern.canonical; } } } // Return as-is if no match return field; } //# sourceMappingURL=FieldPatterns.js.map