Skip to main content
Glama
client-detection.ts6.68 kB
/** * MCP Client Detection and Timeout Configuration * * Different MCP clients have different timeout thresholds: * - Gemini CLI: ~30 seconds * - ChatGPT Desktop: ~45 seconds * - Claude Desktop: ~60 seconds * - Claude Mobile: ~60 seconds * * This module detects the client and provides appropriate timeout configurations */ export interface ClientTimeoutConfig { clientName: string; totalTimeoutMs: number; bufferMs: number; delayBetweenRequestsMs: number; maxPages: number; description: string; } export interface ClientInfo { name: string; version?: string; userAgent?: string; } /** * Predefined timeout configurations for known MCP clients */ const CLIENT_TIMEOUT_CONFIGS: Record<string, ClientTimeoutConfig> = { gemini: { clientName: 'Gemini', totalTimeoutMs: 20000, // 20 seconds (conservative for 30s timeout) bufferMs: 3000, // 3 second buffer delayBetweenRequestsMs: 50, // Minimal delay maxPages: 3, // Limit pages to ensure completion description: 'Gemini CLI has strict 30-second timeout' }, chatgpt: { clientName: 'ChatGPT', totalTimeoutMs: 30000, // 30 seconds (conservative for 45s timeout) bufferMs: 5000, // 5 second buffer delayBetweenRequestsMs: 100, // Small delay maxPages: 5, // Moderate page limit description: 'ChatGPT Desktop has moderate timeout' }, claude: { clientName: 'Claude', totalTimeoutMs: 45000, // 45 seconds (conservative for 60s timeout) bufferMs: 5000, // 5 second buffer delayBetweenRequestsMs: 100, // Small delay maxPages: 10, // Higher page limit description: 'Claude has generous timeout' }, default: { clientName: 'Unknown', totalTimeoutMs: 20000, // 20 seconds (safest default) bufferMs: 3000, // 3 second buffer delayBetweenRequestsMs: 50, // Minimal delay maxPages: 3, // Conservative page limit description: 'Unknown client - using conservative settings' } }; /** * Detect MCP client from initialization info */ export function detectClient(clientInfo?: ClientInfo, userAgent?: string): string { // Check client name from MCP initialize request if (clientInfo?.name) { const name = clientInfo.name.toLowerCase(); if (name.includes('gemini')) return 'gemini'; if (name.includes('chatgpt') || name.includes('openai')) return 'chatgpt'; if (name.includes('claude') || name.includes('anthropic')) return 'claude'; } // Check user agent as fallback if (userAgent) { const ua = userAgent.toLowerCase(); if (ua.includes('gemini')) return 'gemini'; if (ua.includes('chatgpt') || ua.includes('openai')) return 'chatgpt'; if (ua.includes('claude') || ua.includes('anthropic')) return 'claude'; } return 'default'; } /** * Get timeout configuration for a specific client */ export function getClientTimeoutConfig(clientType: string): ClientTimeoutConfig { return CLIENT_TIMEOUT_CONFIGS[clientType] || CLIENT_TIMEOUT_CONFIGS.default; } /** * Get timeout configuration from client info */ export function getTimeoutConfigFromClientInfo( clientInfo?: ClientInfo, userAgent?: string ): ClientTimeoutConfig { const clientType = detectClient(clientInfo, userAgent); const config = getClientTimeoutConfig(clientType); console.error(`[Client Detection] Detected client: ${config.clientName}`); console.error(`[Client Detection] Timeout config: ${config.totalTimeoutMs}ms total, ${config.maxPages} max pages`); return config; } /** * Client detection manager - stores client info for the session */ export class ClientDetectionManager { private clientInfo?: ClientInfo; private userAgent?: string; private detectedClient?: string; private timeoutConfig?: ClientTimeoutConfig; /** * Update client information from initialize request */ updateClientInfo(clientInfo?: ClientInfo, userAgent?: string): void { this.clientInfo = clientInfo; this.userAgent = userAgent; this.detectedClient = detectClient(clientInfo, userAgent); this.timeoutConfig = getClientTimeoutConfig(this.detectedClient); console.error(`[Client Detection] Client updated: ${this.timeoutConfig.clientName}`); console.error(`[Client Detection] Config: ${JSON.stringify(this.timeoutConfig, null, 2)}`); } /** * Get current timeout configuration */ getTimeoutConfig(): ClientTimeoutConfig { if (!this.timeoutConfig) { console.error('[Client Detection] No client info available, using default config'); return CLIENT_TIMEOUT_CONFIGS.default; } return this.timeoutConfig; } /** * Get detected client name */ getClientName(): string { return this.timeoutConfig?.clientName || 'Unknown'; } /** * Check if client is known to have strict timeouts */ hasStrictTimeout(): boolean { return this.detectedClient === 'gemini' || this.detectedClient === 'default'; } /** * Get recommended strategy for pagination */ getRecommendedPaginationStrategy(): 'fast' | 'balanced' | 'complete' { if (!this.detectedClient || this.detectedClient === 'default') { return 'fast'; // Unknown clients get fastest strategy } switch (this.detectedClient) { case 'gemini': return 'fast'; // Gemini needs fast strategy case 'chatgpt': return 'balanced'; // ChatGPT can handle balanced case 'claude': return 'complete'; // Claude can handle complete pagination default: return 'fast'; } } } /** * Global client detection manager instance */ export const globalClientManager = new ClientDetectionManager(); /** * Helper function to format timeout warning message */ export function formatTimeoutWarning( itemsRetrieved: number, nextCursor?: string, clientName?: string ): string { const client = clientName || 'your MCP client'; if (nextCursor) { return `⚠️ Timeout protection: Retrieved ${itemsRetrieved} items to avoid ${client} timeout. More results available. To retrieve additional items, call this tool again with starting_after="${nextCursor}"`; } return `✅ Retrieved ${itemsRetrieved} items successfully within ${client} timeout limits.`; } /** * Calculate safe page limit based on estimated time per page */ export function calculateSafePageLimit( config: ClientTimeoutConfig, estimatedMsPerPage: number = 3000 ): number { const availableTime = config.totalTimeoutMs - config.bufferMs; const calculatedPages = Math.floor(availableTime / estimatedMsPerPage); // Return minimum of calculated pages and configured max pages return Math.min(calculatedPages, config.maxPages); }

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/bcharleson/Instantly-MCP'

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