UNPKG

clinicaltrialsgov-mcp-server

Version:

ClinicalTrials.gov Model Context Protocol (MCP) Server that provides a suite of tools for interacting with the official ClinicalTrials.gov v2 API. Enables AI agents and LLMs to programmatically search, retrieve, and analyze clinical trial data.

99 lines (98 loc) 3.5 kB
/** * @fileoverview Centralized error handler for the Hono HTTP transport. * This middleware intercepts errors that occur during request processing, * standardizes them using the application's ErrorHandler utility, and * formats them into a consistent JSON-RPC error response. * @module src/mcp-server/transports/httpErrorHandler */ import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js"; /** * A centralized error handling middleware for Hono. * This function is registered with `app.onError()` and will catch any errors * thrown from preceding middleware or route handlers. * * @param err - The error that was thrown. * @param c - The Hono context object for the request. * @returns A Response object containing the formatted JSON-RPC error. */ export const httpErrorHandler = async (err, c) => { const context = requestContextService.createRequestContext({ operation: "httpErrorHandler", path: c.req.path, method: c.req.method, }); logger.debug("HTTP error handler invoked.", context); const handledError = ErrorHandler.handleError(err, { operation: "httpTransport", context, }); let status = 500; if (handledError instanceof McpError) { switch (handledError.code) { case BaseErrorCode.NOT_FOUND: status = 404; break; case BaseErrorCode.UNAUTHORIZED: status = 401; break; case BaseErrorCode.FORBIDDEN: status = 403; break; case BaseErrorCode.VALIDATION_ERROR: case BaseErrorCode.INVALID_INPUT: status = 400; break; case BaseErrorCode.CONFLICT: status = 409; break; case BaseErrorCode.RATE_LIMITED: status = 429; break; default: status = 500; } } logger.debug(`Mapping error to HTTP status ${status}.`, { ...context, status, errorCode: handledError.code, }); // Attempt to get the request ID from the body, but don't fail if it's not there or unreadable. let requestId = null; // Only attempt to read the body if it hasn't been consumed already. if (c.req.raw.bodyUsed === false) { try { const body = await c.req.json(); requestId = body?.id || null; logger.debug("Extracted JSON-RPC request ID from body.", { ...context, jsonRpcId: requestId, }); } catch { logger.warning("Could not parse request body to extract JSON-RPC ID.", context); // Ignore parsing errors, requestId will remain null } } else { logger.debug("Request body already consumed, cannot extract JSON-RPC ID.", context); } const errorCode = handledError instanceof McpError ? handledError.code : -32603; c.status(status); const errorResponse = { jsonrpc: "2.0", error: { code: errorCode, message: handledError.message, }, id: requestId, }; logger.info(`Sending formatted error response for request.`, { ...context, status, errorCode, jsonRpcId: requestId, }); return c.json(errorResponse); };