@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
TypeScript
/**
* @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