@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
493 lines • 20.6 kB
JavaScript
/**
* Universal Response Wrapper for Optimizely MCP Tools
* @description Provides consistent response formatting with success indicators,
* metadata, and performance tracking across all MCP tools while preserving
* existing business logic response structures.
*
* DESIGN PRINCIPLES:
* - Additive, not transformative: Wraps existing responses without breaking them
* - Universal compatibility: Works with all 26 tool response patterns
* - Neural-first: Places critical metadata in high-attention zones
* - Backward compatible: Preserves existing response structures
* - Progressive enhancement: Layers additional metadata as needed
*
* @pattern Decorator Pattern - Enhances responses without modifying core logic
* @version 1.0.0
*/
export const SuccessStopType = {
COMPREHENSIVE_DATA_PROVIDED: 'COMPREHENSIVE_DATA_PROVIDED',
MULTI_PROJECT_COMPLETE_DATASET: 'MULTI_PROJECT_COMPLETE_DATASET',
TEMPLATE_GUIDANCE_PROVIDED: 'TEMPLATE_GUIDANCE_PROVIDED',
PAGINATION_COMPLETE: 'PAGINATION_COMPLETE',
THRESHOLD_EXCEEDED: 'THRESHOLD_EXCEEDED'
};
export class ResponseWrapper {
/**
* Detects if a success hard stop should be triggered
* @param data - The response data
* @param options - Wrapping configuration options
* @returns Success stop configuration or null
*/
static detectSuccessStop(data, options) {
if (!options.enable_success_stops) {
return null;
}
const { tool_name, project_context } = options;
// Large dataset criteria (50+ results)
const resultCount = this.extractResultCount(data);
if (resultCount && resultCount >= 50) {
return {
type: SuccessStopType.COMPREHENSIVE_DATA_PROVIDED,
reason: 'LARGE_RESULT_SET_PROVIDED',
message: `Complete ${tool_name} results retrieved (${resultCount} items). You have all available data. Process these results instead of making additional queries.`
};
}
// Multi-project complete query
if (project_context?.is_multi_project && (project_context?.projects_queried || 0) > 1) {
return {
type: SuccessStopType.MULTI_PROJECT_COMPLETE_DATASET,
reason: 'MULTI_PROJECT_COMPLETE_DATASET',
message: `Complete multi-project query (${project_context.projects_queried} projects). You have comprehensive data across all configured projects. Process these results instead of making additional queries.`
};
}
// Template mode guidance provided
if (data && typeof data === 'object' && data.template_mode?.level === 'warning') {
return {
type: SuccessStopType.TEMPLATE_GUIDANCE_PROVIDED,
reason: 'TEMPLATE_MODE_GUIDANCE_READY',
message: `Template mode guidance provided. Follow the template instructions instead of making additional queries.`
};
}
// Tool-specific thresholds
switch (tool_name) {
case 'list_experiments':
if ((resultCount && resultCount >= 25) || project_context?.query_mode === 'all_projects') {
return {
type: SuccessStopType.COMPREHENSIVE_DATA_PROVIDED,
reason: 'MODE_all_projects_COMPLETE',
message: `STOP querying list_experiments; mode=all_projects is complete. You have comprehensive experiment data. Analyze these results instead of making additional queries.`
};
}
break;
case 'list_flags':
if ((resultCount && resultCount >= 75) || project_context?.query_mode === 'all_projects') {
return {
type: SuccessStopType.COMPREHENSIVE_DATA_PROVIDED,
reason: 'FLAG_DATASET_COMPLETE',
message: `Complete flag dataset retrieved. You have comprehensive flag data. Analyze these results instead of making additional queries.`
};
}
break;
case 'search_all':
if (resultCount && resultCount >= 30) {
return {
type: SuccessStopType.COMPREHENSIVE_DATA_PROVIDED,
reason: 'SEARCH_RESULTS_COMPREHENSIVE',
message: `Comprehensive search results retrieved. You have extensive search data. Analyze these results instead of making additional queries.`
};
}
break;
}
return null;
}
/**
* Creates a success hard stop response with business data at root level
* @param successStop - Success stop configuration
* @param data - Original data
* @param toolName - Tool name
* @returns Success hard stop response with data spread at root level
*/
static createSuccessHardStop(successStop, data, toolName) {
const baseResponse = {
// ===== HIGH-ATTENTION ZONE (First 200 chars) =====
hard_stop: 'STOP_YOU_HAVE_ALL_DATA',
required_action: 'PROCESS_DATA',
forbidden_actions: ['retry_query', 'make_additional_calls', 'request_more_details'],
critical_rule: 'STOP querying. Process the comprehensive data provided.',
// ===== OPERATION METADATA =====
success: {
code: 200,
type: successStop.type,
message: successStop.message,
reason: successStop.reason,
comprehensive: true
},
immediate_guidance: {
critical_rule: 'STOP querying list_experiments; mode=all_projects is complete.',
detected_situation: `${successStop.reason} - all available data retrieved`,
next_step: 'Analyze results and present organized information to user'
}
};
// Spread business data at root level alongside hard stop fields
if (data && typeof data === 'object' && !Array.isArray(data)) {
// Build result with specific field ordering
const result = {};
const businessData = data;
// 1. First, add all hard stop fields
Object.assign(result, baseResponse);
// 2. Then add business metadata fields (before experiments array)
if ('total' in businessData)
result.total = businessData.total;
if ('summary' in businessData)
result.summary = businessData.summary;
if ('filters_applied' in businessData)
result.filters_applied = businessData.filters_applied;
if ('_metadata' in businessData)
result._metadata = businessData._metadata;
// 3. Then add the main data array (experiments, flags, etc.)
if ('experiments' in businessData)
result.experiments = businessData.experiments;
if ('flags' in businessData)
result.flags = businessData.flags;
if ('audiences' in businessData)
result.audiences = businessData.audiences;
if ('events' in businessData)
result.events = businessData.events;
// 4. Finally, add any remaining fields
Object.keys(businessData).forEach(key => {
if (!(key in result)) {
result[key] = businessData[key];
}
});
return result;
}
else {
// For non-object data, still put it in data field
return { ...baseResponse, data };
}
}
/**
* Wraps any tool response with consistent success indicators and metadata
* @param data - The original business logic response
* @param options - Wrapping configuration options
* @returns Enhanced response with metadata at top, data preserved
*/
static wrap(data, options) {
const endTime = Date.now();
const startTime = options.start_time || endTime;
// Check for success hard stop conditions first
const successStop = this.detectSuccessStop(data, options);
if (successStop) {
return this.createSuccessHardStop(successStop, data, options.tool_name);
}
// Determine result count from common patterns
const resultCount = this.extractResultCount(data);
// Determine query mode for MODE pattern tools
const queryMode = this.determineQueryMode(data, options.project_context);
// Check for hard stop requirements
const hasHardStop = this.checkForHardStop(data);
// Determine status
const status = this.determineStatus(data, hasHardStop);
const wrapped = {
// High-attention metadata (first 200 chars)
status,
tool: options.tool_name,
...(resultCount !== undefined && { result_count: resultCount }),
// Mode-specific metadata
...(queryMode && { query_mode: queryMode }),
...(options.project_context?.projects_queried && {
projects_queried: options.project_context.projects_queried
}),
...(options.operation_type && { operation_type: options.operation_type }),
// Performance metrics
timing: {
duration_ms: endTime - startTime,
started_at: new Date(startTime).toISOString(),
completed_at: new Date(endTime).toISOString()
},
// Preserved business data
data,
// Wrapper metadata
_wrapper_metadata: {
wrapper_version: '1.0.0',
enhanced_response: true,
original_structure_preserved: true,
applied_patterns: this.getAppliedPatterns(options)
}
};
return wrapped;
}
/**
* Enhanced wrapper for tools using the MODE pattern
* Provides neural-first metadata for all_projects vs single_project operations
*/
static wrapWithModePattern(data, toolName, projectContext, startTime) {
return this.wrap(data, {
tool_name: toolName,
operation_type: 'list',
start_time: startTime,
project_context: projectContext,
enhance_for_mode_pattern: true,
enable_success_stops: true // Enable success stops for MODE pattern tools
});
}
/**
* Enhanced wrapper with explicit success stop enablement
* Use this when you specifically want to prevent repetitive tool calls
*/
static wrapWithSuccessStops(data, toolName, projectContext, startTime) {
return this.wrap(data, {
tool_name: toolName,
operation_type: 'list',
start_time: startTime,
project_context: projectContext,
enable_success_stops: true
});
}
/**
* Lightweight wrapper for simple success/failure indication
* Minimal overhead for system tools that don't need full enhancement
*/
static wrapSimple(data, toolName, operationType) {
return {
status: 'success',
tool: toolName,
...(operationType && { operation_type: operationType }),
timing: {
duration_ms: 0,
started_at: new Date().toISOString(),
completed_at: new Date().toISOString()
},
data
};
}
/**
* Wraps response with AI metadata for guiding AI agents
* Preserves root structure for backward compatibility
* @param data - The original response
* @param options - Wrapper options including AI metadata
* @returns Response with _ai_meta added
*/
static wrapWithAIMeta(data, options) {
const endTime = Date.now();
const startTime = options.start_time || endTime;
// Build comprehensive AI metadata
const aiMeta = {
// Core metadata - always included
cursor_notice: "Ignore Cursor's conversation history panel",
tool_status: this.determineToolStatus(data),
source: "optimizely-mcp-server",
timestamp: new Date().toISOString(),
// Merge in any custom AI metadata
...options.aiMeta
};
// Add automatic guidance based on data patterns
const autoGuidance = this.generateAutoGuidance(data, options);
if (autoGuidance) {
Object.assign(aiMeta, autoGuidance);
}
// For backward compatibility, preserve root structure
if (options.preserve_root_structure !== false) {
// Spread original data at root level, add _ai_meta
return {
_ai_meta: aiMeta,
...data
};
}
// New structure with data nested (opt-in)
return {
_ai_meta: aiMeta,
data: data
};
}
/**
* Determines tool status from response data
*/
static determineToolStatus(data) {
if (!data || typeof data !== 'object') {
return 'SUCCESS';
}
// Check for errors
if (data._isError || data.error || data.status === 'error') {
return 'ERROR';
}
// Check for warnings
if (data.template_mode || data._usage_guidance || data.status === 'warning') {
return 'WARNING';
}
return 'SUCCESS';
}
/**
* Generates automatic AI guidance based on response patterns
*/
static generateAutoGuidance(data, options) {
const guidance = {};
// Check for large result sets
const resultCount = this.extractResultCount(data);
if (resultCount && resultCount >= 50) {
guidance.operation_hints = {
complexity: 'simple',
data_comprehensive: true,
no_further_calls_needed: true
};
guidance.avoid_common_mistakes = [
"Don't make additional calls - you have all data",
"Focus on analyzing the results provided"
];
}
// Check for template mode
if (data && typeof data === 'object' && data.template_mode) {
guidance.template_mode = {
required: true,
reason: data.template_mode.message || "Complex entity requires template",
instruction: "Use get_entity_templates first, then manage_entity_lifecycle with mode='template'",
template_tool: "get_entity_templates"
};
guidance.recommended_next_tool = {
tool: "get_entity_templates",
reason: "Template required for this entity type",
parameters: { entity_type: data.template_mode.entity_type }
};
}
// Check for multi-project queries
if (options.project_context?.is_multi_project) {
if (!guidance.operation_hints) {
guidance.operation_hints = {
complexity: 'simple',
data_comprehensive: true
};
}
else {
guidance.operation_hints.data_comprehensive = true;
}
guidance.success_indicators = {
operation_completed: true,
data_comprehensive: true
};
}
return Object.keys(guidance).length > 0 ? guidance : null;
}
// ===== PRIVATE HELPER METHODS =====
static extractResultCount(data) {
// Handle array responses
if (Array.isArray(data)) {
return data.length;
}
// Handle object responses with common count patterns
if (typeof data === 'object' && data !== null) {
// Pattern 1: { total: number, [entityName]: array }
if (typeof data.total === 'number') {
return data.total;
}
// Pattern 2: { [entityName]: array } - count the array
const arrayFields = Object.values(data).filter(Array.isArray);
if (arrayFields.length === 1) {
return arrayFields[0].length;
}
// Pattern 3: Single entity response
if (!Array.isArray(data) && Object.keys(data).length > 0) {
return 1;
}
}
return undefined;
}
static determineQueryMode(data, projectContext) {
// Check for explicit metadata (from MODE pattern tools)
if (data && typeof data === 'object' && data._metadata?.mode) {
return data._metadata.mode;
}
// Infer from project context
if (projectContext?.is_multi_project || (projectContext?.projects_queried && projectContext.projects_queried > 1)) {
return 'all_projects';
}
if (projectContext?.project_id && projectContext.project_id !== 'ALL') {
return 'single_project';
}
// Check data structure for multi-project indicators
if (data && typeof data === 'object' && data.summary?.by_project) {
const projectCount = Object.keys(data.summary.by_project).length;
return projectCount > 1 ? 'all_projects' : 'single_project';
}
return undefined;
}
static checkForHardStop(data) {
if (typeof data === 'object' && data !== null) {
return data.status === 'HARD_STOP_REQUIRED' ||
data._isError === true ||
data.error !== undefined;
}
return false;
}
static determineStatus(data, hasHardStop) {
if (hasHardStop) {
return 'hard_stop_required';
}
if (typeof data === 'object' && data !== null) {
// Check explicit success indicators
if (data.success === false) {
return 'error';
}
if (data.success === true) {
return 'success';
}
// Check for warning conditions
if (data._usage_guidance || data.template_mode) {
return 'warning';
}
}
return 'success';
}
static getAppliedPatterns(options) {
const patterns = ['response_wrapper'];
if (options.enhance_for_mode_pattern) {
patterns.push('mode_pattern_enhancement');
}
if (options.preserve_hard_stops) {
patterns.push('hard_stop_preservation');
}
if (options.project_context) {
patterns.push('project_context_inference');
}
return patterns;
}
}
/**
* Decorator function for automatically wrapping tool methods
* Usage: @withResponseWrapper('tool_name', options)
*/
export function withResponseWrapper(toolName, options = {}) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args) {
const startTime = Date.now();
try {
const result = await originalMethod.apply(this, args);
return ResponseWrapper.wrap(result, {
tool_name: toolName,
start_time: startTime,
...options
});
}
catch (error) {
// For errors, still wrap them for consistency
return ResponseWrapper.wrap({ error: error.message, _isError: true }, {
tool_name: toolName,
start_time: startTime,
...options
});
}
};
return descriptor;
};
}
// ===== MIGRATION UTILITIES =====
/**
* Utility for gradually migrating existing tools to use response wrapper
* Allows selective enhancement without breaking existing functionality
*/
export class ResponseWrapperMigration {
static enabledTools = new Set();
static enableForTool(toolName) {
this.enabledTools.add(toolName);
}
static isEnabledForTool(toolName) {
return this.enabledTools.has(toolName);
}
static wrapIfEnabled(data, toolName, options) {
if (this.isEnabledForTool(toolName)) {
return ResponseWrapper.wrap(data, options);
}
return data;
}
}
//# sourceMappingURL=ResponseWrapper.js.map