@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
233 lines • 8.41 kB
JavaScript
/**
* Pagination Configuration Manager
* @description Centralized configuration for pagination behavior across the MCP server.
* Manages page sizes, bypass limits, and entity-specific defaults to ensure
* consistent pagination behavior and prevent overwhelming AI agents with large datasets.
*
* Key features:
* - Environment-based configuration with sensible defaults
* - Entity-specific pagination rules based on typical dataset sizes
* - Smart bypass detection for small entities (attributes, environments)
* - Hard limits to prevent memory issues with large datasets
*
* @author Optimizely MCP Server
* @version 1.0.0
*/
/**
* Manages pagination configuration for the MCP server
*/
export class PaginationConfigManager {
config;
entityDefaults;
constructor() {
// Load configuration from environment variables with defaults
this.config = {
defaultPageSize: parseInt(process.env.PAGINATION_DEFAULT_PAGE_SIZE || '25'),
complexPageSize: parseInt(process.env.PAGINATION_COMPLEX_PAGE_SIZE || '25'),
simplePageSize: parseInt(process.env.PAGINATION_SIMPLE_PAGE_SIZE || '25'),
bypassMax: parseInt(process.env.PAGINATION_BYPASS_MAX || '200'),
autoBypassEntities: (process.env.PAGINATION_AUTO_BYPASS_ENTITIES || 'attribute,environment,collaborator').split(',').map(e => e.trim()),
warnThreshold: parseInt(process.env.PAGINATION_WARN_THRESHOLD || '100')
};
// Define entity-specific defaults based on typical dataset sizes
this.entityDefaults = {
// Small datasets - bypass pagination by default
attribute: {
autoBypass: true,
typicalSize: '10-50',
warnThreshold: 50
},
environment: {
autoBypass: true,
typicalSize: '2-4',
warnThreshold: 10
},
collaborator: {
autoBypass: true,
typicalSize: '5-20',
warnThreshold: 30
},
list_attribute: {
autoBypass: true,
typicalSize: '5-20',
warnThreshold: 30
},
webhook: {
autoBypass: true,
typicalSize: '5-15',
warnThreshold: 20
},
// Medium datasets - paginate by default
audience: {
autoBypass: false,
typicalSize: '20-100',
warnThreshold: 50
},
event: {
autoBypass: false,
typicalSize: '50-200',
warnThreshold: 100
},
extension: {
autoBypass: false,
typicalSize: '10-50',
warnThreshold: 30
},
group: {
autoBypass: false,
typicalSize: '10-50',
warnThreshold: 30
},
// Large datasets - always paginate
flag: {
autoBypass: false,
typicalSize: '100-1000',
warnThreshold: 500
},
experiment: {
autoBypass: false,
typicalSize: '50-500',
warnThreshold: 200
},
page: {
autoBypass: false,
typicalSize: '50-500',
warnThreshold: 200
},
campaign: {
autoBypass: false,
typicalSize: '20-200',
warnThreshold: 100
},
variation: {
autoBypass: false,
typicalSize: '100-1000',
warnThreshold: 300
},
rule: {
autoBypass: false,
typicalSize: '100-1000',
warnThreshold: 300
},
// Analytics - special handling
analytics: {
autoBypass: false,
typicalSize: '25-10000',
warnThreshold: 1000
},
ruleset: {
autoBypass: false,
typicalSize: '100-1000',
warnThreshold: 300
}
};
}
/**
* Get the appropriate page size for an entity type
* @param entityType The type of entity being queried
* @param isSimplified Whether simplified/condensed data is requested
* @returns The page size to use
*/
getPageSize(entityType, isSimplified = false) {
if (isSimplified) {
return this.config.simplePageSize;
}
// For complex entities with nested data, use smaller page size
const complexEntities = ['flag', 'experiment', 'campaign', 'ruleset'];
if (complexEntities.includes(entityType)) {
return this.config.complexPageSize;
}
// Analytics gets default page size from env or 10
if (entityType === 'analytics') {
return parseInt(process.env.ANALYTICS_DEFAULT_PAGE_SIZE || '25');
}
return this.config.defaultPageSize;
}
/**
* Check if pagination should be automatically bypassed for an entity type
* @param entityType The type of entity being queried
* @returns Whether to bypass pagination
*/
shouldAutoBypass(entityType) {
// Check entity-specific defaults first
const entityDefault = this.entityDefaults[entityType];
if (entityDefault !== undefined) {
return entityDefault.autoBypass;
}
// Fall back to environment configuration
return this.config.autoBypassEntities.includes(entityType);
}
/**
* Get the maximum number of records allowed when bypassing pagination
* @returns The bypass limit
*/
getBypassLimit() {
return this.config.bypassMax;
}
/**
* Get the warning threshold for an entity type
* @param entityType The type of entity
* @returns The number of records that triggers a warning
*/
getWarnThreshold(entityType) {
const entityDefault = this.entityDefaults[entityType];
if (entityDefault?.warnThreshold !== undefined) {
return entityDefault.warnThreshold;
}
return this.config.warnThreshold;
}
/**
* Check if a bypass request is valid
* @param totalCount The total number of records
* @param requestedBypass Whether bypass was explicitly requested
* @returns Object indicating if bypass is allowed and any warnings
*/
validateBypassRequest(totalCount, requestedBypass) {
if (!requestedBypass) {
return { allowed: false };
}
if (totalCount > this.config.bypassMax) {
return {
allowed: false,
reason: `Cannot bypass pagination for ${totalCount} records (max: ${this.config.bypassMax})`,
suggestion: 'Use export functionality or paginate through results'
};
}
if (totalCount > this.config.warnThreshold) {
return {
allowed: true,
reason: `Large dataset (${totalCount} records) - consider using export for better performance`
};
}
return { allowed: true };
}
/**
* Get entity metadata for documentation
* @param entityType The type of entity
* @returns Metadata about the entity's pagination behavior
*/
getEntityMetadata(entityType) {
const defaults = this.entityDefaults[entityType] || {
autoBypass: false,
typicalSize: 'varies',
warnThreshold: this.config.warnThreshold
};
return {
autoBypass: this.shouldAutoBypass(entityType),
typicalSize: defaults.typicalSize,
recommendation: defaults.autoBypass
? 'Small dataset - pagination bypassed by default'
: 'Large dataset - pagination recommended'
};
}
/**
* Get all configuration values for debugging
* @returns Current configuration
*/
getConfig() {
return { ...this.config };
}
}
// Export singleton instance for consistent configuration
export const paginationConfig = new PaginationConfigManager();
//# sourceMappingURL=PaginationConfig.js.map