@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
346 lines • 13.3 kB
JavaScript
/**
* ContextInferrer - Infers context and disambiguates queries
*
* IMPLEMENTATION STATUS:
* COMPLETE: Platform detection, field disambiguation, relationship inference
*
* Last Updated: July 3, 2025
*/
import { ENTITY_PATTERNS } from '../patterns/EntityPatterns.js';
export class ContextInferrer {
/**
* Infer complete context from query components
*/
inferContext(query, primaryEntity, relatedEntities, filters, fields) {
const context = {
platform: this.inferPlatform(primaryEntity, relatedEntities, query),
primaryIntent: this.inferPrimaryIntent(query, primaryEntity),
impliedEntities: this.inferImpliedEntities(query, primaryEntity, filters),
impliedFilters: this.inferImpliedFilters(query, primaryEntity),
impliedJoins: this.inferImpliedJoins(primaryEntity, relatedEntities, filters, fields),
disambiguations: this.disambiguateTerms(query, primaryEntity)
};
return context;
}
/**
* Infer platform from entities and query terms
*/
inferPlatform(primaryEntity, relatedEntities, query) {
const allEntities = [primaryEntity, ...relatedEntities];
// Check entity platforms
const webEntities = allEntities.filter(e => ENTITY_PATTERNS[e]?.platform === 'web');
const featureEntities = allEntities.filter(e => ENTITY_PATTERNS[e]?.platform === 'feature');
// Check query terms
const hasWebTerms = /experiment|variation|campaign|page|personalization/i.test(query);
const hasFeatureTerms = /flag|feature|toggle|rollout|rule/i.test(query);
// Platform-specific terms take precedence
if (hasWebTerms && !hasFeatureTerms)
return 'web';
if (hasFeatureTerms && !hasWebTerms)
return 'feature';
// Entity platforms
if (webEntities.length > 0 && featureEntities.length === 0)
return 'web';
if (featureEntities.length > 0 && webEntities.length === 0)
return 'feature';
return 'both';
}
/**
* Infer the primary intent of the query
*/
inferPrimaryIntent(query, primaryEntity) {
const normalizedQuery = query.toLowerCase();
// Analysis intents
if (/analyz|insight|report|metric|performance/i.test(normalizedQuery)) {
return 'analysis';
}
// Comparison intents
if (/compar|versus|vs|difference|between/i.test(normalizedQuery)) {
return 'comparison';
}
// Troubleshooting intents
if (/why|issue|problem|not working|broken|fail/i.test(normalizedQuery)) {
return 'troubleshooting';
}
// Discovery intents
if (/which|what|where|find|search/i.test(normalizedQuery)) {
return 'discovery';
}
// Monitoring intents
if (/status|state|current|active|enabled/i.test(normalizedQuery)) {
return 'monitoring';
}
return 'general';
}
/**
* Infer entities that should be included based on context
*/
inferImpliedEntities(query, primaryEntity, filters) {
const implied = [];
// Flag queries with environment filters imply flag_environments
if (primaryEntity === 'flags' &&
(filters.some(f => f.field === 'enabled') || /environment/i.test(query))) {
implied.push('flag_environments');
}
// Experiment queries with metrics imply results
if (primaryEntity === 'experiments' && /metric|result|performance|conversion/i.test(query)) {
implied.push('results');
}
// Audience targeting only implies audiences if we're actually querying audience data
// NOT when we're just filtering on the JSON field audience_conditions
if (/audience\s+(name|id|description)|all\s+audiences|list\s+audiences/i.test(query)) {
implied.push('audiences');
}
// Note: audience_conditions is a JSON field in rules/experiments, not a reason to JOIN audiences table
// Rule-related queries
if (/rule|targeting|rollout/i.test(query) && primaryEntity === 'flags') {
implied.push('rules');
if (/ruleset/i.test(query)) {
implied.push('rulesets');
}
}
// Project context
if (/project|workspace/i.test(query) && !implied.includes('projects')) {
implied.push('projects');
}
return [...new Set(implied)]; // Remove duplicates
}
/**
* Infer filters based on common patterns
*/
inferImpliedFilters(query, primaryEntity) {
const filters = [];
// Active/enabled status
if (/\bactive\b/i.test(query) && !/(inactive|not active)/i.test(query)) {
if (primaryEntity === 'flags') {
filters.push({
field: 'enabled',
operator: '=',
value: true,
confidence: 0.8
});
}
else {
filters.push({
field: 'status',
operator: '=',
value: 'active',
confidence: 0.8
});
}
}
// Archived status
if (/\barchived\b/i.test(query) && !/(not archived|unarchived)/i.test(query)) {
filters.push({
field: 'archived',
operator: '=',
value: true,
confidence: 0.9
});
}
// Production environment
if (/\bproduction\b|\bprod\b/i.test(query)) {
filters.push({
field: 'environment_key',
operator: '=',
value: 'production',
confidence: 0.9
});
}
// Recent items
if (/\brecent\b|\bnew\b|\blatest\b/i.test(query)) {
filters.push({
field: 'created_time',
operator: '>',
value: 'NOW() - INTERVAL 7 DAY',
confidence: 0.7
});
}
// High traffic/percentage
if (/\bhigh traffic\b|\bhigh percentage\b/i.test(query)) {
filters.push({
field: 'percentage_included',
operator: '>',
value: 7500, // 75% in basis points
confidence: 0.7
});
}
return filters;
}
/**
* Infer joins based on entity relationships and filters
*/
inferImpliedJoins(primaryEntity, relatedEntities, filters, fields) {
const joins = [];
// Flag environment joins
const needsFlagEnvironmentJoin = primaryEntity === 'flags' && (filters.some(f => ['enabled', 'environment_key', 'rollout_percentage'].includes(f.field) ||
f.field.includes('flag_environments.enabled') ||
f.field.includes('flag_environments.environment_key') ||
f.field.includes('flag_environments.percentage_included')) ||
(fields && fields.some(field => field.includes('flag_environments.') ||
['enabled', 'environment_key'].includes(field.split('.').pop() || ''))));
if (needsFlagEnvironmentJoin) {
joins.push({
type: 'LEFT',
entity: 'flag_environments',
on: {
leftField: 'flags.key',
rightField: 'flag_environments.flag_key'
}
});
}
// Experiment variation joins
// L2-2 FIX: Don't add variations JOIN for flags - variations are JSON in flags table
if (primaryEntity === 'experiments' &&
(relatedEntities.includes('variations') || filters.some(f => f.field.includes('variation')))) {
joins.push({
type: 'LEFT',
entity: 'variations',
on: {
leftField: 'experiments.id',
rightField: 'variations.experiment_id'
}
});
}
// L2-2 FIX: Don't add variations JOIN for flags either
if (primaryEntity === 'flags' &&
(relatedEntities.includes('variations') || filters.some(f => f.field.includes('variation')))) {
// Variations are stored as JSON in the flags table, not a separate entity
// L2-2 FIX: Skipping variations JOIN for flags - variations are JSON column
}
// Rule audience joins
if ((primaryEntity === 'rules' || relatedEntities.includes('rules')) &&
filters.some(f => f.field === 'audience_conditions')) {
joins.push({
type: 'LEFT',
entity: 'audiences',
on: {
leftField: 'rules.audience_conditions',
rightField: 'audiences.id'
}
});
}
return joins;
}
/**
* Disambiguate ambiguous terms
*/
disambiguateTerms(query, primaryEntity) {
const disambiguations = [];
// "status" can mean different things
if (/\bstatus\b/i.test(query)) {
if (primaryEntity === 'flags') {
disambiguations.push({
term: 'status',
interpretation: 'enabled/disabled state in environments',
confidence: 0.9
});
}
else if (primaryEntity === 'experiments') {
disambiguations.push({
term: 'status',
interpretation: 'experiment state (running/paused/archived)',
confidence: 0.9
});
}
}
// "active" interpretation
if (/\bactive\b/i.test(query)) {
if (primaryEntity === 'flags') {
disambiguations.push({
term: 'active',
interpretation: 'enabled in at least one environment',
confidence: 0.85
});
}
else {
disambiguations.push({
term: 'active',
interpretation: 'not archived and currently running',
confidence: 0.85
});
}
}
// "traffic" interpretation
if (/\btraffic\b/i.test(query)) {
if (primaryEntity === 'flags') {
disambiguations.push({
term: 'traffic',
interpretation: 'rollout percentage in rules',
confidence: 0.9
});
}
else if (primaryEntity === 'experiments') {
disambiguations.push({
term: 'traffic',
interpretation: 'traffic allocation to variations',
confidence: 0.9
});
}
}
// "results" interpretation
if (/\bresults?\b/i.test(query)) {
if (primaryEntity === 'experiments') {
disambiguations.push({
term: 'results',
interpretation: 'experiment metrics and statistical results',
confidence: 0.95
});
}
else {
disambiguations.push({
term: 'results',
interpretation: 'query output/data',
confidence: 0.7
});
}
}
return disambiguations;
}
/**
* Resolve field ambiguity based on context
*/
resolveFieldAmbiguity(field, primaryEntity, relatedEntities) {
// Common ambiguous fields - using partial mapping
const ambiguousFields = {
'name': {
'flags': 'flags.name',
'experiments': 'experiments.name',
'audiences': 'audiences.name',
'variations': 'variations.name'
},
'status': {
'flags': 'flag_environments.enabled',
'experiments': 'experiments.status',
'campaigns': 'campaigns.status'
},
'created': {
'flags': 'flags.created_time',
'experiments': 'experiments.created',
'events': 'events.created'
}
};
// Check if field needs disambiguation
const normalizedField = field.toLowerCase();
if (ambiguousFields[normalizedField]) {
const entityMap = ambiguousFields[normalizedField];
// Use primary entity first
if (entityMap[primaryEntity]) {
return entityMap[primaryEntity];
}
// Check related entities
for (const entity of relatedEntities) {
if (entityMap[entity]) {
return entityMap[entity];
}
}
}
// Check if field already has table prefix
if (field.includes('.')) {
return field;
}
// Add primary entity prefix for safety
return `${primaryEntity}.${field}`;
}
}
//# sourceMappingURL=ContextInferrer.js.map