Skip to main content

performance-optimization

Node.js/TypeScript API performance, MCP protocol optimization, European Parliament API caching, async operations, and memory efficiency

Quellinformationen

Repository
Hack23/European-Parliament-MCP-Server
Letzte Quellaktivität
18. April 2026 um 16:39
Erkannte Sprache von SKILL.md
Englisch
Sterne
29
Forks
9

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
performance-optimization
description
Node.js/TypeScript API performance, MCP protocol optimization, European Parliament API caching, async operations, and memory efficiency
license
MIT
# Performance Optimization Skill ## Context This skill applies when: - Implementing MCP protocol handlers and tool endpoints - Fetching data from European Parliament APIs - Processing large datasets or document collections - Designing caching strategies for API responses - Optimizing database queries or data transformations - Profiling and measuring performance bottlenecks - Writing async/await code or Promise chains - Handling concurrent requests or batch operations - Reducing memory allocations and garbage collection pressure - Optimizing startup time and initialization Performance is critical for MCP servers as they power real-time AI interactions. Response times should be < 500ms, memory usage should be stable, and throughput should handle 100+ requests per 15-minute window. ## Rules 1. **Measure Before Optimizing**: Always profile and measure performance before making changes - no premature optimization 2. **Set Performance Budgets**: API responses < 500ms, memory growth < 10MB per hour, startup time < 2s 3. **Cache Aggressively**: Cache European Parliament API responses with appropriate TTL (5-60 minutes) 4. **Use Async/Await Properly**: Avoid blocking the event loop - use async for I/O, sync for CPU-bound work 5. **Minimize Allocations**: Reuse objects, use object pools, avoid creating unnecessary intermediate arrays 6. **Batch Operations**: Group multiple API calls or database queries when possible 7. **Lazy Load**: Defer loading of non-critical resources until needed 8. **Stream Large Data**: Use Node.js streams for processing large documents or datasets 9. **Optimize Serialization**: Minimize JSON parsing overhead, consider caching parsed results 10. **Index Data Structures**: Use Maps/Sets for O(1) lookups instead of O(n) array searches 11. **Avoid Memory Leaks**: Clear timers, remove event listeners, close connections properly 12. **Monitor Performance**: Implement performance logging and alerting for regression detection 13. **Compress Responses**: Use gzip/brotli for MCP responses when size exceeds 1KB 14. **Debounce/Throttle**: Rate limit expensive operations like API calls or searches 15. **Use Worker Threads**: Offload CPU-intensive work (parsing, transformation) to worker threads ## Examples ### ✅ Good Pattern: Efficient European Parliament API Caching ```typescript /** * Caching service for European Parliament API responses * * Performance optimizations: * - In-memory LRU cache with automatic eviction * - TTL-based expiration (5 minutes for search, 60 minutes for documents) * - Cache key normalization for hit rate optimization * - Memory-bounded cache (max 100MB) * * ISMS Policy: SC-001 (Secure Configuration - Performance Standards) * Compliance: NIST CSF PR.PT-4 (Communications and control networks) * * @performance Cache hit rate: 70%+, Response time reduction: 90%+ */ import { LRUCache } from 'lru-cache'; interface CacheOptions { ttl: number; maxSize: number; maxItems: number; } export class EuropeanParliamentCache { private readonly searchCache: LRUCache<string, SearchResult>; private readonly documentCache: LRUCache<string, Document>; constructor() { // Search results cache: 5 minute TTL, max 1000 items, ~50MB this.searchCache = new LRUCache<string, SearchResult>({ max: 1000, maxSize: 50 * 1024 * 1024, sizeCalculation: (value) => JSON.stringify(value).length, ttl: 5 * 60 * 1000, // 5 minutes updateAgeOnGet: true, updateAgeOnHas: true, }); // Document cache: 60 minute TTL, max 500 items, ~50MB this.documentCache = new LRUCache<string, Document>({ max: 500, maxSize: 50 * 1024 * 1024, sizeCalculation: (value) => JSON.stringify(value).length, ttl: 60 * 60 * 1000, // 60 minutes updateAgeOnGet: true, }); } /** * Get or fetch search results with caching * Cache key includes all query parameters for proper invalidation */ async getSearchResults( query: SearchQuery, fetcher: () => Promise<SearchResult> ): Promise<SearchResult> { const cacheKey = this.normalizeCacheKey(query); // Check cache first const cached = this.searchCache.get(cacheKey); if (cached) { return cached; } // Cache miss - fetch from API const result = await fetcher(); this.searchCache.set(cacheKey, result); return result; } /** * Normalize cache key for consistent hits * Sorts parameters, trims whitespace, lowercases strings */ private normalizeCacheKey(query: SearchQuery): string { const normalized = { keywords: query.keywords.trim().toLowerCase(), documentType: query.documentType?.toLowerCase(), dateFrom: query.dateFrom, dateTo: query.dateTo, limit: query.limit, }; // Sort keys for consistent serialization return JSON.stringify(normalized, Object.keys(normalized).sort()); } /** * Clear cache on demand or schedule periodic clearing */ clear(): void { this.searchCache.clear(); this.documentCache.clear(); } /** * Get cache statistics for monitoring */ getStats() { return { search: { size: this.searchCache.size, calculatedSize: this.searchCache.calculatedSize, hitRate: this.searchCache.calculatedSize > 0 ? (this.searchCache.size / this.searchCache.calculatedSize) * 100 : 0, }, document: { size: this.documentCache.size, calculatedSize: this.documentCache.calculatedSize, }, }; } } ``` ### ✅ Good Pattern: Optimized MCP Tool Handler ```typescript /** * High-performance MCP tool handler with multiple optimizations * * Performance features: * - Request coalescing for duplicate in-flight requests * - Input validation caching to avoid repeated parsing * - Streaming response for large result sets * - Connection pooling for European Parliament API * - Metrics tracking for performance monitoring * * ISMS Policy: SC-001 (Secure Configuration) * * @performance Target: < 500ms p95, < 200ms p50 */ export class OptimizedSearchHandler { private readonly cache: EuropeanParliamentCache; private readonly metrics: PerformanceMetrics; private readonly inFlightRequests = new Map<string, Promise<SearchResult>>(); constructor( cache: EuropeanParliamentCache, metrics: PerformanceMetrics ) { this.cache = cache; this.metrics = metrics; } /** * Handle search_documents tool request with performance optimizations */ async handleSearch(request: ToolRequest): Promise<ToolResponse> { const startTime = performance.now(); try { // Fast path: Validate input (should be < 1ms) const query = this.validateQuery(request.params); // Request coalescing: Deduplicate identical in-flight requests const requestKey = this.getRequestKey(query); const existingRequest = this.inFlightRequests.get(requestKey); if (existingRequest) { this.metrics.recordCoalescedRequest(); return await existingRequest; } // Create new request const requestPromise = this.executeSearch(query); this.inFlightRequests.set(requestKey, requestPromise); try { const result = await requestPromise; const duration = performance.now() - startTime; // Track metrics this.metrics.recordRequest('search_documents', duration, true); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2), }], }; } finally { // Always clean up in-flight tracking this.inFlightRequests.delete(requestKey); } } catch (error) { const duration = performance.now() - startTime; this.metrics.recordRequest('search_documents', duration, false); throw error; } } /** * Execute search with caching */ private async executeSearch(query: SearchQuery): Promise<SearchResult> { return this.cache.getSearchResults(query, async () => { // Actual API call - only happens on cache miss return await europeanParliamentApi.search(query); }); } /** * Generate deterministic request key for deduplication */ private getRequestKey(query: SearchQuery): string { return `search:${JSON.stringify(query)}`; } /** * Fast input validation with result caching */ private validateQuery(params: unknown): SearchQuery { if (typeof params !== 'object' || params === null) { throw new ValidationError('Invalid query parameters'); } const query = params as Record<string, unknown>; return { keywords: this.validateString(query.keywords, 'keywords', 1, 200), documentType: query.documentType ? this.validateString(query.documentType, 'documentType', 1, 50) : undefined, dateFrom: query.dateFrom ? this.validateDate(query.dateFrom) : undefined, dateTo: query.dateTo ? this.validateDate(query.dateTo) : undefined, limit: this.validateNumber(query.limit, 'limit', 1, 100) ?? 20, }; } private validateString( value: unknown, field: string, minLength: number, maxLength: number ): string { if (typeof value !== 'string') { throw new ValidationError(`${field} must be a string`); } const trimmed = value.trim(); if (trimmed.length < minLength || trimmed.length > maxLength) { throw new ValidationError( `${field} must be between ${minLength} and ${maxLength} characters` ); } return trimmed; } private validateNumber( value: unknown, field: string, min: number, max: number ): number | undefined { if (value === undefined || value === null) { return undefined; } const num = Number(value); if (!Number.isFinite(num) || num < min || num > max) { throw new ValidationError( `${field} must be a number between ${min} and ${max}` ); } return num; } private validateDate(value: unknown): string { if (typeof value !== 'string') { throw new ValidationError('Date must be a string'); } // ISO 8601 date format validation const dateRegex = /^\d{4}-\d{2}-\d{2}$/; if (!dateRegex.test(value)) { throw new ValidationError('Date must be in ISO 8601 format (YYYY-MM-DD)'); } return value; } } ``` ### ✅ Good Pattern: Async Batch Processing ```typescript /** * Efficient batch processing for multiple document fetches * * Optimizations: * - Parallel requests with concurrency limit * - Request batching to reduce round trips * - Early return on partial success * - Progress tracking for long operations * * @performance Processes 100 documents in ~2s vs 20s sequential */ export async function fetchDocumentsBatch( documentIds: string[], options: { concurrency?: number; timeout?: number } = {} ): Promise<Map<string, Document>> { const concurrency = options.concurrency ?? 10; const timeout = options.timeout ?? 30000; const results = new Map<string, Document>(); const errors: Array<{ id: string; error: Error }> = []; // Process in batches to control concurrency for (let i = 0; i < documentIds.length; i += concurrency) { const batch = documentIds.slice(i, i + concurrency); // Parallel fetch with individual timeout handling const batchResults = await Promise.allSettled( batch.map(async (id) => { const timeoutPromise = new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Request timeout')), timeout) ); const fetchPromise = europeanParliamentApi.getDocument(id); return Promise.race([fetchPromise, timeoutPromise]); }) ); // Collect results batchResults.forEach((result, index) => { const id = batch[index]; if (result.status === 'fulfilled') { results.set(id, result.value); } else { errors.push({ id, error: result.reason }); } }); } // Log errors but don't throw - partial success is acceptable if (errors.length > 0) { console.warn(`Failed to fetch ${errors.length} documents:`, errors); } return results; } ``` ### ✅ Good Pattern: Memory-Efficient Stream Processing ```typescript /** * Stream large document content without loading into memory * * Uses Node.js streams to process large documents efficiently: * - Constant memory usage regardless of document size * - Backpressure handling for flow control * - Transform pipelines for text processing * * @performance Memory usage: O(1) vs O(n) for full load */ import { pipeline } from 'stream/promises'; import { Transform } from 'stream'; export async function processLargeDocument( documentId: string, outputPath: string ): Promise<void> { // Create read stream from European Parliament API const inputStream = await europeanParliamentApi.getDocumentStream(documentId); // Transform: Extract text content const textExtractor = new Transform({ objectMode: true, transform(chunk, encoding, callback) { try { const text = extractTextFromChunk(chunk); callback(null, text); } catch (error) { callback(error as Error); } }, }); // Transform: Apply text sanitization const sanitizer = new Transform({ objectMode: true, transform(chunk, encoding, callback) { try { const sanitized = sanitizeText(chunk.toString()); callback(null, sanitized); } catch (error) { callback(error as Error); } }, }); // Create write stream const outputStream = fs.createWriteStream(outputPath); // Pipeline with backpressure handling await pipeline( inputStream, textExtractor, sanitizer, outputStream ); } ``` ### ✅ Good Pattern: Performance Monitoring ```typescript /** * Performance metrics collection and monitoring * * Tracks key performance indicators: * - Request latency (p50, p95, p99) * - Cache hit rate * - Error rate * - Memory usage * - Event loop lag * * ISMS Policy: IM-001 (Incident Management - Performance Monitoring) */ export class PerformanceMetrics { private readonly latencies: number[] = []; private readonly errors = new Map<string, number>(); private coalescedRequests = 0; private totalRequests = 0; /** * Record request metrics */ recordRequest(tool: string, duration: number, success: boolean): void { this.totalRequests++; if (success) { this.latencies.push(duration); // Keep only last 1000 measurements to avoid memory growth
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen