Skip to main content
Glama

Google Drive MCP Server

by ducla5
graceful-degradation.ts11.1 kB
/** * Graceful degradation service for handling API failures */ import { GoogleDriveMCPError, ErrorCategory } from '../types/errors.js'; import { CacheManager } from '../types/cache.js'; import { Logger } from '../logging/logger.js'; /** * Degradation strategy options */ export interface DegradationStrategy { useCachedData: boolean; usePartialData: boolean; useDefaultValues: boolean; skipNonEssentialFeatures: boolean; enableOfflineMode: boolean; } /** * Degradation context for decision making */ export interface DegradationContext { operation: string; error: GoogleDriveMCPError; hasCache: boolean; isEssential: boolean; userContext?: Record<string, any>; } /** * Degraded response wrapper */ export interface DegradedResponse<T> { data: T; degraded: boolean; degradationReason: string; limitations: string[]; suggestedActions: string[]; } /** * Default degradation strategy */ export const DEFAULT_DEGRADATION_STRATEGY: DegradationStrategy = { useCachedData: true, usePartialData: true, useDefaultValues: false, skipNonEssentialFeatures: true, enableOfflineMode: true }; /** * Graceful degradation service */ export class GracefulDegradationService { private strategy: DegradationStrategy; private cacheManager: CacheManager | undefined; private logger: Logger; constructor( strategy: Partial<DegradationStrategy> = {}, cacheManager?: CacheManager, logger?: Logger ) { this.strategy = { ...DEFAULT_DEGRADATION_STRATEGY, ...strategy }; this.cacheManager = cacheManager; this.logger = logger || new Logger(); } /** * Attempt graceful degradation for failed operation */ async handleFailure<T>( context: DegradationContext, fallbackOperations: Array<() => Promise<T | null>> ): Promise<DegradedResponse<T> | null> { this.logger.warn('Attempting graceful degradation', { operation: context.operation, error: context.error.toJSON(), strategy: this.strategy }); // Try fallback operations in order for (let i = 0; i < fallbackOperations.length; i++) { try { const result = await fallbackOperations[i](); if (result !== null) { const degradationInfo = this.createDegradationInfo(context, i); this.logger.info('Graceful degradation successful', { operation: context.operation, fallbackIndex: i, degradationInfo }); return { data: result, degraded: true, degradationReason: degradationInfo.reason, limitations: degradationInfo.limitations, suggestedActions: degradationInfo.suggestedActions }; } } catch (fallbackError) { this.logger.warn(`Fallback operation ${i} failed`, { operation: context.operation, fallbackIndex: i, error: fallbackError instanceof Error ? fallbackError.message : String(fallbackError) }); continue; } } this.logger.error('All graceful degradation attempts failed', context.error, { operation: context.operation }); return null; } /** * Create fallback operations for file retrieval */ createFileRetrievalFallbacks(fileId: string): Array<() => Promise<any | null>> { const fallbacks: Array<() => Promise<any | null>> = []; // 1. Try cached file content if (this.strategy.useCachedData && this.cacheManager) { fallbacks.push(async () => { try { const cached = await this.cacheManager!.get(`file_content_${fileId}`); return cached ? { content: cached.content, source: 'cache' } : null; } catch { return null; } }); } // 2. Try cached metadata only if (this.strategy.usePartialData && this.cacheManager) { fallbacks.push(async () => { try { const cached = await this.cacheManager!.get(`file_metadata_${fileId}`); return cached ? { metadata: cached, content: null, source: 'cache_metadata_only' } : null; } catch { return null; } }); } // 3. Return minimal default response if (this.strategy.useDefaultValues) { fallbacks.push(async () => ({ metadata: { id: fileId, name: 'Unknown File', mimeType: 'application/octet-stream', size: 0, modifiedTime: new Date(), error: 'File details unavailable due to API failure' }, content: null, source: 'default' })); } return fallbacks; } /** * Create fallback operations for search */ createSearchFallbacks(query: any): Array<() => Promise<any | null>> { const fallbacks: Array<() => Promise<any | null>> = []; // 1. Try cached search results if (this.strategy.useCachedData && this.cacheManager) { fallbacks.push(async () => { try { const cacheKey = `search_${JSON.stringify(query)}`; const cached = await this.cacheManager!.get(cacheKey); return cached ? { results: cached.content, source: 'cache', stale: true } : null; } catch { return null; } }); } // 2. Return recent files from cache if (this.strategy.usePartialData && this.cacheManager) { fallbacks.push(async () => { try { const recentFiles = await this.cacheManager!.get('recent_files'); return recentFiles ? { results: Array.isArray(recentFiles.content) ? recentFiles.content.slice(0, 10) : [], // Limit to 10 recent files source: 'cache_recent', partial: true } : null; } catch { return null; } }); } // 3. Return empty results with explanation if (this.strategy.useDefaultValues) { fallbacks.push(async () => ({ results: [], source: 'default', message: 'Search unavailable due to API failure. Please try again later.' })); } return fallbacks; } /** * Create fallback operations for folder listing */ createFolderListingFallbacks(folderId: string): Array<() => Promise<any | null>> { const fallbacks: Array<() => Promise<any | null>> = []; // 1. Try cached folder contents if (this.strategy.useCachedData && this.cacheManager) { fallbacks.push(async () => { try { const cached = await this.cacheManager!.get(`folder_${folderId}`); return cached ? { files: cached.content, source: 'cache' } : null; } catch { return null; } }); } // 2. Return empty folder with explanation if (this.strategy.useDefaultValues) { fallbacks.push(async () => ({ files: [], source: 'default', message: 'Folder contents unavailable due to API failure. Please try again later.' })); } return fallbacks; } /** * Determine if operation should use degradation */ shouldDegrade(error: GoogleDriveMCPError, isEssential: boolean): boolean { // Don't degrade for authentication errors - user needs to fix these if (error.category === ErrorCategory.AUTHENTICATION || error.category === ErrorCategory.AUTHORIZATION) { return false; } // Don't degrade for validation errors - these need to be fixed if (error.category === ErrorCategory.VALIDATION) { return false; } // Always try to degrade for non-essential operations if (!isEssential) { return true; } // Degrade for retryable errors that have exhausted retries if (error.retryable) { return true; } // Degrade for network and API errors return [ ErrorCategory.NETWORK, ErrorCategory.API_ERROR, ErrorCategory.RATE_LIMIT, ErrorCategory.QUOTA ].includes(error.category); } /** * Create degradation information */ private createDegradationInfo(context: DegradationContext, fallbackIndex: number): { reason: string; limitations: string[]; suggestedActions: string[]; } { const baseReason = `${context.operation} failed: ${context.error.message}`; let reason: string; let limitations: string[] = []; let suggestedActions: string[] = []; switch (fallbackIndex) { case 0: // Cached data reason = `${baseReason}. Using cached data.`; limitations = [ 'Data may be outdated', 'Some recent changes may not be reflected' ]; suggestedActions = [ 'Try again later when the API is available', 'Check your internet connection' ]; break; case 1: // Partial data reason = `${baseReason}. Using partial cached data.`; limitations = [ 'Only metadata available, content may be missing', 'Data may be incomplete or outdated' ]; suggestedActions = [ 'Try again later for complete data', 'Use cached content if available' ]; break; case 2: // Default values reason = `${baseReason}. Using default values.`; limitations = [ 'Minimal information available', 'Most features may be unavailable' ]; suggestedActions = [ 'Check your internet connection', 'Verify Google Drive API status', 'Try again later' ]; break; default: reason = `${baseReason}. Using fallback data.`; limitations = ['Limited functionality available']; suggestedActions = ['Try again later']; } // Add error-specific suggestions suggestedActions.push(...this.getErrorSpecificActions(context.error)); return { reason, limitations, suggestedActions }; } /** * Get error-specific suggested actions */ private getErrorSpecificActions(error: GoogleDriveMCPError): string[] { switch (error.category) { case ErrorCategory.NETWORK: return ['Check your internet connection', 'Try using a different network']; case ErrorCategory.RATE_LIMIT: return ['Wait a few minutes before trying again', 'Reduce the frequency of requests']; case ErrorCategory.QUOTA: return ['Wait for quota to reset', 'Contact your administrator about quota limits']; case ErrorCategory.API_ERROR: return ['Check Google Drive API status', 'Verify your API credentials']; default: return []; } } /** * Update degradation strategy */ updateStrategy(newStrategy: Partial<DegradationStrategy>): void { this.strategy = { ...this.strategy, ...newStrategy }; this.logger.info('Degradation strategy updated', { strategy: this.strategy }); } /** * Get current strategy */ getStrategy(): DegradationStrategy { return { ...this.strategy }; } }

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/ducla5/gdriver-mcp'

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