@cyanheads/pubmed-mcp-server 2.10.21 → 2.10.22

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 (86) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/Dockerfile +3 -3
  4. package/README.md +8 -6
  5. package/changelog/2.10.x/2.10.22.md +44 -0
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp-server/tools/definitions/_budget.d.ts +35 -6
  9. package/dist/mcp-server/tools/definitions/_budget.d.ts.map +1 -1
  10. package/dist/mcp-server/tools/definitions/_budget.js +26 -3
  11. package/dist/mcp-server/tools/definitions/_budget.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/_notices.d.ts +33 -0
  13. package/dist/mcp-server/tools/definitions/_notices.d.ts.map +1 -0
  14. package/dist/mcp-server/tools/definitions/_notices.js +35 -0
  15. package/dist/mcp-server/tools/definitions/_notices.js.map +1 -0
  16. package/dist/mcp-server/tools/definitions/_schemas.d.ts +14 -0
  17. package/dist/mcp-server/tools/definitions/_schemas.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/_schemas.js +9 -0
  19. package/dist/mcp-server/tools/definitions/_schemas.js.map +1 -1
  20. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +3 -0
  21. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
  22. package/dist/mcp-server/tools/definitions/fetch-articles.tool.js +28 -15
  23. package/dist/mcp-server/tools/definitions/fetch-articles.tool.js.map +1 -1
  24. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +1 -0
  25. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
  26. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +107 -48
  27. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
  28. package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
  29. package/dist/mcp-server/tools/definitions/format-citations.tool.js +3 -16
  30. package/dist/mcp-server/tools/definitions/format-citations.tool.js.map +1 -1
  31. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +1 -1
  32. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
  33. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js +66 -76
  34. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js.map +1 -1
  35. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +6 -5
  36. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
  37. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js +11 -17
  38. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js.map +1 -1
  39. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +4 -4
  40. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -1
  41. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +9 -8
  42. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -1
  43. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +4 -4
  44. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
  45. package/dist/mcp-server/tools/definitions/spell-check.tool.js +12 -8
  46. package/dist/mcp-server/tools/definitions/spell-check.tool.js.map +1 -1
  47. package/dist/services/error-contracts.d.ts +1 -1
  48. package/dist/services/error-contracts.d.ts.map +1 -1
  49. package/dist/services/error-contracts.js +1 -1
  50. package/dist/services/error-contracts.js.map +1 -1
  51. package/dist/services/europe-pmc/europe-pmc-service.d.ts +31 -11
  52. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
  53. package/dist/services/europe-pmc/europe-pmc-service.js +122 -47
  54. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
  55. package/dist/services/europe-pmc/types.d.ts +5 -1
  56. package/dist/services/europe-pmc/types.d.ts.map +1 -1
  57. package/dist/services/ncbi/formatting/citation-formatter.d.ts.map +1 -1
  58. package/dist/services/ncbi/formatting/citation-formatter.js +204 -110
  59. package/dist/services/ncbi/formatting/citation-formatter.js.map +1 -1
  60. package/dist/services/ncbi/ncbi-service.d.ts +22 -0
  61. package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
  62. package/dist/services/ncbi/ncbi-service.js +113 -17
  63. package/dist/services/ncbi/ncbi-service.js.map +1 -1
  64. package/dist/services/ncbi/parsing/article-parser.d.ts.map +1 -1
  65. package/dist/services/ncbi/parsing/article-parser.js +11 -4
  66. package/dist/services/ncbi/parsing/article-parser.js.map +1 -1
  67. package/dist/services/ncbi/parsing/pmc-article-parser.d.ts +5 -1
  68. package/dist/services/ncbi/parsing/pmc-article-parser.d.ts.map +1 -1
  69. package/dist/services/ncbi/parsing/pmc-article-parser.js +85 -31
  70. package/dist/services/ncbi/parsing/pmc-article-parser.js.map +1 -1
  71. package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts +13 -0
  72. package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts.map +1 -1
  73. package/dist/services/ncbi/parsing/pmc-xml-helpers.js +108 -10
  74. package/dist/services/ncbi/parsing/pmc-xml-helpers.js.map +1 -1
  75. package/dist/services/ncbi/parsing/text-helpers.d.ts +10 -0
  76. package/dist/services/ncbi/parsing/text-helpers.d.ts.map +1 -1
  77. package/dist/services/ncbi/parsing/text-helpers.js +19 -0
  78. package/dist/services/ncbi/parsing/text-helpers.js.map +1 -1
  79. package/dist/services/ncbi/response-handler.d.ts +0 -11
  80. package/dist/services/ncbi/response-handler.d.ts.map +1 -1
  81. package/dist/services/ncbi/response-handler.js +21 -33
  82. package/dist/services/ncbi/response-handler.js.map +1 -1
  83. package/dist/services/ncbi/types.d.ts +30 -30
  84. package/dist/services/ncbi/types.d.ts.map +1 -1
  85. package/package.json +5 -5
  86. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** @cyanheads/pubmed-mcp-server
4
- **Version:** 2.10.21
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
4
+ **Version:** 2.10.22
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
 
8
8
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/CLAUDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** @cyanheads/pubmed-mcp-server
4
- **Version:** 2.10.21
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
4
+ **Version:** 2.10.22
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
 
8
8
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/Dockerfile CHANGED
@@ -10,7 +10,7 @@
10
10
  # the deps stage cross-installs the runtime dependencies per target. Emulating
11
11
  # this stage aborts `tsc` on a source tree this size.
12
12
  # ==============================================================================
13
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS build
13
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS build
14
14
 
15
15
  WORKDIR /usr/src/app
16
16
 
@@ -44,7 +44,7 @@ RUN bun run build
44
44
  # A clean image rather than `FROM build`: the build stage's node_modules holds
45
45
  # devDependencies.
46
46
  # ==============================================================================
47
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.2 AS deps
47
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS deps
48
48
 
49
49
  WORKDIR /usr/src/app
50
50
 
@@ -111,7 +111,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
111
111
  # dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
