@cyanheads/pubmed-mcp-server 2.10.17 → 2.10.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/AGENTS.md +12 -14
  2. package/CLAUDE.md +12 -14
  3. package/Dockerfile +77 -36
  4. package/README.md +86 -109
  5. package/changelog/2.10.x/2.10.17.md +1 -1
  6. package/changelog/2.10.x/2.10.18.md +14 -0
  7. package/changelog/2.10.x/2.10.19.md +42 -0
  8. package/dist/config/server-config.d.ts.map +1 -1
  9. package/dist/config/server-config.js +7 -3
  10. package/dist/config/server-config.js.map +1 -1
  11. package/dist/mcp-server/resources/definitions/database-info.resource.d.ts +43 -1
  12. package/dist/mcp-server/resources/definitions/database-info.resource.d.ts.map +1 -1
  13. package/dist/mcp-server/resources/definitions/database-info.resource.js +2 -0
  14. package/dist/mcp-server/resources/definitions/database-info.resource.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/_visible-text.d.ts +19 -0
  16. package/dist/mcp-server/tools/definitions/_visible-text.d.ts.map +1 -0
  17. package/dist/mcp-server/tools/definitions/_visible-text.js +22 -0
  18. package/dist/mcp-server/tools/definitions/_visible-text.js.map +1 -0
  19. package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts +11 -4
  20. package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/convert-ids.tool.js +1 -1
  22. package/dist/mcp-server/tools/definitions/convert-ids.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +20 -7
  24. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
  25. package/dist/mcp-server/tools/definitions/fetch-articles.tool.js +40 -5
  26. package/dist/mcp-server/tools/definitions/fetch-articles.tool.js.map +1 -1
  27. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +11 -4
  28. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
  29. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +18 -1
  30. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
  31. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/find-related.tool.js +39 -10
  33. package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
  34. package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +11 -4
  35. package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts +16 -15
  37. package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
  38. package/dist/mcp-server/tools/definitions/lookup-citation.tool.js +40 -22
  39. package/dist/mcp-server/tools/definitions/lookup-citation.tool.js.map +1 -1
  40. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +13 -6
  41. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
  42. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js +8 -8
  43. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js.map +1 -1
  44. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +2 -2
  45. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
  46. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js +1 -1
  47. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js.map +1 -1
  48. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +19 -5
  49. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -1
  50. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +37 -7
  51. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -1
  52. package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +40 -18
  53. package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
  54. package/dist/mcp-server/tools/definitions/search-articles.tool.js +193 -43
  55. package/dist/mcp-server/tools/definitions/search-articles.tool.js.map +1 -1
  56. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +13 -6
  57. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/definitions/spell-check.tool.js +8 -8
  59. package/dist/mcp-server/tools/definitions/spell-check.tool.js.map +1 -1
  60. package/dist/services/error-contracts.d.ts +42 -71
  61. package/dist/services/error-contracts.d.ts.map +1 -1
  62. package/dist/services/error-contracts.js +43 -75
  63. package/dist/services/error-contracts.js.map +1 -1
  64. package/dist/services/europe-pmc/api-client.d.ts +8 -2
  65. package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
  66. package/dist/services/europe-pmc/api-client.js +13 -8
  67. package/dist/services/europe-pmc/api-client.js.map +1 -1
  68. package/dist/services/europe-pmc/europe-pmc-service.d.ts +43 -14
  69. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
  70. package/dist/services/europe-pmc/europe-pmc-service.js +165 -85
  71. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
  72. package/dist/services/europe-pmc/request-queue.d.ts +11 -27
  73. package/dist/services/europe-pmc/request-queue.d.ts.map +1 -1
  74. package/dist/services/europe-pmc/request-queue.js +17 -104
  75. package/dist/services/europe-pmc/request-queue.js.map +1 -1
  76. package/dist/services/europe-pmc/types.d.ts +6 -1
  77. package/dist/services/europe-pmc/types.d.ts.map +1 -1
  78. package/dist/services/ncbi/api-client.d.ts.map +1 -1
  79. package/dist/services/ncbi/api-client.js +2 -3
  80. package/dist/services/ncbi/api-client.js.map +1 -1
  81. package/dist/services/ncbi/ncbi-service.d.ts +2 -1
  82. package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
  83. package/dist/services/ncbi/ncbi-service.js +67 -20
  84. package/dist/services/ncbi/ncbi-service.js.map +1 -1
  85. package/dist/services/ncbi/parsing/article-parser.d.ts +15 -1
  86. package/dist/services/ncbi/parsing/article-parser.d.ts.map +1 -1
  87. package/dist/services/ncbi/parsing/article-parser.js +29 -0
  88. package/dist/services/ncbi/parsing/article-parser.js.map +1 -1
  89. package/dist/services/ncbi/response-handler.d.ts.map +1 -1
  90. package/dist/services/ncbi/response-handler.js +22 -19
  91. package/dist/services/ncbi/response-handler.js.map +1 -1
  92. package/dist/services/ncbi/types.d.ts +33 -0
  93. package/dist/services/ncbi/types.d.ts.map +1 -1
  94. package/dist/services/openalex/api-client.d.ts.map +1 -1
  95. package/dist/services/openalex/api-client.js +6 -7
  96. package/dist/services/openalex/api-client.js.map +1 -1
  97. package/dist/services/openalex/openalex-service.d.ts +11 -1
  98. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  99. package/dist/services/openalex/openalex-service.js +18 -11
  100. package/dist/services/openalex/openalex-service.js.map +1 -1
  101. package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
  102. package/dist/services/unpaywall/unpaywall-service.js +1 -3
  103. package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
  104. package/package.json +6 -5
  105. package/server.json +3 -3
