Skip to main content
Glama
ezhou89

Medical Research MCP Suite

by ezhou89
responseSizeMonitor.ts7.12 kB
// src/utils/responseSizeMonitor.ts export interface SizeConfig { maxResponseSize: number; // Maximum response size in bytes warningThreshold: number; // Warning threshold as percentage (0-1) truncationMode: 'truncate' | 'summarize' | 'fail'; enableSizeTracking: boolean; } export interface SizeMetrics { responseSize: number; estimatedMemoryUsage: number; compressionRatio?: number; truncated: boolean; originalSize?: number; } export interface SizeExceededInfo { actualSize: number; maxSize: number; exceedsByBytes: number; exceedsByPercent: number; suggestedActions: string[]; } export class ResponseSizeMonitor { private static instance: ResponseSizeMonitor; private config: SizeConfig; private metrics: Map<string, SizeMetrics> = new Map(); private constructor(config: Partial<SizeConfig> = {}) { this.config = { maxResponseSize: 1048576, // 1MB default (matches the error message) warningThreshold: 0.8, truncationMode: 'fail', enableSizeTracking: true, ...config }; } static getInstance(config?: Partial<SizeConfig>): ResponseSizeMonitor { if (!ResponseSizeMonitor.instance) { ResponseSizeMonitor.instance = new ResponseSizeMonitor(config); } return ResponseSizeMonitor.instance; } updateConfig(config: Partial<SizeConfig>): void { this.config = { ...this.config, ...config }; } /** * Calculate the size of a response object in bytes */ calculateSize(data: any): number { const jsonString = JSON.stringify(data); return new TextEncoder().encode(jsonString).length; } /** * Check if response size exceeds limits */ checkSizeLimit(data: any, context: string = 'unknown'): { withinLimit: boolean; metrics: SizeMetrics; exceededInfo?: SizeExceededInfo; } { const size = this.calculateSize(data); const withinLimit = size <= this.config.maxResponseSize; const withinWarning = size <= (this.config.maxResponseSize * this.config.warningThreshold); const metrics: SizeMetrics = { responseSize: size, estimatedMemoryUsage: size * 1.2, // Rough estimate with overhead truncated: false, originalSize: size }; if (this.config.enableSizeTracking) { this.metrics.set(context, metrics); } if (!withinLimit) { const exceededInfo: SizeExceededInfo = { actualSize: size, maxSize: this.config.maxResponseSize, exceedsByBytes: size - this.config.maxResponseSize, exceedsByPercent: ((size - this.config.maxResponseSize) / this.config.maxResponseSize) * 100, suggestedActions: this.generateSuggestedActions(size, context) }; return { withinLimit: false, metrics, exceededInfo }; } return { withinLimit: true, metrics }; } /** * Generate suggested actions for oversized responses */ private generateSuggestedActions(size: number, context: string): string[] { const suggestions: string[] = []; // Context-specific suggestions if (context.includes('clinical') || context.includes('trial')) { suggestions.push( 'Filter by study phase (e.g., Phase III only)', 'Limit to recent studies (last 1-2 years)', 'Filter by study status (e.g., Active, Recruiting only)', 'Reduce page size and use pagination', 'Select specific fields instead of full study details' ); } if (context.includes('pubmed') || context.includes('publication')) { suggestions.push( 'Add date range filter (e.g., last 5 years)', 'Use more specific search terms', 'Filter by publication type (e.g., clinical trials only)', 'Limit to specific journals or impact factors', 'Reduce the number of results per page' ); } // General suggestions suggestions.push( 'Use more specific search criteria', 'Apply additional filters to narrow results', 'Load results in smaller batches', 'Use progressive loading instead of bulk retrieval' ); return suggestions; } /** * Attempt to truncate response while preserving important data */ truncateResponse(data: any, context: string = 'unknown'): { truncatedData: any; metrics: SizeMetrics; truncationSummary: string; } { const originalSize = this.calculateSize(data); let truncatedData = data; let truncationSummary = ''; if (Array.isArray(data)) { // For arrays, keep the first portion and add summary const maxItems = Math.floor(data.length * 0.3); // Keep 30% of items truncatedData = data.slice(0, maxItems); truncationSummary = `Showing ${maxItems} of ${data.length} results. Use pagination or filters to access remaining ${data.length - maxItems} items.`; } else if (typeof data === 'object' && data !== null) { // For objects, try to preserve structure but truncate arrays within truncatedData = { ...data }; if (data.studies && Array.isArray(data.studies)) { const maxStudies = Math.floor(data.studies.length * 0.3); truncatedData.studies = data.studies.slice(0, maxStudies); truncationSummary = `Showing ${maxStudies} of ${data.studies.length} studies. Use pagination or filters to access remaining ${data.studies.length - maxStudies} studies.`; } } const newSize = this.calculateSize(truncatedData); const metrics: SizeMetrics = { responseSize: newSize, estimatedMemoryUsage: newSize * 1.2, truncated: true, originalSize }; if (this.config.enableSizeTracking) { this.metrics.set(context, metrics); } return { truncatedData, metrics, truncationSummary }; } /** * Get size metrics for a specific context */ getMetrics(context: string): SizeMetrics | undefined { return this.metrics.get(context); } /** * Get all recorded metrics */ getAllMetrics(): Map<string, SizeMetrics> { return new Map(this.metrics); } /** * Clear metrics for a specific context or all contexts */ clearMetrics(context?: string): void { if (context) { this.metrics.delete(context); } else { this.metrics.clear(); } } /** * Get current configuration */ getConfig(): SizeConfig { return { ...this.config }; } /** * Check if size is approaching the warning threshold */ isApproachingLimit(size: number): boolean { return size >= (this.config.maxResponseSize * this.config.warningThreshold); } /** * Format size in human-readable format */ formatSize(bytes: number): string { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } } // Convenience function for quick size checks export function checkResponseSize(data: any, context: string = 'unknown', config?: Partial<SizeConfig>) { const monitor = ResponseSizeMonitor.getInstance(config); return monitor.checkSizeLimit(data, context); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ezhou89/medical-research-mcp-suite'

If you have feedback or need assistance with the MCP directory API, please join our Discord server