UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

403 lines 19.7 kB
/** * Modification-Aware Error Enhancement System * @description Specialized error enhancement for entity modification workflows * * Addresses critical gap: Current system only handles entity creation errors. * This enhancer specifically handles modification operations like: * - Adding variations to existing flags * - Updating rulesets and rules * - Modifying traffic allocations * - Complex entity update workflows * * @author Optimizely MCP Server * @version 1.0.0 */ import { AutomatedPrescriptiveErrorEnhancer } from './AutomatedPrescriptiveErrorEnhancer.js'; import { getLogger } from '../logging/Logger.js'; /** * Specialized error enhancer for modification workflows */ export class ModificationAwareErrorEnhancer extends AutomatedPrescriptiveErrorEnhancer { constructor() { super(); // Add modification-specific patterns to the beginning of the pattern list // This ensures they're checked before generic patterns this.addModificationPatterns(); } /** * Enhanced error processing with modification workflow detection */ enhanceError(originalError, context = {}) { getLogger().debug({ originalError, context, isModification: context.isModificationWorkflow }, 'ModificationAwareErrorEnhancer: Processing error'); // Check if this is a modification workflow error first if (this.isModificationError(originalError, context)) { context.isModificationWorkflow = true; // Try modification-specific patterns const modificationResponse = this.processModificationError(originalError, context); if (modificationResponse) { return modificationResponse; } } // Fall back to base class for standard patterns return super.enhanceError(originalError, context); } /** * Add modification-specific error patterns */ addModificationPatterns() { // Access the protected patterns array via reflection const patterns = this.patterns; // Insert modification patterns at the beginning const modificationPatterns = [ { name: 'RulesetUpdateFailure', detect: (error, context) => this.detectRulesetError(error, context), enhance: (error, context) => this.enhanceRulesetError(error, context) }, { name: 'VariationAdditionWorkflow', detect: (error, context) => this.detectVariationWorkflowError(error, context), enhance: (error, context) => this.enhanceVariationWorkflowError(error, context) }, { name: 'EntityModificationConfusion', detect: (error, context) => this.detectModificationConfusion(error, context), enhance: (error, context) => this.enhanceModificationGuidance(error, context) }, { name: 'TrafficAllocationError', detect: (error, context) => this.detectTrafficError(error, context), enhance: (error, context) => this.enhanceTrafficError(error, context) } ]; // Insert at the beginning of patterns array patterns.unshift(...modificationPatterns); } /** * Detect if this is a modification workflow error */ isModificationError(error, context) { // Operation-based detection if (context.operation === 'update' || context.operation === 'delete') { return true; } // Entity-specific modification indicators const modificationIndicators = [ 'ruleset', 'rule', 'variation', 'traffic', 'allocation', 'HTTP 400', 'HTTP 405', 'HTTP 409', 'Method Not Allowed', 'already exists', 'cannot be modified', 'invalid state' ]; return modificationIndicators.some(indicator => error.toLowerCase().includes(indicator.toLowerCase())); } /** * Process modification-specific errors */ processModificationError(error, context) { // Detect workflow context if not provided if (!context.workflowContext) { context.workflowContext = this.detectWorkflowContext(error, context); } // Check each modification pattern explicitly if (this.detectRulesetError(error, context)) { return this.enhanceRulesetError(error, context); } if (this.detectVariationWorkflowError(error, context)) { return this.enhanceVariationWorkflowError(error, context); } if (this.detectModificationConfusion(error, context)) { return this.enhanceModificationGuidance(error, context); } if (this.detectTrafficError(error, context)) { return this.enhanceTrafficError(error, context); } return null; } /** * Detect workflow context from error patterns */ detectWorkflowContext(error, context) { // Pattern: Variation creation followed by ruleset errors if (context.entityType === 'variation' || error.includes('variation')) { return { detectedWorkflow: 'add_variation_to_existing_flag', relatedEntities: ['flag', 'variation', 'ruleset'], modificationTarget: 'variation_addition' }; } // Pattern: Ruleset/rule update errors if (context.entityType === 'ruleset' || context.entityType === 'rule') { return { detectedWorkflow: 'update_targeting_rules', relatedEntities: ['flag', 'ruleset', 'rule'], modificationTarget: 'traffic_allocation' }; } // Pattern: Traffic allocation errors if (error.includes('traffic') || error.includes('allocation')) { return { detectedWorkflow: 'redistribute_traffic', relatedEntities: ['ruleset', 'variation'], modificationTarget: 'traffic_distribution' }; } return { detectedWorkflow: 'generic_modification', relatedEntities: [context.entityType || 'unknown'], modificationTarget: 'unknown' }; } // Pattern Detection Methods detectRulesetError(error, context) { const rulesetIndicators = [ // Direct ruleset/rule mentions context.entityType === 'ruleset' || context.entityType === 'rule', error.toLowerCase().includes('ruleset'), error.toLowerCase().includes('rule'), // REMOVED: Overly broad HTTP error detection that was catching ALL update errors // Only check for ruleset-specific API patterns error.includes('/rulesets') || error.includes('/rules'), error.includes('/environments/') && error.includes('/ruleset'), // More specific targeting errors that mention rules error.includes('targeting') && (error.includes('rule') || error.includes('ruleset')), error.includes('allocation') && (error.includes('rule') || error.includes('variation')) ]; return rulesetIndicators.some(indicator => indicator); } detectVariationWorkflowError(error, context) { const variationIndicators = [ // Creating variation on existing flag - key indicator is flag_key in metadata/options context.operation === 'create' && context.entityType === 'variation' && (context.metadata?.flag_key || context.metadata?.existing_flag), // Creating variation with flag context (most common pattern) context.operation === 'create' && context.entityType === 'variation' && context.metadata && Object.keys(context.metadata).some(key => key.includes('flag') || key.includes('environment')), // Errors mentioning variations in context of existing entities error.includes('variation') && (error.includes('already') || error.includes('existing')), // Traffic allocation errors during variation operations error.includes('variation') && error.includes('traffic'), // API errors when trying to create variations on existing flags error.includes('variations') && (error.includes('400') || error.includes('Bad Request')), // Workflow sequence indicators context.workflowContext?.detectedWorkflow === 'add_variation_to_existing_flag' ]; return variationIndicators.some(indicator => indicator); } detectModificationConfusion(error, context) { // CRITICAL: Don't enhance already well-formatted error messages // Check if error message is already properly structured with REASON and SUGGESTED ACTIONS if (error.includes('REASON:') && error.includes('SUGGESTED ACTIONS:')) { getLogger().info({ errorPattern: 'Well-formatted error detected', entityType: context.entityType, operation: context.operation }, 'ModificationAwareErrorEnhancer: Skipping enhancement for well-formatted error'); return false; } const confusionIndicators = [ // Update operations that are failing due to workflow misunderstanding context.operation === 'update' && (error.includes('not found') || error.includes('invalid') || error.includes('cannot')), // Complex entity modifications without proper workflow this.isComplexModificationWorkflow(context), // Multiple entity references in error (indicates complex workflow) this.countEntityMentions(error) > 1 ]; return confusionIndicators.some(indicator => indicator); } detectTrafficError(error, context) { const trafficIndicators = [ error.includes('traffic'), error.includes('allocation'), error.includes('percentage'), error.includes('basis points'), error.includes('weight'), error.includes('100%') || error.includes('10000'), error.includes('sum') && error.includes('allocation') ]; return trafficIndicators.some(indicator => indicator); } // Enhancement Methods enhanceRulesetError(error, context) { return { status: 'WORKFLOW_GUIDANCE_REQUIRED', error: { code: 'RULESET_MODIFICATION_ERROR', message: 'Ruleset/rule operations require understanding of targeting hierarchy and proper workflow' }, required_action: { tool: 'get_entity_templates', parameters: { entity_type: 'ruleset_concepts', project_id: context.projectId, flag_key: context.metadata?.flag_key, environment_key: context.metadata?.environment_key } }, immediate_guidance: { critical_rule: 'ANY error with rules/rulesets MUST use get_entity_templates for workflow guidance', why_templates_required: 'Rules and rulesets have complex hierarchy and API patterns that require step-by-step guidance', next_step: 'Call get_entity_templates(entity_type="ruleset_concepts") immediately' }, workflow_hints: [ 'Rulesets contain rules - update the ruleset, not individual rules', 'Each flag+environment has exactly one ruleset', 'Use PATCH method for ruleset updates, not POST or PUT', 'Always provide flag_key and environment_key for ruleset operations' ], api_guidance: { correct_pattern: 'GET ruleset → modify rules array → PATCH entire ruleset', common_mistakes: [ 'Trying to POST individual rules', 'Using wrong HTTP method', 'Missing flag_key or environment_key context', 'Modifying rules without updating parent ruleset' ] }, documentation_link: 'Call get_entity_templates(entity_type="ruleset_concepts") for complete guidance' }; } enhanceVariationWorkflowError(error, context) { return { status: 'WORKFLOW_GUIDANCE_REQUIRED', error: { code: 'VARIATION_ADDITION_WORKFLOW_ERROR', message: 'Adding variation to existing A/B test requires specific multi-step workflow' }, required_action: { tool: 'get_entity_templates', parameters: { entity_type: 'flag_variation_addition', project_id: context.projectId, flag_key: context.metadata?.flag_key || context.metadata?.existing_flag } }, immediate_guidance: { critical_rule: 'Adding variations requires BOTH variation creation AND ruleset updates', why_templates_required: 'Variation addition involves coordination between multiple entities with specific sequencing', next_step: 'Call get_entity_templates(entity_type="flag_variation_addition") for complete workflow' }, workflow_explanation: { title: 'Adding Variation to Existing A/B Test', required_steps: [ '1. Create the new variation definition', '2. Update the ruleset to include new variation', '3. Redistribute traffic allocation across all variations', '4. Validate total allocation equals 100%' ], critical_concepts: { 'variations_vs_rulesets': 'Variations define options; rulesets control traffic allocation', 'sequencing': 'Create variation first, then update ruleset - never the reverse', 'traffic_math': 'Total allocation must equal 10000 basis points (100%)' } }, common_errors: { 'variation_without_ruleset': 'Created variation but forgot to update ruleset', 'wrong_sequence': 'Tried to update ruleset before creating variation', 'traffic_mismatch': 'Traffic allocation doesn\'t sum to 100%' }, documentation_link: 'Call get_entity_templates(entity_type="flag_variation_addition") for step-by-step guide' }; } enhanceModificationGuidance(error, context) { const workflowType = context.workflowContext?.detectedWorkflow || 'entity_modification'; return { status: 'WORKFLOW_GUIDANCE_REQUIRED', error: { code: 'COMPLEX_MODIFICATION_WORKFLOW', message: `Complex entity modification workflow detected - requires guided approach` }, required_action: { tool: 'get_entity_templates', parameters: { entity_type: workflowType, project_id: context.projectId } }, immediate_guidance: { critical_rule: 'Complex entity modifications require workflow templates for success', detected_workflow: workflowType, next_step: `Call get_entity_templates(entity_type="${workflowType}") for guidance` }, modification_context: { target_entity: context.entityType || 'unknown', modification_type: context.modificationDetails?.modificationTarget || 'unknown', related_entities: context.workflowContext?.relatedEntities || [], complexity_reason: 'Multi-entity workflow with dependencies and sequencing requirements' }, workflow_hints: [ 'Entity modifications often require updating multiple related entities', 'Order of operations matters for complex modifications', 'Use templates to understand complete workflow before starting', 'Template mode can handle complex modifications automatically' ], documentation_link: `Call get_entity_templates(entity_type="${workflowType}") for workflow guidance` }; } enhanceTrafficError(error, context) { return { status: 'WORKFLOW_GUIDANCE_REQUIRED', error: { code: 'TRAFFIC_ALLOCATION_ERROR', message: 'Traffic allocation errors require understanding of distribution rules and calculations' }, required_action: { tool: 'get_entity_templates', parameters: { entity_type: 'traffic_allocation_concepts', project_id: context.projectId } }, immediate_guidance: { critical_rule: 'Traffic allocation must always sum to exactly 100% (10000 basis points)', common_mistake: 'Modifying one variation without adjusting others', next_step: 'Call get_entity_templates(entity_type="traffic_allocation_concepts") for calculation guide' }, traffic_rules: { basis_points: '10000 basis points = 100% traffic', equal_split_2: '5000, 5000 (50%, 50%)', equal_split_3: '3333, 3333, 3334 (33.33%, 33.33%, 33.34%)', validation: 'Sum must equal exactly 10000 - no more, no less' }, calculation_help: [ 'For N equal variations: Math.floor(10000/N) for N-1 variations, remainder for last', 'For custom splits: Convert percentages to basis points (multiply by 100)', 'Always validate sum equals 10000 before submitting', 'Remainder goes to last variation to handle rounding' ], documentation_link: 'Call get_entity_templates(entity_type="traffic_allocation_concepts") for examples' }; } // Helper Methods isComplexModificationWorkflow(context) { const complexIndicators = [ // Multiple entities involved context.workflowContext?.relatedEntities?.length && context.workflowContext.relatedEntities.length > 1, // Known complex modification types ['variation', 'ruleset', 'rule', 'campaign'].includes(context.entityType || ''), // Modification involves dependencies context.modificationDetails?.existingState !== undefined, // Known complex workflow patterns ['add_variation_to_existing_flag', 'update_targeting_rules', 'redistribute_traffic'].includes(context.workflowContext?.detectedWorkflow || '') ]; return complexIndicators.some(indicator => indicator); } countEntityMentions(error) { const entityTypes = [ 'flag', 'variation', 'ruleset', 'rule', 'experiment', 'campaign', 'page', 'event', 'metric', 'audience', 'attribute' ]; return entityTypes.reduce((count, entity) => { return count + (error.toLowerCase().includes(entity) ? 1 : 0); }, 0); } } /** * Singleton instance for global use */ export const modificationAwareErrorEnhancer = new ModificationAwareErrorEnhancer(); //# sourceMappingURL=ModificationAwareErrorEnhancer.js.map