UNPKG

@cyanheads/pubmed-mcp-server

Version:

Production-ready PubMed Model Context Protocol (MCP) server that empowers AI agents and research tools with comprehensive access to PubMed's article database. Enables advanced, automated LLM workflows for searching, retrieving, analyzing, and visualizing

78 lines 3.59 kB
/** * @fileoverview Provides a utility function to make fetch requests with a specified timeout. * @module src/utils/network/fetchWithTimeout */ import { logger } from "../internal/logger.js"; // Adjusted import path import { McpError, BaseErrorCode } from "../../types-global/errors.js"; /** * Fetches a resource with a specified timeout. * * @param url - The URL to fetch. * @param timeoutMs - The timeout duration in milliseconds. * @param context - The request context for logging. * @param options - Optional fetch options (RequestInit), excluding 'signal'. * @returns A promise that resolves to the Response object. * @throws {McpError} If the request times out or another fetch-related error occurs. */ export async function fetchWithTimeout(url, timeoutMs, context, options) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); const urlString = url.toString(); const operationDescription = `fetch ${options?.method || "GET"} ${urlString}`; logger.debug(`Attempting ${operationDescription} with ${timeoutMs}ms timeout.`, context); try { const response = await fetch(url, { ...options, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorBody = await response .text() .catch(() => "Could not read response body"); logger.error(`Fetch failed for ${urlString} with status ${response.status}.`, { ...context, statusCode: response.status, statusText: response.statusText, responseBody: errorBody, errorSource: "FetchHttpError", }); throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, `Fetch failed for ${urlString}. Status: ${response.status}`, { ...context, statusCode: response.status, statusText: response.statusText, responseBody: errorBody, }); } logger.debug(`Successfully fetched ${urlString}. Status: ${response.status}`, context); return response; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === "AbortError") { logger.error(`${operationDescription} timed out after ${timeoutMs}ms.`, { ...context, errorSource: "FetchTimeout", }); throw new McpError(BaseErrorCode.TIMEOUT, `${operationDescription} timed out.`, { ...context, errorSource: "FetchTimeout" }); } // Log and re-throw other errors as McpError const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Network error during ${operationDescription}: ${errorMessage}`, { ...context, originalErrorName: error instanceof Error ? error.name : "UnknownError", errorSource: "FetchNetworkError", }); if (error instanceof McpError) { // If it's already an McpError, re-throw it throw error; } throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, // Generic error for network/service issues `Network error during ${operationDescription}: ${errorMessage}`, { ...context, originalErrorName: error instanceof Error ? error.name : "UnknownError", errorSource: "FetchNetworkErrorWrapper", }); } } //# sourceMappingURL=fetchWithTimeout.js.map