@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.
190 lines • 9.16 kB
TypeScript
/**
* @fileoverview Full-text fetch tool. Resolves full-text articles through a
* three-stage chain: NCBI PMC EFetch → Europe PMC `fullTextXML` → Unpaywall.
* Accepts three mutually-exclusive input shapes:
*
* - `pmcids` — fetch directly by PMC ID. Articles not in PMC fall through to
* EPMC by PMC ID, then to Unpaywall when the DOI is available.
* - `pmids` — resolve PMID → PMCID via PMC ID Converter, then run the chain.
* - `dois` — skip PMC EFetch (no PMCID); resolve via EPMC search-by-DOI →
* fullTextXML, then Unpaywall. Covers EPMC-only OA and preprints with no
* PubMed presence.
*
* Output uses a discriminated union on `source` (`pmc` | `unpaywall`) with an
* extra `viaSource` discriminator that records which layer produced the
* content. EPMC's JATS reuses the `pmc` schema shape because it's the same
* DTD; `viaSource: 'europepmc'` distinguishes it from PMC EFetch output.
*
* @module src/mcp-server/tools/definitions/fetch-fulltext.tool
*/
import { z } from '@cyanheads/mcp-ts-core';
export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
pmcids: z.ZodOptional<z.ZodArray<z.ZodString>>;
pmids: z.ZodOptional<z.ZodArray<z.ZodString>>;
dois: z.ZodOptional<z.ZodArray<z.ZodString>>;
includeReferences: z.ZodDefault<z.ZodBoolean>;
maxSections: z.ZodOptional<z.ZodNumber>;
sections: z.ZodOptional<z.ZodArray<z.ZodString>>;
}, z.core.$strip>, z.ZodObject<{
articles: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
source: z.ZodLiteral<"pmc">;
viaSource: z.ZodEnum<{
pmc: "pmc";
europepmc: "europepmc";
}>;
pmcId: z.ZodOptional<z.ZodString>;
pmcUrl: z.ZodOptional<z.ZodString>;
pmid: z.ZodOptional<z.ZodString>;
pubmedUrl: z.ZodOptional<z.ZodString>;
doi: z.ZodOptional<z.ZodString>;
title: z.ZodOptional<z.ZodString>;
abstract: z.ZodOptional<z.ZodString>;
authors: z.ZodOptional<z.ZodArray<z.ZodObject<{
collectiveName: z.ZodOptional<z.ZodString>;
givenNames: z.ZodOptional<z.ZodString>;
lastName: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>>;
affiliations: z.ZodOptional<z.ZodArray<z.ZodString>>;
journal: z.ZodOptional<z.ZodObject<{
title: z.ZodOptional<z.ZodString>;
issn: z.ZodOptional<z.ZodString>;
volume: z.ZodOptional<z.ZodString>;
issue: z.ZodOptional<z.ZodString>;
pages: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>;
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
articleType: z.ZodOptional<z.ZodString>;
publicationDate: z.ZodOptional<z.ZodObject<{
year: z.ZodOptional<z.ZodString>;
month: z.ZodOptional<z.ZodString>;
day: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>;
sections: z.ZodArray<z.ZodObject<{
title: z.ZodOptional<z.ZodString>;
label: z.ZodOptional<z.ZodString>;
text: z.ZodString;
subsections: z.ZodOptional<z.ZodArray<z.ZodObject<{
title: z.ZodOptional<z.ZodString>;
label: z.ZodOptional<z.ZodString>;
text: z.ZodString;
}, z.core.$strip>>>;
}, z.core.$strip>>;
references: z.ZodOptional<z.ZodArray<z.ZodObject<{
citation: z.ZodString;
id: z.ZodOptional<z.ZodString>;
label: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>>;
epmcId: z.ZodOptional<z.ZodString>;
epmcSource: z.ZodOptional<z.ZodString>;
}, z.core.$strip>, z.ZodObject<{
source: z.ZodLiteral<"unpaywall">;
viaSource: z.ZodLiteral<"unpaywall">;
contentFormat: z.ZodEnum<{
"html-markdown": "html-markdown";
"pdf-text": "pdf-text";
}>;
pmid: z.ZodOptional<z.ZodString>;
pubmedUrl: z.ZodOptional<z.ZodString>;
doi: z.ZodString;
sourceUrl: z.ZodString;
title: z.ZodOptional<z.ZodString>;
content: z.ZodString;
wordCount: z.ZodOptional<z.ZodNumber>;
totalPages: z.ZodOptional<z.ZodNumber>;
license: z.ZodOptional<z.ZodString>;
hostType: z.ZodOptional<z.ZodString>;
version: z.ZodOptional<z.ZodString>;
}, z.core.$strip>], "source">>;
totalReturned: z.ZodNumber;
unavailable: z.ZodOptional<z.ZodArray<z.ZodObject<{
id: z.ZodString;
idType: z.ZodEnum<{
pmid: "pmid";
pmcid: "pmcid";
doi: "doi";
}>;
reason: z.ZodEnum<{
"no-oa": "no-oa";
"not-found": "not-found";
"no-pmc-fallback-disabled": "no-pmc-fallback-disabled";
"no-epmc-fulltext": "no-epmc-fulltext";
"no-doi": "no-doi";
"fetch-failed": "fetch-failed";
"parse-failed": "parse-failed";
"service-error": "service-error";
}>;
triedTiers: z.ZodArray<z.ZodObject<{
tier: z.ZodEnum<{
pmc: "pmc";
europepmc: "europepmc";
unpaywall: "unpaywall";
}>;
outcome: z.ZodEnum<{
"no-oa": "no-oa";
"no-doi": "no-doi";
"fetch-failed": "fetch-failed";
"parse-failed": "parse-failed";
"service-error": "service-error";
"not-attempted": "not-attempted";
miss: "miss";
"no-fulltext": "no-fulltext";
}>;
detail: z.ZodOptional<z.ZodString>;
}, z.core.$strip>>;
}, z.core.$strip>>>;
}, z.core.$strip>, readonly [{
readonly reason: "queue_full";
readonly code: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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;
}, {
readonly reason: "unpaywall_unreachable";
readonly code: import("@cyanheads/mcp-ts-core/errors").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;
}, {
readonly reason: "europepmc_unreachable";
readonly code: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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: import("@cyanheads/mcp-ts-core/errors").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;
}]>;
//# sourceMappingURL=fetch-fulltext.tool.d.ts.map