package/README.md CHANGED
@@ -7,9 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
-
11
-
12
- [![Version](https://img.shields.io/badge/Version-2.10.17-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pubmed-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/pubmed-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pubmed-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-2.10.19-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pubmed-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.1.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/pubmed-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pubmed-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.2-blueviolet.svg?style=flat-square)](https://bun.sh/)
13
11
 
14
12
  </div>
15
13
 
@@ -31,23 +29,23 @@
31
29
 
32
30
  ## Overview
33
31
 
34
- The biomedical literature via NCBI's E-utilities, PubMed Central, and Europe PMC. Search it, fetch metadata and full text, resolve identifiers and partial citations, format references, and ground queries in MeSH vocabulary. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
32
+ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fetch metadata and full text, resolve identifiers and partial citations, format references, and ground queries in MeSH vocabulary. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
35
33
 
36
34
  ### Tools
37
35
 
38
36
  | Tool | Description |
39
37
  |:---|:---|
40
- | `pubmed_search_articles` | Search PubMed with full query syntax, field-specific filters, date ranges, pagination, and optional brief summaries |
41
- | `pubmed_europepmc_search` | Search Europe PMC for preprints, patents, Agricola, and EPMC-only OA records that don't surface in PubMed. Cursor-based pagination. |
42
- | `pubmed_europepmc_fetch` | Fetch complete Europe PMC records — including the untruncated abstract — by `source` + `epmcId`, the only identifier many preprint, patent, and Agricola records carry |
43
- | `pubmed_fetch_articles` | Fetch full article metadata by PMIDs — abstract, authors, journal, MeSH terms, grants |
44
- | `pubmed_fetch_fulltext` | Fetch full-text articles via a chain: NCBI PMC EFetch → Europe PMC `fullTextXML` → Unpaywall. Accepts PMIDs, PMCIDs, or DOIs. |
45
- | `pubmed_format_citations` | Generate formatted citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM) |
46
- | `pubmed_find_related` | Find similar articles, citing articles, or references for a given PMID |
47
- | `pubmed_spell_check` | Spell-check a PubMed query via NCBI ESpell — every misspelled token corrected in one call; the recovery step after a zero-hit or thin search |
48
- | `pubmed_lookup_mesh` | Search MeSH by heading — tree numbers, scope notes, entry terms — for building controlled-vocabulary queries |
49
- | `pubmed_lookup_citation` | Resolve partial bibliographic references — one citation or a batch of up to 25 — to PubMed IDs via ECitMatch |
50
- | `pubmed_convert_ids` | Convert between DOI, PMID, and PMCID using the PMC ID Converter API |
38
+ | `pubmed_search_articles` | Search PubMed with full query syntax, structured filters, date ranges, and optional summaries |
39
+ | `pubmed_fetch_articles` | Fetch article metadata by PMID: abstract, authors, journal, MeSH terms, grants, linked retraction and correction notices |
40
+ | `pubmed_fetch_fulltext` | Fetch full text by PMID, PMCID, or DOI, falling back from PMC to Europe PMC to Unpaywall |
41
+ | `pubmed_europepmc_search` | Search Europe PMC for preprints, patents, Agricola, and open-access records PubMed doesn't carry |
42
+ | `pubmed_europepmc_fetch` | Fetch complete Europe PMC records, including the untruncated abstract, by `source` + `epmcId` |
43
+ | `pubmed_format_citations` | Format citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM) |
44
+ | `pubmed_find_related` | Find similar articles, citing articles, or references for a PMID |
45
+ | `pubmed_spell_check` | Correct a misspelled PubMed query via NCBI ESpell |
46
+ | `pubmed_lookup_mesh` | Look up MeSH headings with tree numbers, scope notes, and entry terms |
47
+ | `pubmed_lookup_citation` | Resolve partial references, up to 25 at a time, to PMIDs via ECitMatch |
48
+ | `pubmed_convert_ids` | Convert between DOI, PMID, and PMCID via the PMC ID Converter |
51
49
 
52
50
  ### Resources
53
51
 
@@ -59,131 +57,103 @@ The biomedical literature via NCBI's E-utilities, PubMed Central, and Europe PMC
59
57
 
60
58
  | Prompt | Description |
61
59
  |:---|:---|
62
- | `research_plan` | Generate a structured 4-phase biomedical research plan outline |
60
+ | `research_plan` | Generate a structured four-phase biomedical research plan |
63
61
 
64
62
  ## Capability reference
65
63
 
66
64
  ### `pubmed_search_articles` <sub>tool</sub>
67
65
 
68
- - Full PubMed boolean and field-tag syntax, plus structured filters: author, journal, MeSH terms, language, species, publication type, has-abstract, free-full-text
69
- - Date ranges by publication, modification, or Entrez date; sort by relevance, date, author, or journal; offset pagination
70
- - Optional brief summaries for the top N results via ESummary
71
- - NCBI Bookshelf hits carry `bookTitle`, `publisherName`, `docType`, and `editors` in place of the empty `source`; the rendered summary shows the doc type only for these, not for ordinary journal articles (`citation`)
72
- - Reports `totalCount`, stated in the header beside the page (`Returned: 3 of 2924`); echoes the original query, the fully applied PubMed query, and normalized filter metadata
73
- - A query with no search term — blank, markup only, a bare field tag like `[pdat]`, or empty `()` — is rejected as `blank_query` rather than sent upstream
74
- - `limit` is accepted for `maxResults`
66
+ - Full PubMed query syntax plus filters (author, journal, MeSH, publication type, language, species, abstract, free full text) and publication/modification/Entrez date ranges
67
+ - Up to 1,000 results per page and offsets up to 9,998; `summaryCount` adds briefs for up to 50 hits
68
+ - Reports `totalCount`, the `effectiveQuery` PubMed ran, and the `appliedFilters`
69
+ - Rejects blank filter values, impossible dates, and reversed date ranges before PubMed is called
75
70
 
76
71
  ---
77
72
 
78
73
  ### `pubmed_fetch_articles` <sub>tool</sub>
79
74
 
80
- - Up to 200 PMIDs per call (POST for batches of 100 or more); `ids` is accepted for `pmids`
81
- - A zero-padded PMID (`00000001`) resolves as the PMID it spells; `unavailablePmids` lists misses as you sent them
82
- - Title, abstract, authors with deduplicated affiliations, journal info, DOI, PubMed/PMC links; optional MeSH terms, grants, and publication types
83
- - Tolerant of PubMed's inconsistent XML — structured abstracts, missing fields, varying date formats
84
- - Bookshelf chapters and books are first-class: `recordType` (`journal-article` / `book-chapter` / `book`) plus a `book` object (title, publisher, editors, ISBNs, Bookshelf accession); `journalInfo` is absent on them
85
- - Article-number journals report `journalInfo.elocationId` + `elocationIdType` rather than a page range
86
- - Opt-in `maxResponseCharacters` keeps whole records in order until the ceiling, then defers the rest to `deferred.ids` for a follow-up call
75
+ - Up to 200 PMIDs per call; misses are listed in `unavailablePmids`
76
+ - Abstract, authors, journal, DOI, publication types, linked retraction/erratum/comment notices, and links; MeSH terms on by default, grants via `includeGrants`
77
+ - `recordType` separates journal articles from Bookshelf chapters and books; opt-in `maxResponseCharacters` defers overflow to `deferred.ids`
87
78
 
