UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

177 lines 8.2 kB
/** * Intelligent Response Parser * @description Automatically normalizes API responses to consistent format regardless of source complexity * * This parser handles the reverse direction of IntelligentPayloadParser: * - IntelligentPayloadParser: AI payloads → API format * - IntelligentResponseParser: API responses → Normalized format */ import { getLogger } from '../logging/Logger.js'; export class IntelligentResponseParser { /** * Parse any API response into normalized format */ parseResponse(response, options) { const logger = getLogger(); try { // Handle null/undefined if (response === null || response === undefined) { return this.createErrorResponse('Response is null or undefined'); } // Handle string responses (usually errors) if (typeof response === 'string') { return this.createErrorResponse(response); } // Handle direct arrays if (Array.isArray(response)) { return this.createSuccessResponse(this.processArrayData(response, options), { total_count: response.length }); } // Handle objects if (typeof response === 'object') { return this.parseObjectResponse(response, options); } // Handle primitives if (options.expectedType === 'any') { return this.createSuccessResponse(response); } return this.createErrorResponse(`Unexpected response type: ${typeof response}`); } catch (error) { logger.error({ error: error?.message || String(error), response: typeof response }, 'IntelligentResponseParser: Parse failed'); return this.createErrorResponse(`Parse error: ${error?.message || String(error)}`); } } /** * Parse object responses with intelligent format detection */ parseObjectResponse(response, options) { const logger = getLogger(); // Pattern 1: Error responses if (response.result === 'error' || response.error) { const errorMessage = response.error?.message || response.message || 'Unknown error'; return this.createErrorResponse(errorMessage); } // Pattern 2: Success with entities as array if (response.result === 'success' && Array.isArray(response.entities)) { const processedData = this.processArrayData(response.entities, options); return this.createSuccessResponse(processedData, response.metadata, response._usage_guidance); } // Pattern 3: Success with entities as object containing data array ← THE MISSING PATTERN if (response.result === 'success' && response.entities && typeof response.entities === 'object' && Array.isArray(response.entities.data)) { logger.info({ entityType: options.entityType, pattern: 'nested_entities_data', originalCount: response.entities.data.length }, 'IntelligentResponseParser: Detected nested entities.data pattern'); const processedData = this.processArrayData(response.entities.data, options); return this.createSuccessResponse(processedData, { ...response.metadata, ...response.entities.metadata, total_count: response.entities.data.length }, response._usage_guidance); } // Pattern 4: Success with direct data array if (response.result === 'success' && Array.isArray(response.data)) { const processedData = this.processArrayData(response.data, options); return this.createSuccessResponse(processedData, response.metadata, response._usage_guidance); } // Pattern 5: Direct data wrapper if (Array.isArray(response.data)) { const processedData = this.processArrayData(response.data, options); return this.createSuccessResponse(processedData, { total_count: response.data.length }); } // Pattern 6: Direct entities wrapper if (Array.isArray(response.entities)) { const processedData = this.processArrayData(response.entities, options); return this.createSuccessResponse(processedData, { total_count: response.entities.length }); } // Pattern 7: Smart routing responses (Feature Experimentation) if (response._smartRouting && response.experimentSummary) { logger.info({ entityType: options.entityType, pattern: 'smart_routing', experimentCount: response.experimentSummary.length }, 'IntelligentResponseParser: Detected smart routing pattern'); const processedData = this.processArrayData(response.experimentSummary, options); return this.createSuccessResponse(processedData, { total_count: response.experimentSummary.length, project_type: 'feature' }); } // Pattern 8: Pagination responses if (response.items && Array.isArray(response.items)) { const processedData = this.processArrayData(response.items, options); return this.createSuccessResponse(processedData, { total_count: response.total_count || response.items.length, page: response.page, total_pages: response.total_pages }); } // Pattern 9: Single object response if (options.expectedType === 'object' || options.expectedType === 'any') { return this.createSuccessResponse(response); } // Pattern 10: Empty response for array expectation if (options.expectedType === 'array' && options.allowEmpty) { logger.warn({ response: Object.keys(response) }, 'IntelligentResponseParser: No array found in response, returning empty array'); return this.createSuccessResponse([], { total_count: 0 }); } // Log unhandled pattern for future enhancement logger.error({ entityType: options.entityType, operation: options.operation, responseKeys: Object.keys(response), hasResult: 'result' in response, hasEntities: 'entities' in response, hasData: 'data' in response, entitiesType: response.entities ? typeof response.entities : 'undefined' }, 'IntelligentResponseParser: Unhandled response pattern - add new pattern detection'); return this.createErrorResponse('Unhandled response format - please report this pattern'); } /** * Process array data with entity-specific filtering */ processArrayData(data, options) { if (!Array.isArray(data)) { return []; } // Special handling for experiments - filter guidance objects if (options.entityType === 'experiment') { const filtered = data.filter(item => item && typeof item === 'object' && !item._mcp_guidance && !item._smartRouting); if (filtered.length !== data.length) { getLogger().info({ entityType: options.entityType, originalCount: data.length, filteredCount: filtered.length }, 'IntelligentResponseParser: Filtered guidance objects from experiment array'); } return filtered; } return data; } /** * Create standardized success response */ createSuccessResponse(data, metadata, usageGuidance) { return { success: true, data, ...(metadata && { metadata }), ...(usageGuidance && { _usage_guidance: usageGuidance }) }; } /** * Create standardized error response */ createErrorResponse(message, code) { return { success: false, data: [], error: { message, code } }; } } //# sourceMappingURL=IntelligentResponseParser.js.map