@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
177 lines • 8.2 kB
JavaScript
/**
* 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