88
79
  ---
89
80
 
90
81
  ### `pubmed_fetch_fulltext` <sub>tool</sub>
91
82
 
92
- - Exactly one of `pmcids`, `pmids`, or `dois` (one id per element), up to 10 per request; a zero-padded PMID or PMC ID resolves as the ID it spells, DOIs match case-insensitively, and PMC IDs match with or without the `PMC` prefix in any case. Each record is fetched once however many spellings name it; `unavailable[].id` keeps your PMID or DOI spelling, and reports a PMC ID as `PMC<digits>`
93
- - Three-tier chain: NCBI PMC EFetch → Europe PMC `fullTextXML` (`EUROPEPMC_ENABLED`, default on) → Unpaywall (needs `UNPAYWALL_EMAIL`); `viaSource` names which tier served each article
94
- - Preprints, patents, and Agricola records have metadata via `pubmed_europepmc_search` but no full text through this chain — Europe PMC's `fullTextXML` is PMC-keyed
95
- - `source: "pmc"` returns structured sections plus `tables[]` (cells, caption, label, footnotes) and `assets[]` (figures and supplementary material, with `[Figure: <label>]` markers left in the body, and the file pointer read through `<alternatives>` when a figure offers several formats); `source: "unpaywall"` returns a best-effort body with `contentFormat` (`html-markdown` / `pdf-text`), a `title` from Unpaywall's record (else the Europe PMC record, else the HTML page), and `journalName` / `year` when Unpaywall has them
96
- - A titled list, definition list, or boxed text that sits in the body outside any section, or is the only content of an untitled section, such as an abbreviations list, becomes a section under its own title, and a figure or table inside it names that section; a section with no title is headed `untitled section`, the same name the `truncation` ledger gives it, and section titles in headings and ledger lines are Markdown-escaped
97
- - Unavailable entries carry a typed `reason` (`not-found`, `no-doi`, `doi-lookup-failed`, `no-oa`, `service-error`, …), `idType`, `triedTiers` (per-tier outcome in execution order), and `unqueriedTiers` when an unconfigured tier could have served the id
98
- - Filters and budgets: `sections` (case-insensitive title match), `maxSections`, `includeTables`, `includeAssets`, `maxCharacters`, `maxCharactersPerSection`, `overflowMode` (`truncate` / `outline`), and `maxResponseCharacters`, which defers whole articles past the ceiling to `deferred.ids`; a `truncation` object reports what was shortened or omitted, per section and subsection
99
- - A budget cut ends at a word boundary; `truncate` drops every section and subsection past the cut, while `outline` keeps each heading and marks one the budget left empty
83
+ - One of `pmcids`, `pmids`, or `dois`, up to 10 per call, tried against PMC, then Europe PMC, then Unpaywall (needs `UNPAYWALL_EMAIL`); `viaSource` names the tier that answered
84
+ - `source: "pmc"` returns sections, tables, and figures; `source: "unpaywall"` returns a best-effort HTML or PDF text body
85
+ - Misses carry a typed `reason` and `triedTiers`; `sections`, `maxCharacters`, `overflowMode`, and `maxResponseCharacters` bound the response, and `truncation` reports what was cut
100
86
 
101
87
  ---
102
88
 
103
89
  ### `pubmed_europepmc_search` <sub>tool</sub>
104
90
 
105
- - Reaches records PubMed can't: preprints (`PPR`), patents (`PAT`), Agricola (`AGR`), alongside `MED` and `PMC`; default `sources` is `["MED", "PMC", "PPR"]`
106
- - Cursor pagination via `cursorMark` — `*` for the first page, then `nextCursorMark`; `pageSize` up to 100, with `max_results` and `limit` accepted for it
107
- - Hits carry `source` plus `pmid` / `pmcId` / `doi` when known; `abstractSnippet` is capped at 400 characters, with `abstractTruncated` flagging the cut
108
- - `totalCount` reports the full hit count, stated in the header beside the page; `searchUrl` opens the same source-filtered query on europepmc.org
91
+ - Adds preprints (`PPR`), patents (`PAT`), and Agricola (`AGR`) to `MED` and `PMC`; defaults to `MED`, `PMC`, `PPR`
92
+ - Cursor pagination via `cursorMark` / `nextCursorMark`, up to 100 per page; abstracts arrive as 400-character snippets
109
93
  - Not registered when `EUROPEPMC_ENABLED=false`
110
94
 
111
95
  ---
112
96
 
113
97
  ### `pubmed_europepmc_fetch` <sub>tool</sub>
114
98
 
115
- - Full records with the untruncated plain-text abstract, addressed by `source` + `epmcId` — the only identifier preprint, patent, and Agricola records reliably carry
116
- - Up to 25 per call in one Europe PMC request; unresolved ids come back in `notFound` rather than failing the batch
99
+ - Up to 25 records per call, addressed by a search hit's `source` + `epmcId`, with the full abstract; unresolved ids land in `notFound`
117
100
  - Not registered when `EUROPEPMC_ENABLED=false`
118
101
 
119
102
  ---
120
103
 
121
104
  ### `pubmed_format_citations` <sub>tool</sub>
122
105
 
123
- - APA 7th, MLA 9th, BibTeX, RIS, Vancouver (ICMJE/NLM); several styles per article in one call, up to 50 articles
124
- - Bookshelf chapters and books cite in each style's edited-book form; articles without a page range cite by electronic locator in each style's convention
125
- - Hand-rolled formatters — zero dependencies, Workers-compatible
126
- - Reports formatted counts and unavailable PMIDs; `ids` is accepted for `pmids`, and a zero-padded PMID resolves as the PMID it spells
106
+ - Up to 50 PMIDs per call, in any mix of `apa`, `mla`, `bibtex`, `ris`, and `vancouver`
107
+ - Bookshelf records cite as edited books; PMIDs that can't be fetched are reported as unavailable
127
108
 
128
109
  ---
129
110
 
130
111
  ### `pubmed_find_related` <sub>tool</sub>
131
112
 
132
- - `similar`, `cited_by`, or `references` for a PMID, in NCBI relevance order, enriched with title, authors, date, and source (or Bookshelf book title and publisher)
133
- - Falls back to Europe PMC, then OpenAlex, when NCBI can't answer; the response names the provider. Fails with a typed `all_providers_failed` error rather than an empty result
134
- - `maxResults` up to 50 (`limit` also accepted) with offset pagination; `totalCount` reports the full match count, stated in the header beside the page
135
- - A zero-padded source PMID resolves as the PMID it spells and is never listed among its own related articles
113
+ - `similar`, `cited_by`, or `references`, up to 50 per page with offset pagination
114
+ - Falls back to Europe PMC, then OpenAlex, and names the provider that answered; fails as `all_providers_failed` if none can
136
115
 
