UNPKG

@cyanheads/pubmed-mcp-server

Version:

Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms via MCP. STDIO or Streamable HTTP.

112 lines 5.68 kB
/** * @fileoverview Canonical service-layer error contracts. Single source of truth * for the failure modes both services throw and tools declare in `errors[]`. * * Service-layer code can't reach `ctx.recoveryFor` (no Context), so it spreads * `recoveryFor(reason)` from this module into the error factory's `data` arg. * The framework mirrors `data.recovery.hint` into the wire payload's * `content[]` text, so clients get the same actionable hint they would from a * handler-level `ctx.fail`. * * Tool definitions import the contract arrays directly and spread them into * their `errors: [...]` declarations to surface the failure modes to the LLM. * * @module src/services/error-contracts */ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors'; /** * Failure modes the NCBI service layer can surface. Tools that consume * `getNcbiService()` should spread these into their own `errors[]` so the * declared contract matches what actually reaches the wire. */ export declare const NCBI_SERVICE_ERRORS: readonly [{ readonly reason: "queue_full"; readonly code: JsonRpcErrorCode.RateLimited; readonly when: "Local NCBI request queue is at capacity."; readonly recovery: "Retry after 1-2 seconds; the request queue hit the NCBI rate limit."; readonly retryable: true; }, { readonly reason: "ncbi_unreachable"; readonly code: JsonRpcErrorCode.ServiceUnavailable; readonly when: "NCBI E-utilities is unreachable after all retry attempts."; readonly recovery: "Retry after a brief delay; NCBI was unreachable across all retry attempts."; readonly retryable: true; }, { readonly reason: "ncbi_deadline_exceeded"; readonly code: JsonRpcErrorCode.Timeout; readonly when: "Total request deadline expired before NCBI returned a response."; readonly recovery: "Reduce batch size or retry; NCBI may be under temporary load."; readonly retryable: true; }, { readonly reason: "ncbi_invalid_response"; readonly code: JsonRpcErrorCode.SerializationError; readonly when: "NCBI returned a body that could not be parsed (invalid XML/JSON)."; readonly recovery: "Retry the request; NCBI returned a malformed response that could not be parsed."; readonly retryable: true; }, { readonly reason: "ncbi_resource_not_found"; readonly code: JsonRpcErrorCode.NotFound; readonly when: "NCBI returned a structured \"not found\" error for the requested ID(s)."; readonly recovery: "Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help."; readonly retryable: false; }]; /** * Failure modes the Unpaywall service layer can surface. Tools that consume * `getUnpaywallService()` (currently `pubmed_fetch_fulltext`) should spread * these into their `errors[]`. */ export declare const UNPAYWALL_SERVICE_ERRORS: readonly [{ readonly reason: "unpaywall_unreachable"; readonly code: JsonRpcErrorCode.ServiceUnavailable; readonly when: "Unpaywall was unreachable when resolving a DOI or fetching content."; readonly recovery: "Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path."; readonly retryable: true; }]; /** * Failure modes the Europe PMC service layer can surface. Tools that consume * `getEuropePmcService()` should spread these into their `errors[]`. */ export declare const EUROPEPMC_SERVICE_ERRORS: readonly [{ readonly reason: "europepmc_unreachable"; readonly code: JsonRpcErrorCode.ServiceUnavailable; readonly when: "Europe PMC was unreachable after all retry attempts."; readonly recovery: "Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available."; readonly retryable: true; }, { readonly reason: "europepmc_invalid_response"; readonly code: JsonRpcErrorCode.SerializationError; readonly when: "Europe PMC returned a body that could not be parsed (invalid JSON or XML)."; readonly recovery: "Retry the request; Europe PMC returned a malformed response that could not be parsed."; readonly retryable: true; }, { readonly reason: "europepmc_invalid_input"; readonly code: JsonRpcErrorCode.ValidationError; readonly when: "Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter)."; readonly recovery: "Adjust the input — usually the query or sort field — before retrying; the same input will be rejected again."; readonly retryable: false; }]; /** * Reason identifier union for type-safe authoring on the service layer. * Tool handlers get a tighter typed union from `ctx.fail` / `ctx.recoveryFor` * via the framework — this is the service-side fallback. */ export type ServiceErrorReason = (typeof NCBI_SERVICE_ERRORS)[number]['reason'] | (typeof UNPAYWALL_SERVICE_ERRORS)[number]['reason'] | (typeof EUROPEPMC_SERVICE_ERRORS)[number]['reason']; /** * Service-layer counterpart to `ctx.recoveryFor`. Returns `{ recovery: { hint } }` * for the contract reason. Use at every service throw that stamps a `reason` so * the wire payload carries the same actionable hint the LLM gets from * handler-level `ctx.fail`. * * The parameter is constrained to `ServiceErrorReason`, so typos fail at compile * time. The runtime guard catches the impossible case where the reason union * and the recovery map drift apart in future edits. * * @example * throw serviceUnavailable(msg, { reason: 'ncbi_unreachable', ...recoveryFor('ncbi_unreachable') }); */ export declare function recoveryFor(reason: ServiceErrorReason): { recovery: { hint: string; }; }; //# sourceMappingURL=error-contracts.d.ts.map