/** * HTTP fetcher with timeout and error handling. * Extracted from real-world MCP server development (webcheck-mcp). */ export interface FetchOptions { timeoutMs?: number; headers?: Record; } export interface FetchResult { status: number; headers: Record; body: string; responseTimeMs: number; } export async function fetchWithTimeout( url: string, options: FetchOptions = {} ): Promise { const { timeoutMs = 10000, headers = {} } = options; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); const start = Date.now(); try { const response = await fetch(url, { signal: controller.signal, headers: { "User-Agent": "MCP-Server/1.0", ...headers, }, }); const body = await response.text(); const responseTimeMs = Date.now() - start; const responseHeaders: Record = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); return { status: response.status, headers: responseHeaders, body, responseTimeMs, }; } finally { clearTimeout(timer); } }