137
116
  ---
138
117
 
139
118
  ### `pubmed_spell_check` <sub>tool</sub>
140
119
 
141
- - Runs a PubMed query through NCBI ESpell and returns `original`, `corrected`, and `hasSuggestion`; every misspelled token is corrected in one call (`alzhiemer diseese treatmnt outcomse` → `alzheimer disease treatment outcomes`)
142
- - Reach for it after a zero-hit or thin `pubmed_search_articles` result, or when a drug, gene, disease, or author name may be misspelled, then re-run the search with `corrected`
143
- - A blank or whitespace-only query is rejected rather than sent upstream
120
+ - Returns `original`, `corrected`, and `hasSuggestion`, fixing every misspelled token in one call
121
+ - Run it after a zero-hit or thin search, then retry `pubmed_search_articles` with `corrected`
144
122
 
145
123
  ---
146
124
 
147
125
  ### `pubmed_lookup_mesh` <sub>tool</sub>
148
126
 
149
- - Looks up MeSH descriptors by name or free-text term, pinning the exact-heading match to the top of the first page
150
- - Records carry `meshId` (DescriptorUI), `entrezUid`, and, with `includeDetails` (default on), tree numbers, scope notes, and entry terms
151
- - `maxResults` up to 50 (`limit` also accepted) with offset pagination via `nextOffset`; `totalCount` reports the upstream match count
127
+ - Descriptors by name or free-text term, with an exact heading match pinned first; up to 50 per page, continued via `nextOffset`
128
+ - Records carry `meshId`; `includeDetails` (default on) adds tree numbers, scope notes, and entry terms
152
129
 
153
130
  ---
154
131
 
155
132
  ### `pubmed_lookup_citation` <sub>tool</sub>
156
133
 
