UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

832 lines (756 loc) 34.8 kB
/** * Plugin Pattern Examples * Common patterns for using custom functions in orchestration */ export const pluginPatterns = { // Campaign Configuration Patterns campaign_configuration: { external_segment_integration: { id: 'pattern_external_segments', name: 'External Customer Segment Integration', description: 'Fetch customer segments from CDP to configure audience targeting', template: { type: 'plugin', plugin: { code: ` const cdpEndpoint = parameters.cdp_api_endpoint; const apiKey = parameters.cdp_api_key; const campaignType = parameters.campaign_type; try { console.log('Fetching customer segments from CDP...'); const response = await api.fetch(cdpEndpoint + '/segments', { method: 'GET', headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error('CDP API returned ' + response.status); } const segments = JSON.parse(response.data); // Filter segments relevant to this campaign type const relevantSegments = segments.filter(segment => { if (campaignType === 'premium_features') { return segment.tier === 'premium' || segment.tier === 'enterprise'; } else if (campaignType === 'onboarding') { return segment.lifecycle_stage === 'new' || segment.lifecycle_stage === 'trial'; } return true; }); // Generate audience conditions for each segment const audienceConditions = relevantSegments.map(segment => ({ segment_id: segment.id, segment_name: segment.name, condition: { type: 'custom_attribute', name: 'customer_segment_id', match_type: 'exact', value: segment.id }, description: 'Targets ' + segment.name + ' (' + segment.size + ' users)' })); return { available_segments: relevantSegments, audience_conditions: audienceConditions, total_addressable_users: relevantSegments.reduce((sum, s) => sum + s.size, 0) }; } catch (error) { console.error('Failed to fetch segments:', error.message); return { available_segments: [], audience_conditions: [], error: 'Could not connect to CDP: ' + error.message }; } `, inputs: { cdp_api_endpoint: '${external_apis.cdp_base_url}', cdp_api_key: '${external_apis.cdp_token}', campaign_type: '${campaign_configuration.type}' }, outputs: ['available_segments', 'audience_conditions', 'total_addressable_users', 'error'], permissions: { external_requests: true }, timeout_ms: 15000 } }, usage: 'Configure audience targeting using external customer segments from CDP' }, pricing_tier_experience_generator: { id: 'pattern_pricing_tiers', name: 'Pricing Tier Experience Generator', description: 'Generate experiment variations based on current pricing tiers from external system', template: { type: 'plugin', plugin: { code: ` const pricingEndpoint = parameters.pricing_api_endpoint; const productId = parameters.product_id; const experimentType = parameters.experiment_type; try { // Fetch current pricing tiers from external system const response = await api.fetch(pricingEndpoint + '/tiers/' + productId, { method: 'GET', headers: { 'Authorization': 'Bearer ' + parameters.pricing_api_key, 'Content-Type': 'application/json' } }); const pricingData = JSON.parse(response.data); // Generate JavaScript code that will run for ALL users in each variation const generatePricingExperience = (tier) => { return \` // This code runs for ALL users assigned to this variation (function() { const tierConfig = \${JSON.stringify(tier)}; // Update pricing display for everyone in this variation const priceElements = document.querySelectorAll('.product-price'); priceElements.forEach(function(element) { element.innerHTML = '<span class="price">$' + tierConfig.price + '</span>'; if (tierConfig.discount > 0) { element.innerHTML += '<span class="discount">' + tierConfig.discount + '% off</span>'; } }); // Add tier-specific styling document.body.className += ' pricing-tier-' + tierConfig.name.toLowerCase(); // Track which tier this variation represents window.optimizely = window.optimizely || []; window.optimizely.push(['trackEvent', 'pricing_tier_shown', { tier: tierConfig.name, price: tierConfig.price }]); })(); \`; }; // Create variation configs for the experiment const variations = pricingData.tiers.map((tier, index) => ({ name: tier.name + ' Pricing (' + tier.price + ')', weight: tier.allocation_percentage * 100, // Convert to basis points javascript_code: generatePricingExperience(tier), metadata: { tier_id: tier.id, price: tier.price, discount: tier.discount } })); return { experiment_name: 'Pricing Tier Test - ' + pricingData.product_name, variations: variations, total_tiers: pricingData.tiers.length, expected_traffic_split: pricingData.tiers.map(t => t.name + ': ' + t.allocation_percentage + '%').join(', ') }; } catch (error) { console.error('Pricing API error:', error.message); return { error: 'Could not generate pricing experiment: ' + error.message, variations: [] }; } `, inputs: { pricing_api_endpoint: '${external_apis.pricing_service_url}', pricing_api_key: '${external_apis.pricing_token}', product_id: '${experiment_config.target_product}', experiment_type: 'pricing_test' }, outputs: ['experiment_name', 'variations', 'total_tiers', 'expected_traffic_split', 'error'], permissions: { external_requests: true } } }, usage: 'Generate pricing experiment variations based on external pricing tier configuration' }, score_calculation: { id: 'pattern_score_calculation', name: 'Engagement Score Calculator', description: 'Calculate user engagement scores', template: { type: 'plugin', plugin: { code: ` const activities = parameters.user_activities; let score = 0; // Weight different activities const weights = { page_view: 1, click: 2, form_submit: 5, purchase: 20, share: 10 }; for (const activity of activities) { score += (weights[activity.type] || 0) * activity.count; } // Normalize to 0-100 const normalizedScore = Math.min(100, Math.round(score / 10)); return { raw_score: score, normalized_score: normalizedScore, engagement_level: normalizedScore > 70 ? 'high' : normalizedScore > 30 ? 'medium' : 'low' }; `, inputs: { user_activities: '${activity_data}' }, outputs: ['raw_score', 'normalized_score', 'engagement_level'] } }, usage: 'Calculate engagement scores from user activity data' } }, // Content Generation Patterns content_generation: { extension_template_generator: { id: 'pattern_extension_generator', name: 'Extension Template Generator', description: 'Generate Optimizely extension code based on external content/configuration', template: { type: 'plugin', plugin: { code: ` const contentEndpoint = parameters.content_api_endpoint; const brandingEndpoint = parameters.branding_api_endpoint; const extensionType = parameters.extension_type; try { // Fetch current branding/content from external CMS const [contentResponse, brandingResponse] = await Promise.all([ api.fetch(contentEndpoint + '/campaign-content', { method: 'GET', headers: { 'Authorization': 'Bearer ' + parameters.cms_api_key } }), api.fetch(brandingEndpoint + '/current-theme', { method: 'GET', headers: { 'Authorization': 'Bearer ' + parameters.cms_api_key } }) ]); const content = JSON.parse(contentResponse.data); const branding = JSON.parse(brandingResponse.data); // Generate extension JavaScript that will run for ALL experiment visitors let extensionCode = ''; if (extensionType === 'banner_overlay') { extensionCode = \` // Banner overlay extension - runs for all visitors in this experiment (function() { var banner = document.createElement('div'); banner.id = 'experiment-banner'; banner.style.cssText = \` position: fixed; top: 0; left: 0; width: 100%; background: \${branding.primary_color}; color: \${branding.text_color}; padding: 12px; text-align: center; z-index: 10000; font-family: \${branding.font_family}; \`; banner.innerHTML = '\${content.banner_message}'; // Add close button var closeBtn = document.createElement('button'); closeBtn.innerHTML = '×'; closeBtn.style.cssText = 'float: right; background: none; border: none; color: inherit; font-size: 18px; cursor: pointer;'; closeBtn.onclick = function() { banner.remove(); }; banner.appendChild(closeBtn); document.body.insertBefore(banner, document.body.firstChild); // Track that banner was shown window.optimizely = window.optimizely || []; window.optimizely.push(['trackEvent', 'banner_shown', { campaign_id: '\${content.campaign_id}', message: '\${content.banner_message}' }]); })(); \`; } else if (extensionType === 'checkout_enhancement') { extensionCode = \` // Checkout enhancement - runs for all visitors on checkout pages (function() { // Wait for checkout form to load var checkInterval = setInterval(function() { var checkoutForm = document.querySelector('\${content.checkout_form_selector}'); if (checkoutForm) { clearInterval(checkInterval); // Add trust signals var trustDiv = document.createElement('div'); trustDiv.className = 'experiment-trust-signals'; trustDiv.innerHTML = \` <div style="background: \${branding.secondary_color}; padding: 15px; margin: 10px 0; border-radius: 5px;"> <h4 style="margin: 0 0 10px 0; color: \${branding.primary_color};">\${content.trust_heading}</h4> <ul style="margin: 0; padding-left: 20px; color: \${branding.text_color};"> \${content.trust_points.map(point => '<li>' + point + '</li>').join('')} </ul> </div> \`; checkoutForm.insertBefore(trustDiv, checkoutForm.firstChild); // Track enhancement shown window.optimizely.push(['trackEvent', 'checkout_enhancement_shown']); } }, 500); })(); \`; } return { extension_name: content.campaign_name + ' - ' + extensionType, extension_code: extensionCode, content_updated_at: content.last_modified, branding_theme: branding.theme_name, estimated_load_impact: 'Low - ~2KB additional' }; } catch (error) { console.error('Content generation failed:', error.message); return { extension_code: '// Error: Could not generate extension - ' + error.message, error: error.message }; } `, inputs: { content_api_endpoint: '${external_apis.cms_base_url}', branding_api_endpoint: '${external_apis.branding_service_url}', cms_api_key: '${external_apis.cms_token}', extension_type: '${experiment_config.extension_type}' }, outputs: ['extension_name', 'extension_code', 'content_updated_at', 'branding_theme', 'error'], permissions: { external_requests: true } } }, usage: 'Generate extension code with current content/branding for all experiment visitors' }, data_quality_check: { id: 'pattern_data_quality', name: 'Data Quality Checker', description: 'Check data quality before processing', template: { type: 'plugin', plugin: { code: ` const data = parameters.input_data; const issues = []; // Check for required fields const requiredFields = parameters.required_fields || []; for (const item of data) { for (const field of requiredFields) { if (!item[field]) { issues.push({ item_id: item.id || 'unknown', issue: \`Missing required field: \${field}\` }); } } } // Check data types const typeChecks = parameters.type_checks || {}; for (const item of data) { for (const [field, expectedType] of Object.entries(typeChecks)) { if (item[field] && typeof item[field] !== expectedType) { issues.push({ item_id: item.id || 'unknown', issue: \`Invalid type for \${field}: expected \${expectedType}\` }); } } } return { total_items: data.length, issue_count: issues.length, quality_score: Math.round((1 - issues.length / data.length) * 100), issues: issues.slice(0, 10), // First 10 issues passed: issues.length === 0 }; `, inputs: { input_data: '${data_to_validate}', required_fields: ['id', 'name', 'value'], type_checks: { value: 'number', active: 'boolean' } }, outputs: ['total_items', 'issue_count', 'quality_score', 'issues', 'passed'] } }, usage: 'Validate data quality before import or processing' } }, // Page Targeting Patterns page_targeting: { dynamic_product_targeting: { id: 'pattern_dynamic_product_targeting', name: 'Dynamic Product Page Targeting', description: 'Generate page targeting conditions based on current product catalog from external system', template: { type: 'plugin', plugin: { code: ` const catalogEndpoint = parameters.catalog_api_endpoint; const campaignType = parameters.campaign_type; const targetCategory = parameters.target_category; try { // Fetch current product catalog from external system const response = await api.fetch(catalogEndpoint + '/products', { method: 'GET', headers: { 'Authorization': 'Bearer ' + parameters.catalog_api_key, 'Content-Type': 'application/json' }, query: { category: targetCategory, active: true, campaign_eligible: true } }); const products = JSON.parse(response.data); // Generate page targeting conditions based on product URLs const pageConditions = []; if (campaignType === 'product_upsell') { // Target product detail pages const productUrls = products.map(p => p.url_path); pageConditions.push({ match_type: 'regex', value: '(' + productUrls.join('|') + ')', description: 'Targets ' + products.length + ' product pages in ' + targetCategory + ' category' }); // Also target category listing pages pageConditions.push({ match_type: 'substring', value: '/category/' + targetCategory.toLowerCase(), description: 'Targets category listing page' }); } else if (campaignType === 'cart_abandonment') { // Target pages where these products might be added to cart pageConditions.push({ match_type: 'simple', value: '/cart', description: 'Targets cart page' }); pageConditions.push({ match_type: 'simple', value: '/checkout', description: 'Targets checkout page' }); } // Generate JavaScript targeting conditions for complex scenarios const jsCondition = \` // Advanced targeting condition - checks if current page has eligible products (function() { var eligibleProductIds = \${JSON.stringify(products.map(p => p.id))}; var currentProductId = window.productData ? window.productData.id : null; // Check if current page is for an eligible product if (currentProductId && eligibleProductIds.indexOf(currentProductId) !== -1) { return true; } // Check if page has eligible products in listing var productCards = document.querySelectorAll('[data-product-id]'); for (var i = 0; i < productCards.length; i++) { var productId = productCards[i].getAttribute('data-product-id'); if (eligibleProductIds.indexOf(productId) !== -1) { return true; } } return false; })(); \`; return { page_conditions: pageConditions, javascript_condition: jsCondition, targeted_products: products.map(p => ({ id: p.id, name: p.name, url: p.url_path })), total_products: products.length, category_targeted: targetCategory }; } catch (error) { console.error('Catalog API error:', error.message); return { page_conditions: [{ match_type: 'simple', value: '/' + targetCategory.toLowerCase(), description: 'Fallback: Target category pages only' }], error: 'Could not fetch product catalog: ' + error.message }; } `, inputs: { catalog_api_endpoint: '${external_apis.catalog_service_url}', catalog_api_key: '${external_apis.catalog_token}', campaign_type: '${experiment_config.campaign_type}', target_category: '${experiment_config.product_category}' }, outputs: ['page_conditions', 'javascript_condition', 'targeted_products', 'total_products', 'error'], permissions: { external_requests: true } } }, usage: 'Configure page targeting based on current product catalog for campaign experiments' }, audience_converter: { id: 'pattern_audience_converter', name: 'Audience Format Converter', description: 'Convert between different audience condition formats', template: { type: 'plugin', plugin: { code: ` const sourceAudiences = parameters.source_audiences; const targetFormat = parameters.target_format; const converted = []; for (const audience of sourceAudiences) { let conditions; if (targetFormat === 'optimizely') { // Convert to Optimizely format conditions = []; for (const rule of audience.rules || []) { if (rule.attribute) { conditions.push({ type: 'custom_attribute', name: rule.attribute, match_type: rule.operator === '==' ? 'exact' : rule.operator, value: rule.value }); } else if (rule.event) { conditions.push({ type: 'behavior', event_name: rule.event, match_type: 'exists' }); } } converted.push({ key: audience.id.toLowerCase().replace(/\\W+/g, '_'), name: audience.name, conditions: conditions.length > 1 ? { and: conditions } : conditions[0] }); } } return { converted_audiences: converted, conversion_count: converted.length }; `, inputs: { source_audiences: '${audiences_to_convert}', target_format: 'optimizely' }, outputs: ['converted_audiences', 'conversion_count'] } }, usage: 'Convert audience definitions between different formats' } }, // Experiment Configuration Patterns experiment_configuration: { experiment_setup_validator: { id: 'pattern_experiment_validator', name: 'Experiment Configuration Validator', description: 'Validate experiment setup meets business requirements before launch', template: { type: 'plugin', plugin: { code: ` const endpoint = parameters.api_endpoint; const apiKey = parameters.api_key; const method = parameters.method || 'GET'; try { console.log(\`Calling \${method} \${endpoint}\`); const response = await api.fetch(endpoint, { method: method, headers: { 'Authorization': \`Bearer \${apiKey}\`, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: method !== 'GET' ? JSON.stringify(parameters.request_body) : undefined }); if (!response.ok) { throw new Error(\`API returned \${response.status}: \${response.statusText}\`); } const data = JSON.parse(response.data); return { success: true, data: data, status: response.status }; } catch (error) { console.error('API call failed:', error.message); return { success: false, error: error.message, data: null }; } `, inputs: { api_endpoint: '${external_api_url}', api_key: '${api_credentials.key}', method: 'GET', request_body: {} }, outputs: ['success', 'data', 'error', 'status'], permissions: { external_requests: true }, timeout_ms: 30000 } }, usage: 'Fetch data from external REST APIs' }, webhook_notifier: { id: 'pattern_webhook_notify', name: 'Webhook Notification Sender', description: 'Send notifications via webhook', template: { type: 'plugin', plugin: { code: ` const webhookUrl = parameters.webhook_url; const eventType = parameters.event_type; const eventData = parameters.event_data; const payload = { event: eventType, timestamp: new Date().toISOString(), data: eventData, source: 'optimizely_orchestration' }; try { const response = await api.fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Event-Type': eventType }, body: JSON.stringify(payload) }); console.log(\`Webhook notification sent: \${eventType}\`); return { sent: true, status: response.status, event_id: \`evt_\${Date.now()}\` }; } catch (error) { console.error('Failed to send webhook:', error.message); return { sent: false, error: error.message }; } `, inputs: { webhook_url: '${notification_webhook}', event_type: '${event_name}', event_data: '${event_payload}' }, outputs: ['sent', 'status', 'event_id', 'error'], permissions: { external_requests: true } } }, usage: 'Send webhook notifications for orchestration events' } }, // Orchestration Tracking Patterns orchestration_tracking: { campaign_setup_tracker: { id: 'pattern_campaign_tracker', name: 'Campaign Setup Progress Tracker', description: 'Track orchestration progress and log setup milestones', template: { type: 'plugin', plugin: { code: ` const counterKey = parameters.counter_name; const increment = parameters.increment || 1; // Get current count const currentCount = await api.getState(counterKey) || 0; const newCount = currentCount + increment; // Update count await api.setState(counterKey, newCount); console.log(\`Counter \${counterKey}: \${currentCount} -> \${newCount}\`); return { counter_name: counterKey, previous_count: currentCount, new_count: newCount, increment: increment }; `, inputs: { counter_name: 'execution_count', increment: 1 }, outputs: ['counter_name', 'previous_count', 'new_count', 'increment'], permissions: { write_state: true } } }, usage: 'Track how many times a workflow or step has executed' }, cache_manager: { id: 'pattern_cache', name: 'Temporary Cache Manager', description: 'Cache data between steps', template: { type: 'plugin', plugin: { code: ` const operation = parameters.operation; const cacheKey = parameters.cache_key; const ttl = parameters.ttl_seconds || 3600; if (operation === 'set') { const cacheData = { data: parameters.data, expires_at: Date.now() + (ttl * 1000), created_at: Date.now() }; await api.setState(cacheKey, cacheData); return { operation: 'set', success: true, expires_in_seconds: ttl }; } else if (operation === 'get') { const cached = await api.getState(cacheKey); if (!cached) { return { operation: 'get', found: false, data: null }; } if (cached.expires_at < Date.now()) { // Expired - clear it await api.setState(cacheKey, null); return { operation: 'get', found: false, expired: true, data: null }; } return { operation: 'get', found: true, data: cached.data, age_seconds: Math.round((Date.now() - cached.created_at) / 1000) }; } `, inputs: { operation: 'get', cache_key: 'temp_data', data: null, ttl_seconds: 3600 }, outputs: ['operation', 'success', 'found', 'data', 'expired', 'age_seconds'], permissions: { write_state: true } } }, usage: 'Cache temporary data with expiration' } } }; //# sourceMappingURL=PluginPatterns.js.map