112
112
  # and CMD, which run on the real target at container start.
113
113
  # ==============================================================================
114
- FROM oven/bun:1.4.2-slim AS production
114
+ FROM oven/bun:1.4.3-slim AS production
115
115
 
116
116
  WORKDIR /usr/src/app
117
117
 
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-2.10.21-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.2.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/)
10
+ [![Version](https://img.shields.io/badge/Version-2.10.22-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.3.1-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.3-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -39,7 +39,7 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
39
39
  | `pubmed_fetch_articles` | Fetch article metadata by PMID: abstract, authors, journal, MeSH terms, grants, a collective author's investigators on request, linked retraction and correction notices |
40
40
  | `pubmed_fetch_fulltext` | Fetch full text by PMID, PMCID, or DOI, falling back from PMC to Europe PMC to Unpaywall |
41
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` |
42
+ | `pubmed_europepmc_fetch` | Fetch complete Europe PMC records, including the untruncated abstract, as `records: [{ source, epmcId }]` |
43
43
  | `pubmed_format_citations` | Format citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM), with linked retraction and correction notices |
44
44
  | `pubmed_find_related` | Find similar articles, citing articles, or references for a PMID |
45
45
  | `pubmed_spell_check` | Correct a misspelled PubMed query via NCBI ESpell |
@@ -74,6 +74,7 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
74
74
 
75
75
  - Up to 200 PMIDs per call; misses are listed in `unavailablePmids`
76
76
  - Abstract, authors, journal, DOI, publication types, linked retraction/erratum/comment notices, and links; MeSH terms on by default, grants via `includeGrants`, and the members of a collective author (its `InvestigatorList`, thousands on some consortium trials) via `includeInvestigators`
77
+ - A retracted, corrected, or questioned article opens with the same notice block as `pubmed_format_citations`, directly under its title
77
78
  - `recordType` separates journal articles from Bookshelf chapters and books; opt-in `maxResponseCharacters` defers overflow to `deferred.ids`
78
79
 
79
80
  ---
@@ -90,13 +91,14 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
90
91
 
91
92
  - Adds preprints (`PPR`), patents (`PAT`), and Agricola (`AGR`) to `MED` and `PMC`; defaults to `MED`, `PMC`, `PPR`
92
93
  - Cursor pagination via `cursorMark` / `nextCursorMark`, up to 100 per page; abstracts arrive as 400-character snippets
94
+ - Optional `sort` of comma-separated `<field> asc|desc` keys; `relevance` is the default order, and a sort Europe PMC cannot use fails as `europepmc_invalid_input`
93
95
  - Not registered when `EUROPEPMC_ENABLED=false`
94
96
 
95
97
  ---
96
98
 
97
99
  ### `pubmed_europepmc_fetch` <sub>tool</sub>
98
100
 
99
- - Up to 25 records per call, addressed by a search hit's `source` + `epmcId`, with the full abstract; unresolved ids land in `notFound`; opt-in `maxResponseCharacters` defers overflow to `deferred.records`
101
+ - Up to 25 records per call as `records: [{ source, epmcId }]`, each pair copied from a search hit, with the full abstract; unresolved ids land in `notFound`; opt-in `maxResponseCharacters` defers overflow to `deferred.records`
100
102
  - Not registered when `EUROPEPMC_ENABLED=false`
101
103
 
102
104
  ---
@@ -118,15 +120,15 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
118
120
 
119
121
  ### `pubmed_spell_check` <sub>tool</sub>
120
122
 
121
- - Returns `original`, `corrected`, and `hasSuggestion`, fixing every misspelled token in one call
123
+ - Returns `original`, `corrected`, and `hasSuggestion`; corrections land in place, so field tags, operators, spacing, and gene symbols stay as written
122
124
  - Run it after a zero-hit or thin search, then retry `pubmed_search_articles` with `corrected`
123
125
 
124
126
  ---
125
127
 
126
128
  ### `pubmed_lookup_mesh` <sub>tool</sub>
127
129
 
128
- - Descriptors by name or free-text term, with an exact heading match pinned first; up to 50 per page, continued via `nextOffset`
129
- - Records carry `meshId`; `includeDetails` (default on) adds tree numbers, scope notes, and entry terms
130
+ - Descriptors by name or free-text term, with an exact heading match pinned first (the descriptor, for a drug-class heading); up to 50 per page, continued via `nextOffset`
131
+ - Records carry their MeSH unique ID (`meshId`, e.g. `D000072657`) beside the Entrez UID; `includeDetails` (default on) adds tree numbers, scope notes, and entry terms
130
132
 
131
133
  ---
132
134
 
@@ -0,0 +1,44 @@
1
+ ---
2
+ summary: "Author name suffixes carry through records, citations, and full text; citation formatting, full-text cleanup, in-place spell-check corrections, and Europe PMC sort errors are fixed."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.22 — 2026-10-12
8
+
9
+ ## Added
10
+
11
+ - **Author `suffix`** ([#226](https://github.com/cyanheads/pubmed-mcp-server/issues/226)) — `pubmed_fetch_articles` authors, investigators, and book editors, and `pubmed_fetch_fulltext` authors carry the generational suffix verbatim (`Jr`, `2nd`, `IV`). Every citation style prints it, and full-text references keep it.
12
+ - **`pubmed_fetch_articles` notice block** ([#237](https://github.com/cyanheads/pubmed-mcp-server/issues/237)) — a retracted, corrected, or questioned article opens with the same notice lines as `pubmed_format_citations`, directly under its title.
13
+ - **Europe PMC lower-case source codes** ([#244](https://github.com/cyanheads/pubmed-mcp-server/issues/244)) — `pubmed_europepmc_fetch` `records[].source` and `pubmed_europepmc_search` `sources` accept lower-case codes (`med`, `pmc`). Search hint text, tool descriptions, and server instructions name the `records: [{ source, epmcId }]` input shape.
14
+
15
+ ## Changed
16
+
17
+ - **`pubmed_spell_check` corrects in place** ([#241](https://github.com/cyanheads/pubmed-mcp-server/issues/241)) — `corrected` applies ESpell's replacements to the caller's query, keeping field tags, operators, spacing, and words in capitals or with digits (gene symbols) as written. `hasSuggestion` is true only when one was applied; otherwise the text reads `No correction applied — query "…" is returned as written.`
18
+ - **`pubmed_lookup_mesh` IDs** ([#238](https://github.com/cyanheads/pubmed-mcp-server/issues/238)) — `meshId` is the MeSH unique ID from eSummary 2.0 (`D…`, `C…`, `Q…`), including records the Entrez UID does not encode. It is omitted when eSummary returns no record for the UID.
19
+ - **`pubmed_lookup_mesh` pinning** ([#247](https://github.com/cyanheads/pubmed-mcp-server/issues/247)) — an exact heading that matches a pharmacological-action record pins its descriptor instead; `treeNumbers` leaves out the record's own UI.
20
+ - **Deferral notices** ([#236](https://github.com/cyanheads/pubmed-mcp-server/issues/236)) — `pubmed_fetch_articles`, `pubmed_fetch_fulltext`, and `pubmed_europepmc_fetch` share one notice naming the ceiling for re-calling the deferred items and the one for repeating the call. `nextDeferredCharacters` is described as the continuation ceiling.
21
+ - **`pubmed_fetch_fulltext` ordering** ([#235](https://github.com/cyanheads/pubmed-mcp-server/issues/235)) — `articles`, `truncation.articles`, and the section-filter notice follow routing order (`pmcids`, then `pmids`, then `dois`, request order within each), so a `maxResponseCharacters` cut is the same on every call.
22
+ - **Europe PMC `sort`** ([#239](https://github.com/cyanheads/pubmed-mcp-server/issues/239)) — `relevance` and a whitespace-only sort are sent as no sort, `FIRST_IDATE_D` joins the documented fields, and a sort without a field plus `asc`/`desc` per key fails as `europepmc_invalid_input` before any request. A second HTTP 503 to a sorted search that the unsorted query survives names the sort.
23
+ - **Framework 0.13.15 adoption** — `framework-skills/` sync.
24
+
25
+ ## Fixed
26
+
27
+ - **Citation journal names** ([#227](https://github.com/cyanheads/pubmed-mcp-server/issues/227)) — APA and MLA drop NLM's place, year, and edition qualifier (`Lancet (London, England)` → `Lancet`) and keep an edition the ISO abbreviation also names.
28
+ - **Citation page ranges** ([#228](https://github.com/cyanheads/pubmed-mcp-server/issues/228)) — BibTeX `pages` writes each range in full, and RIS `SP`/`EP` take the first and last page of a list (`1308, 1310-4` → `1308`/`1314`).
29
+ - **Article numbers** ([#231](https://github.com/cyanheads/pubmed-mcp-server/issues/231)) — a dashless page that equals the locator with its hyphens removed (`e0364621` beside `e03646-21`) cites as the locator.
30
+ - **Citation whitespace** ([#233](https://github.com/cyanheads/pubmed-mcp-server/issues/233)) — line breaks inside titles, names, keywords, and abstracts collapse to one space in every style, so RIS stays one tagged line per value. `pubmed_format_citations` escapes Markdown in the title line.
31
+ - **Literal `None` page** ([#248](https://github.com/cyanheads/pubmed-mcp-server/issues/248)) — NCBI's `None` placeholder is no longer read as a page.
32
+ - **Invisible characters** ([#224](https://github.com/cyanheads/pubmed-mcp-server/issues/224)) — U+FEFF, U+00AD, and U+200B are dropped from NCBI XML responses and Europe PMC full-text XML before parsing, ending split words in full text (`hepatopulmonary s yndrome`).
33
+ - **Space before punctuation** ([#225](https://github.com/cyanheads/pubmed-mcp-server/issues/225)) — a line break between a formula or other element and the closing punctuation after it no longer leaves a space (`L<0 .` → `L<0.`).
34
+ - **MathML `<semantics>`** ([#230](https://github.com/cyanheads/pubmed-mcp-server/issues/230)) — reads one rendering of the expression, never the TeX, MathType, or Content annotations beside it.
35
+ - **Body footnotes** ([#229](https://github.com/cyanheads/pubmed-mcp-server/issues/229)) — a `<fn>` or `<fn-group>` in a body section reads one paragraph per footnote, as it does in back matter.
36
+ - **Table and asset labels** ([#240](https://github.com/cyanheads/pubmed-mcp-server/issues/240)) — one trailing `.`, `|`, or `:` is dropped (`Table 1.` → `Table 1`), and a bare-number label is named with its type in `omittedAssetNames` and asset headings (`Supplementary material 1`).
37
+ - **Rendered dates** ([#242](https://github.com/cyanheads/pubmed-mcp-server/issues/242)) — a one-digit numeric month or day is zero-padded in the rendered date; the structured parts stay as deposited.
38
+
39
+ ## Dependencies
40
+
41
+ - `@cyanheads/mcp-ts-core` ^0.13.14 → ^0.13.15
42
+ - `@types/node` ^26.6.4 → ^26.6.5
43
+ - `@hono/node-server` override ^2.0.11 → ^2.1.4
44
+ - Bun 1.4.2 → 1.4.3 (`Dockerfile`, `packageManager`)
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ await createApp({
42
42
  tools,
43
43
  resources: [databaseInfoResource],
44
44
  prompts: [researchPlanPrompt],
45
- instructions: "Use the pubmed_* tools to search PubMed and PubMed Central, fetch article metadata and full text, format citations, and find related articles via NCBI's E-utilities. Articles are keyed by PMID (integer); PMC full text by PMCID (`PMC` prefix); most also carry a DOI. Typical flow: `pubmed_search_articles` → `pubmed_fetch_articles` → `pubmed_fetch_fulltext`. When PubMed itself comes up empty (preprints, EPMC-only OA), broaden via `pubmed_europepmc_search`, then `pubmed_europepmc_fetch` with a hit's `source` + `epmcId` for its complete abstract. Prefer deterministic resolvers when inputs are structured: `pubmed_lookup_citation` for partial references, `pubmed_convert_ids` to crosswalk IDs. Refine queries with `pubmed_lookup_mesh` and `pubmed_spell_check`.",
45
+ instructions: "Use the pubmed_* tools to search PubMed and PubMed Central, fetch article metadata and full text, format citations, and find related articles via NCBI's E-utilities. Articles are keyed by PMID (integer); PMC full text by PMCID (`PMC` prefix); most also carry a DOI. Typical flow: `pubmed_search_articles` → `pubmed_fetch_articles` → `pubmed_fetch_fulltext`. When PubMed itself comes up empty (preprints, EPMC-only OA), broaden via `pubmed_europepmc_search`, then `pubmed_europepmc_fetch` with `records: [{ source, epmcId }]` copied from a hit for its complete abstract. Prefer deterministic resolvers when inputs are structured: `pubmed_lookup_citation` for partial references, `pubmed_convert_ids` to crosswalk IDs. Refine queries with `pubmed_lookup_mesh` and `pubmed_spell_check`.",
46
46
  landing: {
47
47
  requireAuth: false,
48
48
  tagline: 'Search PubMed, fetch articles, generate citations, explore MeSH terms, and discover related research.',
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+DAA+D,CAAC;AACzG,OAAO,EAAE,yBAAyB,EAAE,MAAM,gEAAgE,CAAC;AAC3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;AACjC,MAAM,KAAK,GAAG;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,mBAAmB;IACnB,eAAe;IACf,cAAc;IACd,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,yBAAyB,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;CAC1F,CAAC;AAEF,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,WAAW;IACxB,KAAK;IACL,SAAS,EAAE,CAAC,oBAAoB,CAAC;IACjC,OAAO,EAAE,CAAC,kBAAkB,CAAC;IAC7B,YAAY,EACV,2vBAA2vB;IAC7vB,OAAO,EAAE;QACP,WAAW,EAAE,KAAK;QAClB,OAAO,EACL,uGAAuG;QACzG,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE;YACL,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC7E;gBACE,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,8CAA8C;gBACpD,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,gDAAgD;gBACtD,QAAQ,EAAE,IAAI;aACf;YACD,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC9E;QACD,UAAU,EAAE;YACV,YAAY,EAAE,mBAAmB;YACjC,gBAAgB,EAAE,iBAAiB;YACnC,eAAe,EAAE,iBAAiB;SACnC;KACF;IACD,KAAK;QACH,eAAe,EAAE,CAAC;QAClB,oBAAoB,EAAE,CAAC;QACvB,oBAAoB,EAAE,CAAC;QACvB,mBAAmB,EAAE,CAAC;IACxB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+DAA+D,CAAC;AACzG,OAAO,EAAE,yBAAyB,EAAE,MAAM,gEAAgE,CAAC;AAC3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;AACjC,MAAM,KAAK,GAAG;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,mBAAmB;IACnB,eAAe;IACf,cAAc;IACd,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,yBAAyB,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;CAC1F,CAAC;AAEF,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EAAE,WAAW;IACxB,KAAK;IACL,SAAS,EAAE,CAAC,oBAAoB,CAAC;IACjC,OAAO,EAAE,CAAC,kBAAkB,CAAC;IAC7B,YAAY,EACV,ixBAAixB;IACnxB,OAAO,EAAE;QACP,WAAW,EAAE,KAAK;QAClB,OAAO,EACL,uGAAuG;QACzG,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE;YACL,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC7E;gBACE,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,8CAA8C;gBACpD,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,gDAAgD;gBACtD,QAAQ,EAAE,IAAI;aACf;YACD,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC9E;QACD,UAAU,EAAE;YACV,YAAY,EAAE,mBAAmB;YACjC,gBAAgB,EAAE,iBAAiB;YACnC,eAAe,EAAE,iBAAiB;SACnC;KACF;IACD,KAAK;QACH,eAAe,EAAE,CAAC;QAClB,oBAAoB,EAAE,CAAC;QACvB,oBAAoB,EAAE,CAAC;QACvB,mBAAmB,EAAE,CAAC;IACxB,CAAC;CACF,CAAC,CAAC"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Whole-response character budgeting shared by the retrieval
3
- * tools: how many whole items fit under a character ceiling, in order. The
4
- * sibling of `_text.ts`, which shortens the *inside* of one item's text —
5
- * nothing here ever cuts into an item. (#99, #100)
3
+ * tools: how many whole items fit under a character ceiling, in order, and the
4
+ * notice that tells a caller how to retrieve the rest. The sibling of
5
+ * `_text.ts`, which shortens the *inside* of one item's text — nothing here
6
+ * ever cuts into an item. (#99, #100, #236)
6
7
  * @module src/mcp-server/tools/definitions/_budget
7
8
  */
8
9
  /**
@@ -22,12 +23,40 @@ export interface WholeItemFit<T> {
22
23
  keptCharacters: number;
23
24
  /**
24
25
  * Size of the next deferred item — `deferred[0]`, the one the cut stopped
25
- * at. Because the cut is a prefix cut, that item is the ceiling a caller must
26
- * clear to make progress: a smaller item further down the tail is unreachable
27
- * until the one in front of it fits. Absent when nothing was deferred.
26
+ * at. Because the cut is a prefix cut, it is the continuation ceiling: a call
27
+ * for `deferred` alone returns nothing under it, and a smaller item further
28
+ * down the tail is unreachable until the one in front of it fits. Repeating
29
+ * the same call needs `keptCharacters` + this to return that item as well.
30
+ * Absent when nothing was deferred.
28
31
  */
29
32
  nextDeferredCharacters?: number;
30
33
  }
34
+ /** The budget state a deferral notice reports — the fields every tool's `deferred` carries. */
35
+ export interface DeferralState {
36
+ deferredCount: number;
37
+ maxResponseCharacters: number;
38
+ nextDeferredCharacters: number;
39
+ returnedCharacters: number;
40
+ }
41
+ /** How one tool names what it deferred and how to ask for it again. */
42
+ export interface DeferralWording {
43
+ /** The deferred items as the caller re-sends them, already joined. */
44
+ ids: string;
45
+ /** One deferred item: `article`, `record`. */
46
+ noun: string;
47
+ /** The re-call after its verb — the tool and how the ids go back in. */
48
+ recall: string;
49
+ }
50
+ /**
51
+ * Compose the recovery notice for items the whole-response budget withheld, so
52
+ * a caller reading only `content[]` can resume without inspecting `deferred`.
53
+ * Each ceiling is named with the call it applies to: re-calling for the
54
+ * deferred items needs `nextDeferredCharacters`; repeating this call needs
55
+ * `returnedCharacters + nextDeferredCharacters`. Both exceed the current
56
+ * ceiling whenever they are named, so the notice never asks for a raise to a
57
+ * value at or below it. (#100, #236)
58
+ */
59
+ export declare function buildDeferralNotice(state: DeferralState, wording: DeferralWording): string;
31
60
  /**
32
61
  * Split an ordered list at the first item that would carry the running
33
62
  * character total past `maxCharacters`. That item and everything after it is
@@ -1 +1 @@
1
- {"version":3,"file":"_budget.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_budget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3D;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAA6B,GAClD,YAAY,CAAC,CAAC,CAAC,CAuBjB"}
1
+ {"version":3,"file":"_budget.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_budget.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3D;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,+CAA+C;IAC/C,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAa1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAA6B,GAClD,YAAY,CAAC,CAAC,CAAC,CAuBjB"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Whole-response character budgeting shared by the retrieval
3
- * tools: how many whole items fit under a character ceiling, in order. The
4
- * sibling of `_text.ts`, which shortens the *inside* of one item's text —
5
- * nothing here ever cuts into an item. (#99, #100)
3
+ * tools: how many whole items fit under a character ceiling, in order, and the
4
+ * notice that tells a caller how to retrieve the rest. The sibling of
5
+ * `_text.ts`, which shortens the *inside* of one item's text — nothing here
6
+ * ever cuts into an item. (#99, #100, #236)
6
7
  * @module src/mcp-server/tools/definitions/_budget
7
8
  */
8
9
  /**
@@ -14,6 +15,28 @@
14
15
  export function serializedCharacters(value) {
15
16
  return JSON.stringify(value)?.length ?? 0;
16
17
  }
18
+ /**
19
+ * Compose the recovery notice for items the whole-response budget withheld, so
20
+ * a caller reading only `content[]` can resume without inspecting `deferred`.
21
+ * Each ceiling is named with the call it applies to: re-calling for the
22
+ * deferred items needs `nextDeferredCharacters`; repeating this call needs
23
+ * `returnedCharacters + nextDeferredCharacters`. Both exceed the current
24
+ * ceiling whenever they are named, so the notice never asks for a raise to a
25
+ * value at or below it. (#100, #236)
26
+ */
27
+ export function buildDeferralNotice(state, wording) {
28
+ const { maxResponseCharacters: max, nextDeferredCharacters: next } = state;
29
+ const { ids, noun, recall } = wording;
30
+ const listed = `${state.deferredCount} resolved ${noun}(s) were deferred whole: ${ids}.`;
31
+ if (state.returnedCharacters === 0) {
32
+ return `The first ${noun} alone exceeds the requested maxResponseCharacters of ${max}, so none were returned. ${listed} Re-call ${recall} and maxResponseCharacters ≥ ${next}, the size of that first ${noun}.`;
33
+ }
34
+ const continuation = next <= max
35
+ ? `To continue, re-call ${recall} at the current maxResponseCharacters of ${max}, which already fits the next deferred ${noun} (${next} characters).`
36
+ : `To continue, re-call ${recall} and maxResponseCharacters ≥ ${next}, the size of the next deferred ${noun}.`;
37
+ const repeat = `To return the next deferred ${noun} in this response instead, repeat this call with maxResponseCharacters ≥ ${state.returnedCharacters + next}.`;
38
+ return `Response character budget reached: ${state.returnedCharacters} of ${max} characters returned. ${listed} ${continuation} ${repeat}`;
39
+ }
17
40
  /**
18
41
  * Split an ordered list at the first item that would carry the running
19
42
  * character total past `maxCharacters`. That item and everything after it is
@@ -1 +1 @@
1
- {"version":3,"file":"_budget.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_budget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;AAC5C,CAAC;AAmBD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAmB,EACnB,aAAqB,EACrB,OAAO,GAAwB,oBAAoB;IAEnD,MAAM,IAAI,GAAQ,EAAE,CAAC;IACrB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAEvB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,cAAc,GAAG,IAAI,GAAG,aAAa,EAAE,CAAC;YAC1C,GAAG,GAAG,KAAK,CAAC;YACZ,MAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,cAAc,IAAI,IAAI,CAAC;IACzB,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IACxB,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,cAAc;QACd,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,sBAAsB,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"_budget.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_budget.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;AAC5C,CAAC;AAuCD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAoB,EAAE,OAAwB;IAChF,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,sBAAsB,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC3E,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,aAAa,IAAI,4BAA4B,GAAG,GAAG,CAAC;IACzF,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,aAAa,IAAI,yDAAyD,GAAG,4BAA4B,MAAM,YAAY,MAAM,gCAAgC,IAAI,4BAA4B,IAAI,GAAG,CAAC;IAClN,CAAC;IACD,MAAM,YAAY,GAChB,IAAI,IAAI,GAAG;QACT,CAAC,CAAC,wBAAwB,MAAM,4CAA4C,GAAG,0CAA0C,IAAI,KAAK,IAAI,eAAe;QACrJ,CAAC,CAAC,wBAAwB,MAAM,gCAAgC,IAAI,mCAAmC,IAAI,GAAG,CAAC;IACnH,MAAM,MAAM,GAAG,+BAA+B,IAAI,4EAA4E,KAAK,CAAC,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjK,OAAO,sCAAsC,KAAK,CAAC,kBAAkB,OAAO,GAAG,yBAAyB,MAAM,IAAI,YAAY,IAAI,MAAM,EAAE,CAAC;AAC7I,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAmB,EACnB,aAAqB,EACrB,OAAO,GAAwB,oBAAoB;IAEnD,MAAM,IAAI,GAAQ,EAAE,CAAC;IACrB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IAEvB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,cAAc,GAAG,IAAI,GAAG,aAAa,EAAE,CAAC;YAC1C,GAAG,GAAG,KAAK,CAAC;YACZ,MAAM;QACR,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,cAAc,IAAI,IAAI,CAAC;IACzB,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IACxB,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,cAAc;QACd,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,sBAAsB,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview The linked-notice block `pubmed_format_citations` and
3
+ * `pubmed_fetch_articles` both open a record with: the retraction, erratum, and
4
+ * expression-of-concern links NCBI attaches to it, under a warning when one is a
5
+ * retraction. (#215, #237)
6
+ * @module src/mcp-server/tools/definitions/_notices
7
+ */
8
+ import { type CitationNoticeType } from '../../../services/ncbi/formatting/citation-formatter.js';
9
+ /** One NCBI comments-and-corrections link as a tool's output carries it. */
10
+ interface NoticeLink {
11
+ note?: string | undefined;
12
+ pmid?: string | undefined;
13
+ refSource: string;
14
+ refType: string;
15
+ }
16
+ /**
17
+ * The links that qualify the record itself — it was retracted, corrected, or
18
+ * questioned — in NCBI's order. Comment, update, and `…Of`/`…For` links, which
19
+ * point from a notice back to what it concerns, are not among them.
20
+ */
21
+ export declare function noticeLinks<T extends NoticeLink>(links: readonly T[] | undefined): (T & {
22
+ refType: CitationNoticeType;
23
+ })[];
24
+ /**
25
+ * The `content[]` lines for one record's notices, opening on a warning when it
26
+ * was retracted. Each entry takes the form the record's full "Comments and
27
+ * Corrections" list gives the same link.
28
+ */
29
+ export declare function noticeLines(notices: readonly (NoticeLink & {
30
+ refType: CitationNoticeType;
31
+ })[]): string[];
32
+ export {};
33
+ //# sourceMappingURL=_notices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_notices.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_notices.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,kDAAkD,CAAC;AAG1D,4EAA4E;AAC5E,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,UAAU,EAC9C,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,GAC9B,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAC,EAAE,CAIzC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,CAAC,UAAU,GAAG;IAAE,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAC,EAAE,GACjE,MAAM,EAAE,CAYV"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @fileoverview The linked-notice block `pubmed_format_citations` and
3
+ * `pubmed_fetch_articles` both open a record with: the retraction, erratum, and
4
+ * expression-of-concern links NCBI attaches to it, under a warning when one is a
5
+ * retraction. (#215, #237)
6
+ * @module src/mcp-server/tools/definitions/_notices
7
+ */
8
+ import { CITATION_NOTICE_TYPES, } from '../../../services/ncbi/formatting/citation-formatter.js';
9
+ import { escapeMarkdownInline } from './_text.js';
10
+ const NOTICE_TYPES = new Set(CITATION_NOTICE_TYPES);
11
+ /**
12
+ * The links that qualify the record itself — it was retracted, corrected, or
13
+ * questioned — in NCBI's order. Comment, update, and `…Of`/`…For` links, which
14
+ * point from a notice back to what it concerns, are not among them.
15
+ */
16
+ export function noticeLinks(links) {
17
+ return (links ?? []).filter((link) => NOTICE_TYPES.has(link.refType));
18
+ }
19
+ /**
20
+ * The `content[]` lines for one record's notices, opening on a warning when it
21
+ * was retracted. Each entry takes the form the record's full "Comments and
22
+ * Corrections" list gives the same link.
23
+ */
24
+ export function noticeLines(notices) {
25
+ const retracted = notices.some((notice) => notice.refType === 'RetractionIn' || notice.refType === 'RetractedandRepublishedIn');
26
+ return [
27
+ retracted ? '**Retracted:** NCBI links a retraction notice to this article.' : '**Notices:**',
28
+ ...notices.map((notice) => {
29
+ const pmid = notice.pmid ? ` — PMID ${notice.pmid}` : '';
30
+ const note = notice.note ? ` — Note: ${escapeMarkdownInline(notice.note)}` : '';
31
+ return `- **${notice.refType}:** ${escapeMarkdownInline(notice.refSource)}${pmid}${note}`;
32
+ }),
33
+ ];
34
+ }
35
+ //# sourceMappingURL=_notices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_notices.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_notices.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,qBAAqB,GAEtB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAUlD,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC,qBAAqB,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,KAA+B;IAE/B,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAA+C,EAAE,CAChF,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAC/B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,OAAkE;IAElE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC5B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,KAAK,cAAc,IAAI,MAAM,CAAC,OAAO,KAAK,2BAA2B,CAChG,CAAC;IACF,OAAO;QACL,SAAS,CAAC,CAAC,CAAC,gEAAgE,CAAC,CAAC,CAAC,cAAc;QAC7F,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACxB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,OAAO,OAAO,MAAM,CAAC,OAAO,OAAO,oBAAoB,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC5F,CAAC,CAAC;KACH,CAAC;AACJ,CAAC"}
@@ -37,4 +37,18 @@ export declare const pmcidStringSchema: z.ZodString;
37
37
  * carrying one is read upstream as two identifiers rather than one DOI.
38
38
  */
39
39
  export declare const doiStringSchema: z.ZodString;
40
+ /**
41
+ * A Europe PMC source code as a tool accepts it: a lower-case code (`med`,
42
+ * `pmc`) is folded to the code itself before the enum check, since Europe PMC
43
+ * matches `SRC:` case-insensitively and search hits always carry the upper-case
44
+ * form. A value that is no code (`pubmed`) is still rejected. The fold stays off
45
+ * the advertised schema, which remains the plain enum. (#244)
46
+ */
47
+ export declare const europePmcSourceInputSchema: z.ZodPreprocess<z.ZodEnum<{
48
+ AGR: "AGR";
49
+ MED: "MED";
50
+ PAT: "PAT";
51
+ PMC: "PMC";
52
+ PPR: "PPR";
53
+ }>, unknown>;
40
54
  //# sourceMappingURL=_schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_schemas.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,aAK1B,CAAC;AAEJ;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAK3B,CAAC;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,aAKzB,CAAC"}
1
+ {"version":3,"file":"_schemas.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,aAK1B,CAAC;AAEJ;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAK3B,CAAC;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,aAKzB,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B;;;;;;YAGtC,CAAC"}
@@ -4,6 +4,7 @@
4
4
  * @module src/mcp-server/tools/definitions/_schemas
5
5
  */
6
6
  import { z } from '@cyanheads/mcp-ts-core';
7
+ import { EUROPEPMC_ALL_SOURCES } from '../../../services/europe-pmc/types.js';
7
8
  /**
8
9
  * Zod string schema for a single PubMed ID. Accepts only digit characters.
9
10
  * The message is intentionally actionable so callers can self-correct without
@@ -45,4 +46,12 @@ export const pmcidStringSchema = z
45
46
  export const doiStringSchema = z
46
47
  .string()
47
48
  .regex(/^10\.[^\s,]+\/[^\s,]+$/, 'DOI must start with "10." and contain a "/" (e.g. "10.1093/nar/gks1195"). Remove any whitespace or commas — provide each DOI separately.');
49
+ /**
50
+ * A Europe PMC source code as a tool accepts it: a lower-case code (`med`,
51
+ * `pmc`) is folded to the code itself before the enum check, since Europe PMC
52
+ * matches `SRC:` case-insensitively and search hits always carry the upper-case
53
+ * form. A value that is no code (`pubmed`) is still rejected. The fold stays off
54
+ * the advertised schema, which remains the plain enum. (#244)
55
+ */
56
+ export const europePmcSourceInputSchema = z.preprocess((value) => (typeof value === 'string' ? value.toUpperCase() : value), z.enum(EUROPEPMC_ALL_SOURCES));
48
57
  //# sourceMappingURL=_schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_schemas.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,EAAE;KACR,KAAK,CACJ,OAAO,EACP,6IAA6I,CAC9I,CAAC;AAEJ;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,KAAK,CACJ,gBAAgB,EAChB,2JAA2J,CAC5J,CAAC;AAEJ;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,EAAE;KACR,KAAK,CACJ,wBAAwB,EACxB,0IAA0I,CAC3I,CAAC"}
1
+ {"version":3,"file":"_schemas.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,EAAE;KACR,KAAK,CACJ,OAAO,EACP,6IAA6I,CAC9I,CAAC;AAEJ;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,KAAK,CACJ,gBAAgB,EAChB,2JAA2J,CAC5J,CAAC;AAEJ;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,EAAE;KACR,KAAK,CACJ,wBAAwB,EACxB,0IAA0I,CAC3I,CAAC;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,UAAU,CACpD,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EACpE,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAC9B,CAAC"}
@@ -28,6 +28,7 @@ export declare const fetchArticlesTool: import("@cyanheads/mcp-ts-core").ToolDef
28
28
  lastName: z.ZodOptional<z.ZodString>;
29
29
  firstName: z.ZodOptional<z.ZodString>;
30
30
  initials: z.ZodOptional<z.ZodString>;
31
+ suffix: z.ZodOptional<z.ZodString>;
31
32
  collectiveName: z.ZodOptional<z.ZodString>;
32
33
  affiliationIndices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
33
34
  orcid: z.ZodOptional<z.ZodString>;
@@ -36,6 +37,7 @@ export declare const fetchArticlesTool: import("@cyanheads/mcp-ts-core").ToolDef
36
37
  lastName: z.ZodOptional<z.ZodString>;
37
38
  firstName: z.ZodOptional<z.ZodString>;
38
39
  initials: z.ZodOptional<z.ZodString>;
40
+ suffix: z.ZodOptional<z.ZodString>;
39
41
  affiliationIndices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
40
42
  orcid: z.ZodOptional<z.ZodString>;
41
43
  }, z.core.$strip>>>;
@@ -72,6 +74,7 @@ export declare const fetchArticlesTool: import("@cyanheads/mcp-ts-core").ToolDef
72
74
  lastName: z.ZodOptional<z.ZodString>;
73
75
  firstName: z.ZodOptional<z.ZodString>;
74
76
  initials: z.ZodOptional<z.ZodString>;
77
+ suffix: z.ZodOptional<z.ZodString>;
75
78
  collectiveName: z.ZodOptional<z.ZodString>;
76
79
  }, z.core.$strip>>>;
77
80
  accession: z.ZodOptional<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-articles.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-articles.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAoWjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAWhB,yBAAyB;;mBAE3B,sEAAsE;uBAE1E,qFAAqF;;;;EA6T3F,CAAC"}
1
+ {"version":3,"file":"fetch-articles.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-articles.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAuXjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAWhB,yBAAyB;;mBAE3B,sEAAsE;uBAE1E,qFAAqF;;;;EAyU3F,CAAC"}
@@ -10,8 +10,9 @@ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
10
10
  import { NCBI_SERVICE_ERRORS } from '../../../services/error-contracts.js';
11
11
  import { getNcbiService } from '../../../services/ncbi/ncbi-service.js';
12
12
  import { parseArticleSet } from '../../../services/ncbi/parsing/article-parser.js';
13
- import { fitWholeItems } from './_budget.js';
13
+ import { buildDeferralNotice, fitWholeItems } from './_budget.js';
14
14
  import { conceptMeta, EDAM_DATA_RETRIEVAL, EDAM_PUBMED_ID, SCHEMA_SCHOLARLY_ARTICLE, } from './_concepts.js';
15
+ import { noticeLines, noticeLinks } from './_notices.js';
15
16
  import { normalizePmid, pmidStringSchema } from './_schemas.js';
16
17
  import { escapeMarkdownInline } from './_text.js';
17
18
  const AuthorSchema = z
@@ -19,6 +20,10 @@ const AuthorSchema = z
19
20
  lastName: z.string().optional().describe('Last name'),
20
21
  firstName: z.string().optional().describe('First/given name'),
21
22
  initials: z.string().optional().describe('Author initials'),
23
+ suffix: z
24
+ .string()
25
+ .optional()
26
+ .describe('Generational name suffix verbatim from NCBI `Suffix` (e.g. "Jr", "2nd", "3rd"), never normalized. Absent when NCBI gives none.'),
22
27
  collectiveName: z
23
28
  .string()
24
29
  .optional()
@@ -35,6 +40,10 @@ const InvestigatorSchema = z
35
40
  lastName: z.string().optional().describe('Last name'),
36
41
  firstName: z.string().optional().describe('First/given name'),
37
42
  initials: z.string().optional().describe('Initials'),
43
+ suffix: z
44
+ .string()
45
+ .optional()
46
+ .describe('Generational name suffix verbatim from NCBI `Suffix` (e.g. "Jr", "2nd"). Absent when NCBI gives none.'),
38
47
  affiliationIndices: z
39
48
  .array(z.number())
40
49
  .optional()
@@ -84,6 +93,10 @@ const BookEditorSchema = z
84
93
  .string()
85
94
  .optional()
86
95
  .describe('Editor initials with no separators (e.g. "MP"). Absent when NCBI supplies none, or on a group editor.'),
96
+ suffix: z
97
+ .string()
98
+ .optional()
99
+ .describe('Editor generational name suffix verbatim from NCBI `Suffix` (e.g. "Jr"). Absent when NCBI supplies none, or on a group editor.'),
87
100
  collectiveName: z
88
101
  .string()
89
102
  .optional()
@@ -260,7 +273,7 @@ const DeferredSchema = z
260
273
  .describe('PMIDs of the deferred articles, in response order. Re-call `pubmed_fetch_articles` with these as `pmids` and the same other inputs to retrieve them. Never contains a PMID from `unavailablePmids`.'),
261
274
  nextDeferredCharacters: z
262
275
  .number()
263
- .describe('Serialized size of the next deferred article — the first entry in `ids`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller article further down `ids` cannot be reached until this one fits.'),
276
+ .describe('Serialized size of the next deferred article — the first entry in `ids`, where the response stopped — and the continuation ceiling: re-calling with the deferred PMIDs needs `maxResponseCharacters` ≥ this to return anything, and a smaller article further down `ids` cannot be reached until this one fits. Repeating this same call needs `maxResponseCharacters` ≥ `returnedCharacters` + this to return this article as well.'),
264
277
  })
265
278
  .describe('Continuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.');
266
279
  export const fetchArticlesTool = tool('pubmed_fetch_articles', {
@@ -377,7 +390,11 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
377
390
  }
378
391
  if (deferred) {
379
392
  ctx.enrich({ truncated: true });
380
- ctx.enrich.notice(buildDeferralNotice(deferred));
393
+ ctx.enrich.notice(buildDeferralNotice(deferred, {
394
+ noun: 'article',
395
+ ids: deferred.ids.join(', '),
396
+ recall: 'pubmed_fetch_articles with those PMIDs',
397
+ }));
381
398
  }
382
399
  return {
383
400
  articles: returned,
@@ -399,6 +416,12 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
399
416
  // Render-time only — `structuredContent.articles[].title` keeps the
400
417
  // plain-text value the NCBI parser produced. (#102)
401
418
  lines.push(`\n### ${escapeMarkdownInline(a.title ?? a.pmid ?? 'Unknown')}`);
419
+ // A retraction or correction qualifies everything below it, so it leads,
420
+ // ahead of name lists that grow with the record. The full list further
421
+ // down keeps NCBI's order and repeats these entries. (#237)
422
+ const notices = noticeLinks(a.commentsCorrections);
423
+ if (notices.length > 0)
424
+ lines.push('', ...noticeLines(notices));
402
425
  if (a.authors?.length) {
403
426
  lines.push(`\n**Authors (${a.authors.length}):**`);
404
427
  for (const au of a.authors) {
@@ -562,18 +585,6 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
562
585
  return [{ type: 'text', text: lines.join('\n') }];
563
586
  },
564
587
  });
565
- /**
566
- * Compose the recovery notice for a response the whole-response budget bounded.
567
- * Names what was spent, which PMIDs are still retrievable, and the ceiling the
568
- * next call has to clear — so a caller reading only `content[]` can resume
569
- * without inspecting `deferred`. (#99)
570
- */
571
- function buildDeferralNotice(deferred) {
572
- const spent = deferred.returnedCharacters === 0
573
- ? `The first article alone exceeds the requested maxResponseCharacters of ${deferred.maxResponseCharacters}, so none were returned.`
574
- : `Response character budget reached: ${deferred.returnedCharacters} of ${deferred.maxResponseCharacters} characters returned.`;
575
- return `${spent} ${deferred.deferredCount} resolved article(s) were deferred whole: ${deferred.ids.join(', ')}. Re-call pubmed_fetch_articles with those PMIDs to retrieve them, or raise maxResponseCharacters to at least ${deferred.nextDeferredCharacters} — the size of the next deferred article.`;
576
- }
577
588
  function formatAuthor(au) {
578
589
  const parts = [];
579
590
  if (au.collectiveName)
@@ -583,6 +594,8 @@ function formatAuthor(au) {
583
594
  parts.push(name);
584
595
  else if (au.initials)
585
596
  parts.push(au.initials);
597
+ if (au.suffix)
598
+ parts.push(au.suffix);
586
599
  if (au.initials && name)
587
600
  parts.push(`(${au.initials})`);
588
601
  if (au.affiliationIndices?.length) {