157
- - Match on journal, year, volume, first page, and/or author — journal or year required, more fields for better precision
158
- - `citations` takes an array of up to 25 or a single citation object; `citation` is accepted for it
159
- - Pipes and line breaks are rejected at the schema (ECitMatch's wire format is pipe-delimited); the free-form `key` label is exempt
160
- - Explicit `matched`, `not_found`, and `ambiguous` statuses with recovery detail
134
+ - Up to 25 partial citations per call; journal or year is required, and volume, first page, and author sharpen the match
135
+ - Each comes back `matched`, `not_found`, or `ambiguous`, with recovery detail
161
136
 
162
137
  ---
163
138
 
164
139
  ### `pubmed_convert_ids` <sub>tool</sub>
165
140
 
166
- - Up to 50 DOIs, PMIDs, or PMCIDs per call, all one type; only PMC-indexed articles resolve
167
- - One id per element — a packed `"23193287,37952131"` is rejected rather than expanded
168
- - One success/error row per submitted element, in order, with `requestedId` exactly as sent — repeats, a bare-digit PMCID, and a DOI's casing included; a partial batch never fails as a whole
169
- - A zero-padded PMID resolves as the PMID it spells
141
+ - Up to 50 ids per call, all of one declared `idType` (`doi`, `pmid`, `pmcid`); only PMC-indexed articles resolve
142
+ - One success or error row per id, in order, so a partial batch never fails
170
143
 
171
144
  ---
172
145
 
173
146
  ### `pubmed://database/info` <sub>resource</sub>
174
147
 
175
148
  - Live EInfo call for the `pubmed` database, returned as `application/json`
176
- - `dbName`, `description`, `count`, `lastUpdate`, and `fields[]` — each field's short `name` (the tag usable in `pubmed_search_articles` queries), `fullName`, and `description`
177
- - No parameters
149
+ - `count`, `lastUpdate`, and `fields[]`, whose names are the search tags `pubmed_search_articles` accepts
178
150
 
179
151
  ---
180
152
 
181
153
  ### `research_plan` <sub>prompt</sub>
182
154
 
183
- - Arguments: `title`, `goal`, `keywords` (comma-separated) required; `organism` and `includeAgentPrompts` (`"true"` / `"false"`) optional
184
- - Returns two messages: an assistant framing message (biomedical research planning assistant, grounds recommendations in the PubMed tools when available) and a user message carrying the plan
185
- - The plan walks four phases — Conception & Planning, Data Collection & Processing, Analysis & Interpretation, Dissemination — with sub-steps under each
186
- - `includeAgentPrompts: "true"` adds an agent-guidance block under each sub-step, several of which point at `pubmed_search_articles` and `pubmed_lookup_mesh`
155
+ - Arguments: `title`, `goal`, and `keywords` required; `organism` and `includeAgentPrompts` optional
156
+ - Returns a four-phase research plan; `includeAgentPrompts: "true"` adds an agent-guidance block under each of its nine sub-steps, two of which point at tools: the literature review names `pubmed_search_articles` and `pubmed_lookup_mesh`, and the interpretation step names `pubmed_search_articles`
187
157
 
188
158
  ## Features
189
159
 
@@ -191,16 +161,17 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
191
161
 
192
162
  PubMed-specific:
193
163
 
194
- - Complete NCBI E-utilities integration (ESearch, EFetch, ESummary, ELink, ESpell, EInfo, ECitMatch) plus PMC ID Converter
195
- - Shared NCBI request queue — paced request starts, capped concurrency, a cooldown that holds every caller after an NCBI 429, and one deadline covering queue wait and retries
196
- - NCBI-specific XML parser with `isArray` hints for PubMed's inconsistent XML structure
197
- - Hand-rolled citation formatters (APA, MLA, BibTeX, RIS, Vancouver) — zero deps, Workers-compatible
164
+ - NCBI E-utilities (ESearch, EFetch, ESummary, ELink, ESpell, EInfo, ECitMatch) and the PMC ID Converter, with Europe PMC, OpenAlex, and Unpaywall filling the gaps
165
+ - Shared NCBI request queue: paced request starts, capped concurrency, a cooldown that holds every caller after an NCBI 429, and one deadline covering queue wait and retries
166
+ - XML parsing built for PubMed's inconsistent records: structured abstracts, missing fields, varying date formats
167
+ - Forgiving identifiers: a zero-padded PMID resolves as the PMID it spells in every tool that takes one; `pubmed_fetch_fulltext` also matches DOIs case-insensitively and PMC IDs with or without the `PMC` prefix
168
+ - Hand-rolled citation formatters (APA, MLA, BibTeX, RIS, Vancouver) with no dependencies
198
169
 
199
170
  Agent-friendly output:
200
171
 
201
- - Provenance on every response — source labels, license fields, best-effort warnings on Unpaywall results, and effective-query echo on searches so agents can reason about trust
202
- - Graceful partial failure — batch tools return per-item success/error rows instead of failing the request, with structured status codes and actionable next-step text
203
- - Discriminated output contracts — `source: "pmc" | "unpaywall"`, typed `unavailable` reasons, `viaSource` and `triedTiers` fields — callers branch on data, not string parsing
172
+ - Provenance on every response: source labels, license fields, best-effort warnings on Unpaywall results, and effective-query echo on searches, so agents can judge what to trust
173
+ - Graceful partial failure: batch tools return per-item success/error rows instead of failing the request, with structured status codes and actionable next-step text
174
+ - Discriminated output contracts: `source: "pmc" | "unpaywall"`, typed `unavailable` reasons, `viaSource` and `triedTiers` fields, so callers branch on data, not string parsing
204
175
 
205
176
  ## Getting started
206
177
 
@@ -282,8 +253,8 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
282
253
 
283
254
  ### Prerequisites
284
255
 
285
- - [Bun v1.4.0](https://bun.sh/) or higher.
286
- - Optional: [NCBI API key](https://www.ncbi.nlm.nih.gov/account/settings/) for higher rate limits (10 req/s vs 3 req/s).
256
+ - [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
257
+ - Optional: an [NCBI API key](https://www.ncbi.nlm.nih.gov/account/settings/) raises the rate limit from 3 to 10 requests per second.
287
258
 
288
259
  ### Installation
289
260
 
@@ -305,37 +276,41 @@ cd pubmed-mcp-server
305
276
  bun install
306
277
  ```
307
278
 
308
- ## Configuration
279
+ 4. **Configure environment:**
309
280
 
310
- Key environment variables:
281
+ ```sh
282
+ cp .env.example .env
283
+ # edit .env and set NCBI_API_KEY, NCBI_ADMIN_EMAIL, and UNPAYWALL_EMAIL as needed
284
+ ```
285
+
286
+ ## Configuration
311
287
 
312
288
  | Variable | Description | Default |
313
289
  |:---|:---|:---|
314
- | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http` | `stdio` |
315
- | `MCP_HTTP_PORT` | HTTP server port | `3010` |
316
- | `MCP_HTTP_ENDPOINT_PATH` | HTTP endpoint path where the MCP server is mounted | `/mcp` |
317
- | `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto` (resolves to `stateful`). This server ships `stateless` — it has no `ctx.requestInput` call sites. | `stateless` |
318
- | `MCP_PUBLIC_URL` | Public origin override for TLS-terminating reverse-proxy deployments (landing page, Server Card, RFC 9728 metadata). | none |
319
- | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth` | `none` |
320
- | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.) | `info` |
321
- | `MCP_GC_PRESSURE_INTERVAL_MS` | Opt-in Bun-only forced-GC pressure loop (ms). Drains the per-request `McpServer`/`McpSessionTransport` cycle under sustained low-traffic HTTP. Recommended starting point if heap growth is observed: `60000`. | `0` (disabled) |
322
- | `LOGS_DIR` | Directory for log files (Node.js only). Relative paths resolve against the application root. | `<app-root>/logs` |
323
- | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
324
- | `NCBI_API_KEY` | NCBI API key for higher rate limits (10 req/s vs 3 req/s) | none |
325
- | `NCBI_ADMIN_EMAIL` | Contact email sent with NCBI requests (recommended by NCBI) | none |
326
- | `NCBI_REQUEST_DELAY_MS` | Minimum gap between NCBI request starts in ms | 334 (100 with key) |
327
- | `NCBI_MAX_CONCURRENT` | Max concurrent in-flight NCBI requests | `8` |
328
- | `NCBI_MAX_RETRIES` | Retry attempts for failed NCBI requests | 6 |
329
- | `NCBI_TIMEOUT_MS` | Per-request HTTP timeout in ms | `30000` |
330
- | `NCBI_TOTAL_DEADLINE_MS` | Total deadline for one NCBI call — queue wait, retry attempts, and backoff — in ms. A call the queue cannot start before it is rejected at once | `60000` |
331
- | `UNPAYWALL_EMAIL` | Contact email for Unpaywall. When set, `pubmed_fetch_fulltext` falls back to Unpaywall open-access copies for non-PMC DOIs | none |
332
- | `UNPAYWALL_TIMEOUT_MS` | Per-request HTTP timeout for Unpaywall lookups and content fetches, in ms | `20000` |
333
- | `EUROPEPMC_ENABLED` | Enable Europe PMC search tool and the `pubmed_fetch_fulltext` JATS fallback chain. Set `false` to disable all EPMC calls and skip tool registration. | `true` |
334
- | `EUROPEPMC_EMAIL` | Optional contact email sent with Europe PMC requests (EBI courtesy). | none |
335
- | `EUROPEPMC_REQUEST_DELAY_MS` | Minimum gap between Europe PMC request starts in ms | `200` |
336
- | `EUROPEPMC_MAX_RETRIES` | Retry attempts for failed Europe PMC requests | `3` |
337
- | `EUROPEPMC_TIMEOUT_MS` | Per-request HTTP timeout for Europe PMC calls, in ms | `20000` |
338
- | `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
290
+ | `NCBI_API_KEY` | NCBI API key; raises the rate limit from 3 to 10 req/s. | none |
291
+ | `NCBI_ADMIN_EMAIL` | Contact email sent with NCBI requests, as NCBI recommends. | none |
292
+ | `NCBI_REQUEST_DELAY_MS` | Minimum gap between NCBI request starts, in ms. | `400` (`100` with a key) |
293
+ | `NCBI_MAX_CONCURRENT` | Max concurrent in-flight NCBI requests. | `8` |
294
+ | `NCBI_MAX_RETRIES` | Retry attempts for failed NCBI requests. | `6` |
295
+ | `NCBI_TIMEOUT_MS` | Per-request HTTP timeout, in ms. | `30000` |
296
+ | `NCBI_TOTAL_DEADLINE_MS` | Deadline for one NCBI call across queue wait, retries, and backoff, in ms. A call that can't start in time is rejected at once. | `60000` |
297
+ | `UNPAYWALL_EMAIL` | Contact email for Unpaywall. Setting it enables the Unpaywall tier of `pubmed_fetch_fulltext` for non-PMC DOIs. | none |
298
+ | `UNPAYWALL_TIMEOUT_MS` | Per-request timeout for Unpaywall lookups and content fetches, in ms. | `20000` |
299
+ | `EUROPEPMC_ENABLED` | Set `false` to disable every Europe PMC call: both Europe PMC tools go unregistered, `pubmed_fetch_fulltext` skips that tier, and `pubmed_find_related` skips that fallback. | `true` |
300
+ | `EUROPEPMC_EMAIL` | Optional contact email sent with Europe PMC requests. | none |
301
+ | `EUROPEPMC_REQUEST_DELAY_MS` | Minimum gap between Europe PMC request starts, in ms. | `200` |
302
+ | `EUROPEPMC_MAX_RETRIES` | Retry attempts for failed Europe PMC requests. | `3` |
303
+ | `EUROPEPMC_TIMEOUT_MS` | Per-request timeout for Europe PMC calls, in ms. | `20000` |
304
+ | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
305
+ | `MCP_HTTP_PORT` | HTTP server port. | `3010` |
306
+ | `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. | `stateless` |
307
+ | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
308
+ | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). | `info` |
309
+ | `LOGS_DIR` | Directory for log files (Node.js only). | `<app-root>/logs` |
310
+ | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
311
+ | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
312
+
313
+ See [`.env.example`](./.env.example) for every server setting and the common framework overrides.
339
314
 
340
315
  ## Running the server
341
316
 
@@ -369,6 +344,7 @@ Key environment variables:
369
344
  | `src/services/ncbi` | NCBI E-utilities service layer — API client, queue, parser, formatter. |
370
345
  | `src/services/europe-pmc` | Europe PMC service — search + `fullTextXML` JATS retrieval. Reuses the NCBI JATS parser. |
371
346
  | `src/services/unpaywall` | Unpaywall service — DOI → OA location resolution and content fetch (HTML/PDF). |
347
+ | `src/services/openalex` | OpenAlex service — last-resort provider for `pubmed_find_related`. |
372
348
  | `src/config` | Server-specific environment variable parsing and validation with Zod. |
373
349
  | `tests/` | Unit and integration tests, mirroring the `src/` structure. |
374
350
 
@@ -378,7 +354,8 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
378
354
 
379
355
  - Handlers throw, framework catches — no `try/catch` in tool logic
380
356
  - Use `ctx.log` for logging, `ctx.state` for storage
381
- - Register new tools and resources in the `createApp()` arrays
357
+ - Register new tools and resources in the `createApp()` arrays in `src/index.ts`
358
+ - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
382
359
 
383
360
  ## Contributing
384
361
 
@@ -4,7 +4,7 @@ breaking: false
4
4
  security: false
5
5
  ---
6
6
 
7
- # 2.10.17 — 2026-09-23
7
+ # 2.10.17 — 2026-09-22
8
8
 
9
9
  ## Fixed
10
10
 
@@ -0,0 +1,14 @@
1
+ ---
2
+ summary: "Moves Europe PMC's request queue onto the framework pacer with retries outside it, so a backoff no longer holds a concurrency slot and an upstream Retry-After is honored; also widens the unkeyed NCBI request delay to 400ms and limits openalex_unreachable to exhausted ServiceUnavailable errors."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.18 — 2026-09-22
8
+
9
+ ## Fixed
10
+
11
+ - **Europe PMC's request queue moves onto the framework pacer, with retries outside it.** A backoff no longer holds one of the four concurrency slots, and every attempt waits out the start gap from the last request any caller started. An upstream `Retry-After` now sets the retry wait; one over the 30 s backoff cap fails without another attempt, `retryAfter` included. [#163](https://github.com/cyanheads/pubmed-mcp-server/issues/163)
12
+ - **The unkeyed `NCBI_REQUEST_DELAY_MS` default rises from 334ms to 400ms.** 334ms sat exactly at NCBI's 3 req/s ceiling, so small bursts drew 429s that closed the request queue's cooldown gate. The keyed 100ms default and an explicit override still apply. [#162](https://github.com/cyanheads/pubmed-mcp-server/issues/162)
13
+ - **OpenAlex stamps `openalex_unreachable` only on an exhausted `ServiceUnavailable`.** An exhausted `Timeout` or `RateLimited` keeps its own code with no reason, and every exhausted error keeps its upstream `retryAfter`. [#160](https://github.com/cyanheads/pubmed-mcp-server/issues/160)
14
+ - **`pubmed_find_related` names a provider failure that carries no reason by its code** — `rate_limited` or `timed_out`, not `unclassified_error` — in the coverage summary and the all-providers-failed `data.attempted`. The coverage summary takes such a failure's retryability from its own `retryable` flag. [#160](https://github.com/cyanheads/pubmed-mcp-server/issues/160)
@@ -0,0 +1,42 @@
1
+ ---
2
+ summary: "pubmed_fetch_articles returns the retraction, erratum, and comment notices NCBI links to a record, and the search tools reject blank queries, filters, and cursors and impossible or reversed date ranges instead of returning unfiltered or falsely empty results."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.19 — 2026-09-27
8
+
9
+ ## Added
10
+
11
+ - **`commentsCorrections[]` on `pubmed_fetch_articles`** ([#178](https://github.com/cyanheads/pubmed-mcp-server/issues/178)) — the notices NCBI links from `CommentsCorrectionsList` (`refType`, `refSource`, optional `pmid` and `note`), `Cites` entries excluded, in NCBI's order. `content[]` lists them under `#### Comments and Corrections (N)` ahead of the abstract; the field is absent when a record links nothing else.
12
+ - **`ncbi_rate_limited`** (`RateLimited`, retryable) in the NCBI error contract, which `pubmed://database/info` now declares too — an NCBI 429 the call stops on carries it with NCBI's `retryAfter`, and its hint names `NCBI_API_KEY` as an operator setting.
13
+
14
+ ## Changed
15
+
16
+ - **`pubmed_lookup_citation` advertises `citations` as `type: "array"` (1–25) again** ([#173](https://github.com/cyanheads/pubmed-mcp-server/issues/173)) — a lone citation object, or one sent as `citation`, is wrapped into a one-element array before validation, so a field error names its element (`citations.0.journal`).
17
+ - **An NCBI JSON `error` field arrives as `data.ncbiErrors: [message]`**, as an XML `<ERROR>` does, in place of `data.ncbiError`.
18
+ - **Cancelling `pubmed_fetch_fulltext` or `pubmed_find_related` ends the call** instead of falling through to the next full-text tier or related-articles provider.
19
+ - **Error envelopes carry `data.requestId`, and a tool error's `content[]` ends with `request <id>`** (mcp-ts-core 0.13.10). Error `data` no longer carries server stack traces or request-context fields such as `operation` (mcp-ts-core 0.13.7, 0.13.8).
20
+ - **An integer sent for a string field, or a JSON-stringified object, is repaired before validation**, and a rejection names the alias rewrite it applied (`Validated limit as maxResults.`) (mcp-ts-core 0.13.9).
21
+ - **Streamable HTTP refusals** (MCP SDK 2.1.0, via mcp-ts-core 0.13.9) — a 2026-07-28 POST without `MCP-Protocol-Version` is refused `400` / `-32020`, and a JSON-RPC batch of more than 100 messages `400` / `-32600`.
22
+ - **Docker image** — production dependencies install in a `$BUILDPLATFORM` deps stage through `scripts/install-otel.ts`; the image gains a `HEALTHCHECK` on `/healthz` and an `org.opencontainers.image.version` label from `APP_VERSION`, and drops `MCP_FORCE_CONSOLE_LOGGING`.
23
+ - **Docs:** the README's tool table and capability reference are condensed to what each tool does and its limits; `.env.example` documents the Europe PMC settings and `NCBI_TOOL_IDENTIFIER`; `framework-skills/` synced to mcp-ts-core 0.13.10.
24
+
25
+ ## Fixed
26
+
27
+ - **An NCBI call that stops short of its deadline reports its last attempt** ([#174](https://github.com/cyanheads/pubmed-mcp-server/issues/174)) — `ncbi_unreachable` with NCBI's message, `ncbiErrors`, and `attempts` when the next backoff would not fit. A real expiry stays `ncbi_deadline_exceeded` and now keeps `ncbiErrors`; its hint leads with a retry and names batch size only for a large batch.
28
+ - **A one-source `pubmed_europepmc_search` filter keeps `EXT_ID` matches** ([#175](https://github.com/cyanheads/pubmed-mcp-server/issues/175)) — a single source is sent as `SRC:"X" OR SRC:"X"`, so a quoted or unquoted `EXT_ID` lookup matches.
29
+ - **An unreadable `cursorMark` is reported as bad input, not an outage** ([#180](https://github.com/cyanheads/pubmed-mcp-server/issues/180)) — a second HTTP 503 on a caller's cursor, while the first page of the same query is served, fails as non-retryable `europepmc_invalid_input` naming `cursorMark`.
30
+ - **Newly rejected inputs** — each used to reach the upstream and return unfiltered results, a false empty result, or an outage ([#176](https://github.com/cyanheads/pubmed-mcp-server/issues/176), [#177](https://github.com/cyanheads/pubmed-mcp-server/issues/177)):
31
+ - `pubmed_search_articles` `blank_filter`: an `author`, `journal`, or `language` value, or a `publicationTypes` / `meshTerms` element, of only whitespace, invisible characters, markup, `()`, `[]`, or `""`. `data.fields` names each (`meshTerms.1`); an exactly-empty string still sets no filter.
32
+ - `pubmed_search_articles` `invalid_date_range`: a `dateRange` bound that is no real calendar date (`2023/02/29`, month `13`, day `00`), or a `minDate` after `maxDate` once PubMed expands each partial date.
33
+ - `pubmed_search_articles`, `pubmed_spell_check`, `pubmed_lookup_mesh` `blank_query`: a `query` of only invisible characters, such as a zero-width space.
34
+ - `pubmed_europepmc_search` `blank_query`: a `query` with no term (`" "`, `()`, `<b></b>`, `&nbsp;`). `blank_cursor`: a `cursorMark` of only whitespace or invisible characters.
35
+ - **Minor:** an exhausted retry after one attempt reads `(failed after 1 attempt)`; `europepmc_invalid_input` errors carry `retryable: false`; the `openalex_invalid_response` contract declares `ServiceUnavailable`, the code it is thrown with.
36
+
37
+ ## Dependencies
38
+
39
+ - `@cyanheads/mcp-ts-core` ^0.13.6 → ^0.13.10
40
+ - `@socketsecurity/bun-security-scanner` ^1.1.2 → ^1.1.3 (dev)
41
+ - `ignore` ^7.0.9 → ^7.0.10 (dev)
42
+ - `packageManager` bun@1.4.0 → bun@1.4.2, with the Docker base images
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;iBA8DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CA+B9C"}
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;iBA8DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAmC9C"}
@@ -10,7 +10,7 @@ const ServerConfigSchema = z.object({
10
10
  apiKey: z.string().optional().describe('NCBI API key'),
11
11
  toolIdentifier: z.string().default('pubmed-mcp-server').describe('NCBI tool identifier'),
12
12
  adminEmail: z.email().optional().describe('Admin contact email'),
13
- requestDelayMs: z.coerce.number().min(50).max(5000).default(334).describe('Request delay in ms'),
13
+ requestDelayMs: z.coerce.number().min(50).max(5000).default(400).describe('Request delay in ms'),
14
14
  maxConcurrent: z.coerce
15
15
  .number()
16
16
  .min(1)
@@ -89,9 +89,13 @@ export function getServerConfig() {
89
89
  });
90
90
  /**
91
91
  * An API key raises NCBI's rate ceiling from ~3 req/s to ~10 req/s. If the
92
- * operator hasn't explicitly overridden the delay, tighten from the 334ms
93
- * safe default to 100ms when a key is present. A set-but-blank delay counts
92
+ * operator hasn't explicitly overridden the delay, tighten from the 400ms
93
+ * keyless default to 100ms when a key is present. A set-but-blank delay counts
94
94
  * as not overridden, matching how `parseEnvConfig` reads it.
95
+ *
96
+ * The keyless default sits above the 334ms that 3 req/s works out to, leaving
97
+ * margin for arrival jitter: spaced at exactly the ceiling, a small burst of
98
+ * concurrent calls draws 429s.
95
99
  */
96
100
  _config =
97
101
  parsed.apiKey && !process.env.NCBI_REQUEST_DELAY_MS?.trim()
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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"}
@@ -14,5 +14,47 @@ export declare const databaseInfoResource: import("@cyanheads/mcp-ts-core").Reso
14
14
  fullName: z.ZodOptional<z.ZodString>;
15
15
  description: z.ZodOptional<z.ZodString>;
16
16
  }, z.core.$strip>>>;
17
- }, z.core.$strip>, undefined>;
17
+ }, z.core.$strip>, readonly [{
18
+ readonly reason: 'queue_full';
19
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.RateLimited;
20
+ readonly when: 'The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429).';
21
+ readonly recovery: 'Wait the number of seconds in `retryAfter`, then retry; the NCBI request queue is saturated or cooling down after a rate limit.';
22
+ readonly retryable: true;
23
+ readonly thrownBy: 'service';
24
+ }, {
25
+ readonly reason: 'ncbi_unreachable';
26
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
27
+ readonly when: 'NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline.';
28
+ readonly recovery: 'Retry after a brief delay; NCBI failed on every attempt this call made.';
29
+ readonly retryable: true;
30
+ readonly thrownBy: 'service';
31
+ }, {
32
+ readonly reason: 'ncbi_rate_limited';
33
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.RateLimited;
34
+ readonly when: 'NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap.';
35
+ readonly recovery: "Wait as long as `retryAfter` says when present (a number of seconds or an HTTP date), otherwise a few seconds, then retry; NCBI is throttling this server's requests. If the server runs without NCBI_API_KEY, setting it raises the E-utilities ceiling from about 3 to about 10 requests per second and shortens the default gap between requests — an operator setting in the server's environment, not a tool input.";
36
+ readonly retryable: true;
37
+ readonly thrownBy: 'service';
38
+ }, {
39
+ readonly reason: 'ncbi_deadline_exceeded';
40
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Timeout;
41
+ readonly when: 'The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff.';
42
+ readonly recovery: 'Retry after a brief delay; NCBI may be under temporary load. For a large batch, split it into smaller calls.';
43
+ readonly retryable: true;
44
+ readonly thrownBy: 'service';
45
+ }, {
46
+ readonly reason: 'ncbi_invalid_response';
47
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.SerializationError;
48
+ readonly when: 'NCBI returned a body that could not be parsed (invalid XML/JSON).';
49
+ readonly recovery: 'Retry the request; NCBI returned a malformed response that could not be parsed.';
50
+ readonly retryable: true;
51
+ readonly thrownBy: 'service';
52
+ }, {
53
+ readonly reason: 'ncbi_resource_not_found';
54
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
55
+ readonly when: 'NCBI returned a structured "not found" error for the requested ID(s).';
56
+ readonly recovery: 'Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help.';
57
+ readonly retryable: false;
58
+ readonly thrownBy: 'service';
59
+ }]>;
18
60
  //# sourceMappingURL=database-info.resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database-info.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/database-info.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAoBrD,eAAO,MAAM,oBAAoB;;;;;;;;;;6BA+C/B,CAAC"}
1
+ {"version":3,"file":"database-info.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/database-info.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAqBrD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgD/B,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * @module src/mcp-server/resources/definitions/database-info.resource
5
5
  */
6
6
  import { resource, z } from '@cyanheads/mcp-ts-core';
7
+ import { NCBI_SERVICE_ERRORS } from '../../../services/error-contracts.js';
7
8
  import { getNcbiService } from '../../../services/ncbi/ncbi-service.js';
8
9
  import { ensureArray, getOptionalText, getText } from '../../../services/ncbi/parsing/xml-helpers.js';
9
10
  const FieldSchema = z
@@ -27,6 +28,7 @@ export const databaseInfoResource = resource('pubmed://database/info', {
27
28
  mimeType: 'application/json',
28
29
  params: z.object({}),
29
30
  output: OutputSchema,
31
+ errors: [...NCBI_SERVICE_ERRORS],
30
32
  async handler(_params, ctx) {
31
33
  ctx.log.info('Fetching PubMed database info');
32
34
  const raw = (await getNcbiService().eInfo({ db: 'pubmed' }, { signal: ctx.signal }));
@@ -1 +1 @@
1
- {"version":3,"file":"database-info.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/database-info.resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database-info.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/database-info.resource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AAE/F,MAAM,WAAW,GAAG,CAAC;KAClB,MAAM,CAAC;IACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;IAC7D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IAC7D,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;CACjE,CAAC;KACD,QAAQ,CAAC,wBAAwB,CAAC,CAAC;AAEtC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACnE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IACnE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;CACjG,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,wBAAwB,EAAE;IACrE,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE,oFAAoF;IACjG,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,CAAC,GAAG,mBAAmB,CAAU;IAEzC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG;QACxB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAE9C,MAAM,GAAG,GAAG,CAAC,MAAM,cAAc,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAGlF,CAAC;QAEF,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAA4B,CAAC;QACxE,MAAM,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM,IAAI,WAAW,CAA4B,CAAC;QAE9E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChD,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEtD,MAAM,kBAAkB,GAAG,MAAM,CAAC,SAAgD,CAAC;QACnF,IAAI,MAAiD,CAAC;QAEtD,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,WAAW,CAAC,kBAAkB,CAAC,KAAK,CAA8B,CAAC;YACrF,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC7B,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBACrB,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACrC,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC;aAC5C,CAAC,CAAC,CAAC;QACN,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,gCAAgC,EAAE;YAC7C,MAAM;YACN,UAAU,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;SAChC,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAC5D,CAAC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,wBAAwB,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;KAC7E,CAAC;CACH,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @fileoverview The one test every blank-input rejection shares: does a value
3
+ * hold any character a reader could see? `String.prototype.trim` is not enough —
4
+ * it keeps U+0085 (NEL), control characters such as U+0001 and U+007F (`\p{Cc}`),
5
+ * every format character (`\p{Cf}`: zero-width space, word joiner, soft
6
+ * hyphen, …), and the other default-ignorable code points (Hangul fillers such
7
+ * as U+3164, variation selectors, the combining grapheme joiner), so a value
8
+ * made only of those reads as nonblank and reaches the upstream as an invisible
9
+ * term. (#176)
10
+ * @module src/mcp-server/tools/definitions/_visible-text
11
+ */
12
+ /**
13
+ * True when `value` has at least one character outside `\p{White_Space}`,
14
+ * `\p{Cc}`, `\p{Cf}`, and `\p{Default_Ignorable_Code_Point}`. Pure: it strips no
15
+ * markup and decodes no entities, so a caller that sanitizes input runs this on
16
+ * the sanitized copy.
17
+ */
18
+ export declare function hasVisibleText(value: string): boolean;
19
+ //# sourceMappingURL=_visible-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_visible-text.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_visible-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD"}