@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
JavaScript
/**
* @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());
}
});
}
}