UNPKG

@cyanheads/pubmed-mcp-server

Version:

A Model Context Protocol (MCP) server enabling AI agents to intelligently search, retrieve, and analyze biomedical literature from PubMed via NCBI E-utilities. Built on the mcp-ts-template for robust, production-ready performance.

95 lines (94 loc) 3.93 kB
/** * @fileoverview Manages a queue for NCBI E-utility requests to ensure compliance with rate limits. * @module src/services/NCBI/ncbiRequestQueueManager */ import { config } from "../../config/index.js"; import { logger, requestContextService, sanitizeInputForLogging, } from "../../utils/index.js"; export class NcbiRequestQueueManager { constructor() { this.requestQueue = []; this.isProcessingQueue = false; this.lastRequestTime = 0; // Constructor should not have side-effects like logging. // The service that uses this manager can log its creation if needed. } /** * Processes the request queue, ensuring delays between requests to respect NCBI rate limits. */ async processQueue() { if (this.isProcessingQueue || this.requestQueue.length === 0) { return; } this.isProcessingQueue = true; const requestItem = this.requestQueue.shift(); if (!requestItem) { this.isProcessingQueue = false; return; } const { resolve, reject, task, context, endpoint, params } = requestItem; try { const now = Date.now(); const timeSinceLastRequest = now - this.lastRequestTime; const delayNeeded = config.ncbiRequestDelayMs - timeSinceLastRequest; if (delayNeeded > 0) { logger.debug(`Delaying NCBI request by ${delayNeeded}ms to respect rate limit.`, requestContextService.createRequestContext({ ...context, operation: "NCBI_RateLimitDelay", delayNeeded, endpoint, })); await new Promise((r) => setTimeout(r, delayNeeded)); } this.lastRequestTime = Date.now(); logger.info(`Executing NCBI request via queue: ${endpoint}`, requestContextService.createRequestContext({ ...context, operation: "NCBI_ExecuteFromQueue", endpoint, params: sanitizeInputForLogging(params), })); const result = await task(); resolve(result); } catch (error) { logger.error("Error processing NCBI request from queue", error instanceof Error ? error : new Error(String(error)), requestContextService.createRequestContext({ ...context, operation: "NCBI_QueueError", endpoint, params: sanitizeInputForLogging(params), errorMessage: error?.message, })); reject(error); } finally { this.isProcessingQueue = false; if (this.requestQueue.length > 0) { // Ensure processQueue is called without awaiting it here to prevent deep stacks Promise.resolve().then(() => this.processQueue()); } } } /** * Enqueues a task (an NCBI API call) to be processed. * @param task A function that returns a Promise resolving to the API call result. * @param context The request context for logging and correlation. * @param endpoint The NCBI endpoint being called (e.g., "esearch", "efetch"). * @param params The parameters for the NCBI request. * @returns A Promise that resolves or rejects with the result of the task. */ enqueueRequest(task, context, endpoint, params) { return new Promise((resolve, reject) => { this.requestQueue.push({ resolve, reject, task, context, endpoint, params, }); if (!this.isProcessingQueue) { // Ensure processQueue is called without awaiting it here Promise.resolve().then(() => this.processQueue()); } }); } }