@cyanheads/pubmed-mcp-server 2.10.13 → 2.10.14

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 (110) hide show
  1. package/AGENTS.md +22 -4
  2. package/CLAUDE.md +22 -4
  3. package/README.md +5 -5
  4. package/changelog/2.0.x/2.0.0.md +32 -0
  5. package/changelog/2.0.x/2.0.1.md +32 -0
  6. package/changelog/2.1.x/2.1.0.md +17 -0
  7. package/changelog/2.1.x/2.1.1.md +29 -0
  8. package/changelog/2.1.x/2.1.2.md +18 -0
  9. package/changelog/2.1.x/2.1.3.md +10 -0
  10. package/changelog/2.1.x/2.1.4.md +12 -0
  11. package/changelog/2.1.x/2.1.5.md +18 -0
  12. package/changelog/2.1.x/2.1.6.md +15 -0
  13. package/changelog/2.10.x/2.10.0.md +17 -0
  14. package/changelog/2.10.x/2.10.1.md +14 -0
  15. package/changelog/2.10.x/2.10.10.md +22 -0
  16. package/changelog/2.10.x/2.10.11.md +21 -0
  17. package/changelog/2.10.x/2.10.12.md +14 -0
  18. package/changelog/2.10.x/2.10.13.md +28 -0
  19. package/changelog/2.10.x/2.10.14.md +28 -0
  20. package/changelog/2.10.x/2.10.2.md +13 -0
  21. package/changelog/2.10.x/2.10.3.md +26 -0
  22. package/changelog/2.10.x/2.10.4.md +11 -0
  23. package/changelog/2.10.x/2.10.5.md +31 -0
  24. package/changelog/2.10.x/2.10.6.md +30 -0
  25. package/changelog/2.10.x/2.10.7.md +17 -0
  26. package/changelog/2.10.x/2.10.8.md +22 -0
  27. package/changelog/2.10.x/2.10.9.md +15 -0
  28. package/changelog/2.2.x/2.2.0.md +67 -0
  29. package/changelog/2.2.x/2.2.1.md +10 -0
  30. package/changelog/2.2.x/2.2.2.md +20 -0
  31. package/changelog/2.2.x/2.2.3.md +17 -0
  32. package/changelog/2.2.x/2.2.4.md +34 -0
  33. package/changelog/2.2.x/2.2.5.md +10 -0
  34. package/changelog/2.2.x/2.2.6.md +17 -0
  35. package/changelog/2.3.x/2.3.0.md +27 -0
  36. package/changelog/2.3.x/2.3.1.md +15 -0
  37. package/changelog/2.3.x/2.3.10.md +20 -0
  38. package/changelog/2.3.x/2.3.11.md +21 -0
  39. package/changelog/2.3.x/2.3.2.md +27 -0
  40. package/changelog/2.3.x/2.3.3.md +38 -0
  41. package/changelog/2.3.x/2.3.4.md +21 -0
  42. package/changelog/2.3.x/2.3.5.md +24 -0
  43. package/changelog/2.3.x/2.3.6.md +26 -0
  44. package/changelog/2.3.x/2.3.7.md +31 -0
  45. package/changelog/2.3.x/2.3.8.md +19 -0
  46. package/changelog/2.3.x/2.3.9.md +22 -0
  47. package/changelog/2.4.x/2.4.0.md +34 -0
  48. package/changelog/2.4.x/2.4.1.md +32 -0
  49. package/changelog/2.5.x/2.5.0.md +35 -0
  50. package/changelog/2.5.x/2.5.1.md +32 -0
  51. package/changelog/2.5.x/2.5.2.md +23 -0
  52. package/changelog/2.5.x/2.5.3.md +22 -0
  53. package/changelog/2.5.x/2.5.5.md +52 -0
  54. package/changelog/2.5.x/2.5.6.md +33 -0
  55. package/changelog/2.6.x/2.6.0.md +32 -0
  56. package/changelog/2.6.x/2.6.1.md +26 -0
  57. package/changelog/2.6.x/2.6.10.md +16 -0
  58. package/changelog/2.6.x/2.6.11.md +24 -0
  59. package/changelog/2.6.x/2.6.12.md +29 -0
  60. package/changelog/2.6.x/2.6.2.md +23 -0
  61. package/changelog/2.6.x/2.6.3.md +17 -0
  62. package/changelog/2.6.x/2.6.4.md +21 -0
  63. package/changelog/2.6.x/2.6.5.md +30 -0
  64. package/changelog/2.6.x/2.6.6.md +25 -0
  65. package/changelog/2.6.x/2.6.7.md +37 -0
  66. package/changelog/2.6.x/2.6.8.md +15 -0
  67. package/changelog/2.6.x/2.6.9.md +36 -0
  68. package/changelog/2.7.x/2.7.0.md +41 -0
  69. package/changelog/2.7.x/2.7.1.md +21 -0
  70. package/changelog/2.7.x/2.7.10.md +13 -0
  71. package/changelog/2.7.x/2.7.11.md +15 -0
  72. package/changelog/2.7.x/2.7.2.md +22 -0
  73. package/changelog/2.7.x/2.7.3.md +18 -0
  74. package/changelog/2.7.x/2.7.4.md +15 -0
  75. package/changelog/2.7.x/2.7.5.md +34 -0
  76. package/changelog/2.7.x/2.7.6.md +14 -0
  77. package/changelog/2.7.x/2.7.7.md +14 -0
  78. package/changelog/2.7.x/2.7.8.md +18 -0
  79. package/changelog/2.7.x/2.7.9.md +16 -0
  80. package/changelog/2.8.x/2.8.0.md +23 -0
  81. package/changelog/2.9.x/2.9.0.md +21 -0
  82. package/changelog/2.9.x/2.9.1.md +12 -0
  83. package/changelog/2.9.x/2.9.10.md +15 -0
  84. package/changelog/2.9.x/2.9.2.md +21 -0
  85. package/changelog/2.9.x/2.9.3.md +11 -0
  86. package/changelog/2.9.x/2.9.4.md +24 -0
  87. package/changelog/2.9.x/2.9.5.md +20 -0
  88. package/changelog/2.9.x/2.9.6.md +22 -0
  89. package/changelog/2.9.x/2.9.7.md +26 -0
  90. package/changelog/2.9.x/2.9.8.md +15 -0
  91. package/changelog/2.9.x/2.9.9.md +35 -0
  92. package/changelog/template.md +151 -0
  93. package/dist/index.js +1 -0
  94. package/dist/index.js.map +1 -1
  95. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
  96. package/dist/services/europe-pmc/europe-pmc-service.js +3 -10
  97. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
  98. package/dist/services/ncbi/ncbi-service.d.ts +0 -2
  99. package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
  100. package/dist/services/ncbi/ncbi-service.js +2 -9
  101. package/dist/services/ncbi/ncbi-service.js.map +1 -1
  102. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  103. package/dist/services/openalex/openalex-service.js +3 -10
  104. package/dist/services/openalex/openalex-service.js.map +1 -1
  105. package/package.json +17 -8
  106. package/server.json +3 -3
  107. package/dist/services/retry-policy.d.ts +0 -18
  108. package/dist/services/retry-policy.d.ts.map +0 -1
  109. package/dist/services/retry-policy.js +0 -21
  110. package/dist/services/retry-policy.js.map +0 -1
@@ -0,0 +1,31 @@
1
+ ---
2
+ summary: "Adopts mcp-ts-core 0.12.5: no HTTP status classifies as InternalError anymore (500/501 join the rest of 5xx as ServiceUnavailable), and an SSRF DNS-guard bypass on Bun 1.4 Linux is closed. The NCBI, OpenAlex, and Europe PMC retry loops now honor the framework's retryable:false opt-out so a 501 fails on the first attempt instead of retrying."
3
+ breaking: false
4
+ security: true
5
+ ---
6
+
7
+ # 2.10.5 — 2026-09-02
8
+
9
+ ## Changed
10
+
11
+ - **`@cyanheads/mcp-ts-core` `^0.12.3` → `^0.12.5`** — no HTTP status classifies as `InternalError` anymore; 500 and 501 join the rest of the 5xx range as `ServiceUnavailable` (504 stays `Timeout`), and a caller disconnect now classifies as `RequestCancelled` (-32011) rather than `InternalError`.
12
+ - **Shipped skills re-synced** — `add-tool` 2.21, `api-config` 1.15, `api-context` 2.2, `api-errors` 1.8, `api-linter` 1.13, `api-utils` 2.8, `design-mcp-server` 2.23, `maintenance` 2.6, `release-and-publish` 2.13, `setup` 1.10.
13
+
14
+ ## Fixed
15
+
16
+ - **Retry loops stop retrying a non-retryable 501** — new `src/services/retry-policy.ts` adds `isTransient()`, honoring the framework's `data.retryable === false` opt-out ahead of code-based classification. Folding 501 into the transient `ServiceUnavailable` code otherwise made the NCBI, OpenAlex, and Europe PMC retry loops re-ask a method the upstream declared unimplemented. `NcbiApiClient`'s local 500→`ServiceUnavailable` `codeOverride` is removed — the framework now classifies it natively.
17
+ - **`.env.example` / README now show `MCP_SESSION_MODE=stateless`** — matching the Dockerfile default this server has shipped with; the prior `auto` example resolved to `stateful`, a mode this server has no `ctx.requestInput` call sites to use.
18
+
19
+ ## Security
20
+
21
+ - **SSRF DNS-guard bypass on Bun 1.4 Linux** (inherited from `@cyanheads/mcp-ts-core` 0.12.4) — `assertDnsNotPrivate` now queries both `node:dns` resolvers, closing a gap where a hostname only the system resolver could see (`/etc/hosts`, split DNS, an NSS module) passed the guard and connected.
22
+
23
+ ## Dependencies
24
+
25
+ - `@cyanheads/mcp-ts-core` `^0.12.3` → `^0.12.5`
26
+ - `defuddle` `^0.19.2` → `^0.19.3`
27
+ - `fast-xml-parser` `^5.11.0` → `^5.11.1`
28
+ - `zod` `^4.4.3` → `^4.5.4`
29
+ - `@biomejs/biome` (dev) `^2.5.9` → `^2.5.11`
30
+ - `@types/node` (dev) `^26.2.0` → `^26.4.0`
31
+ - `ignore` (dev) `^7.0.6` → `^7.0.7`
@@ -0,0 +1,30 @@
1
+ ---
2
+ summary: "Five bug fixes: pubmed_find_related no longer returns false-empty Europe PMC pages or a fake empty success when every provider fails, Europe PMC title/author/journal markup is stripped and Markdown-escaped, an all-numeric spell_check query round-trips as text, and Unpaywall HTML is no longer parsed as a PDF. Adopts mcp-ts-core 0.12.7."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.6 — 2026-09-08
8
+
9
+ ## Changed
10
+
11
+ - **`@cyanheads/mcp-ts-core` `^0.12.5` → `^0.12.7`** — an argument rejection now carries `structuredContent.error` classified as `InvalidParams` rather than `ValidationError`, on every transport; Node and workerd type checking run as separate programs, lifting the `@cloudflare/workers-types` hold. Packaging validation adopted here checks each present plugin manifest's `version` against `package.json`'s.
12
+ - **Shipped skills re-synced** — `add-tool` 2.22, `api-errors` 1.9, `api-workers` 1.8, `design-mcp-server` 2.24, `field-test` 2.10, `git-wrapup` 1.12.
13
+
14
+ ## Fixed
15
+
16
+ - **`pubmed_find_related`'s Europe PMC fallback no longer returns false-empty pages** (#101) — `epmcProvider()` pages Europe PMC (up to 1000 rows/page, up to 10 pages) until the requested window of PubMed-addressable rows is filled instead of fetching a single page capped at 100. `EuropePmcService.citations()`/`references()` now report `droppedNoPmid` (non-`MED` rows with no PubMed PMID) and `hitCount` separately from the addressable `totalCount`; the tool discloses excluded rows and a page-cap shortfall in its enrichment notice instead of silently returning an empty window.
17
+ - **`pubmed_find_related` fails instead of faking an empty success when every provider fails** (#103) — a chain where NCBI, Europe PMC, and OpenAlex all fail now throws a typed `all_providers_failed` error carrying each attempted provider's reason (`data.attempted`), rather than returning `articles: []` with an invented `source: "ncbi"`. A provider disabled by server configuration (`EUROPEPMC_ENABLED=false`, no `OpenAlexService`) is now reported as `provider_disabled` and excluded from the retry hint; a provider that genuinely serves zero related records still succeeds unchanged.
18
+ - **Europe PMC `title`/`authors`/`journal` markup no longer leaks into either output surface** (#102) — `pubmed_europepmc_fetch` and `pubmed_europepmc_search` now run all three through `toDisplayText()`, the same JATS/HTML-stripping, entity-decoding pass already applied to `abstractText`. A new `escapeMarkdownInline()` (`src/mcp-server/tools/definitions/_text.ts`) additionally neutralizes Markdown-significant characters at every title-as-heading interpolation in `content[]` — both Europe PMC tools plus `pubmed_fetch_articles`, `pubmed_fetch_fulltext`, `pubmed_search_articles`, and `pubmed_find_related` — so upstream text can no longer alter heading structure, toggle emphasis, or inject a link; `structuredContent` keeps the unescaped plain-text value.
19
+ - **`pubmed_spell_check` no longer fails output validation on an all-numeric query** (#108) — `NcbiService.eSpell` now parses its response with a new verbatim (`parseTagValue: false`) XML parser, so a query like `33306283` round-trips as the string `"33306283"` instead of being coerced to a number, and `"007"` / `"1e5"` round-trip byte-identical instead of losing their leading zero or exponential form.
20
+ - **`pubmed_fetch_fulltext` no longer parses an HTML paywall page as a PDF** (#104) — `UnpaywallService.fetchAs` classifies a fetched Unpaywall response by its bytes' `%PDF-` magic header instead of the requested kind, so a `url_for_pdf` response that is actually HTML (a publisher paywall/interstitial) falls through to the `location.url` landing page instead of failing with a misleading `Invalid PDF structure` error; a genuine PDF served under an unexpected `content-type` still parses.
21
+
22
+ ## Dependencies
23
+
24
+ - `@cyanheads/mcp-ts-core` `^0.12.5` → `^0.12.7`
25
+ - `@biomejs/biome` (dev) `^2.5.11` → `^2.5.12`
26
+ - `@types/node` (dev) `^26.4.0` → `^26.4.1`
27
+ - `@vitest/coverage-istanbul` (dev) `^4.1.11` → `^5.0.0`
28
+ - `ignore` (dev) `^7.0.7` → `^7.0.8`
29
+ - `tsc-alias` (dev) `^1.9.2` → `^1.9.4`
30
+ - `vitest` (dev) `^4.1.11` → `^5.0.0`
@@ -0,0 +1,17 @@
1
+ ---
2
+ summary: "pubmed_fetch_articles and pubmed_fetch_fulltext gain an opt-in maxResponseCharacters whole-response budget with deferred-article continuation; fetch_fulltext reports unqueriedTiers when a search skipped an unconfigured tier; format_citations names accepted format values on an invalid input."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.7 — 2026-09-09
8
+
9
+ ## Added
10
+
11
+ - **`maxResponseCharacters`** on `pubmed_fetch_articles` (#99) and `pubmed_fetch_fulltext` (#100) — an opt-in whole-response character ceiling, distinct from `fetch_fulltext`'s existing per-article `maxCharacters`. Articles are kept in response order until the next one would cross the ceiling; the rest are deferred whole (never partially populated) and listed in the new `deferred.ids`, keyed under the branch they were requested on (`pmids`/`pmcids`/`dois` for `fetch_fulltext`). Re-calling with `deferred.ids` resumes exactly where the response stopped. Omitting the field is byte-identical to prior output. Shared accounting lives in the new `src/mcp-server/tools/definitions/_budget.ts` (`fitWholeItems`, `serializedCharacters`).
12
+ - **`unavailable[].unqueriedTiers`** on `pubmed_fetch_fulltext` (#110) — lists tiers (`europepmc`, `unpaywall`) the chain skipped because this deployment has not configured them and that could have served the id, so a caller can tell an incomplete search from a settled miss. A tier inapplicable to the id (no DOI for Unpaywall) is never listed.
13
+
14
+ ## Changed
15
+
16
+ - **`pubmed_fetch_fulltext`'s `reason` no longer folds an unconfigured tier's absence into the reported signal** (#110) — `reason` now always reflects the most specific content signal a tier that actually answered reported; the incompleteness moved to `unqueriedTiers` instead of silently overriding `reason`. Two reason values shift as a result: a DOI-less record with Unpaywall unconfigured now reports `no-doi` (was `no-pmc-fallback-disabled`), and a PMCID/PMID no tier indexes now reports `not-found` even when Unpaywall's last chain entry is `no-doi`.
17
+ - **`pubmed_format_citations`'s `format` union now names its accepted values on a top-level validation failure** — an invalid value (e.g. `"chicago"`) surfaces `Invalid option: expected one of "apa"|"mla"|"bibtex"|"ris"|"vancouver", or a non-empty array of those values` in `content[]` directly, instead of a generic `Invalid input` that buried the accepted values inside `structuredContent.error`.
@@ -0,0 +1,22 @@
1
+ ---
2
+ summary: "ECitMatch reconciliation now keys on per-request wire tokens so duplicate caller keys no longer misassign results; pubmed_fetch_fulltext folds JATS sections nested three or more levels deep into the deepest surviving subsection's text; mixed-citation references separate zero-gap adjacent pub-ids and title/volume pairs."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.8 — 2026-09-09
8
+
9
+ ## Changed
10
+
11
+ - **`.env.example` Supabase keys** — `SUPABASE_SERVICE_ROLE_KEY` is what the `supabase` storage provider requires and `SUPABASE_ANON_KEY` is optional; each line now names its role.
12
+
13
+ ## Fixed
14
+
15
+ - **`pubmed_lookup_citation` duplicate-key result misassignment** — `eCitMatch` reconciled response rows by the caller-supplied `key`, which may repeat; a duplicate (or a caller key colliding with an auto-assigned one) collapsed two citations onto one row and handed the second citation's PMID and validation context to the first. Reconciliation now runs on a per-request wire token unique within the call, restoring the caller's key on the way out. ([#113](https://github.com/cyanheads/pubmed-mcp-server/issues/113))
16
+ - **`pubmed_fetch_fulltext` sections nested three or more levels deep** — the output schema declares two levels of JATS section nesting; deeper `<sec>` elements were silently stripped by output validation. They now fold into the deepest surviving subsection's `text`, each heading on its own line; the schema stays at two levels since `format-parity`'s 8-hop walker already spends its budget reaching `sections[].subsections[]`. The character-budget report's `originalCharacters` now counts those folded heading lines, since they're real characters in the returned text. ([#112](https://github.com/cyanheads/pubmed-mcp-server/issues/112))
17
+ - **`pubmed_fetch_fulltext` reference citations with zero-gap adjacent elements** — JATS `mixed-citation` rendered via flat `textContent()`, which glues together elements with no separating text: adjacent typed `<pub-id>`s fused into one token, and an inline italic title running straight into a following bold volume fused the same way. `extractReferences` now renders `mixed-citation` child-by-child, labeling typed pub-ids and inserting a single space between zero-gap adjacent elements. ([#115](https://github.com/cyanheads/pubmed-mcp-server/issues/115), [#123](https://github.com/cyanheads/pubmed-mcp-server/issues/123))
18
+
19
+ ## Dependencies
20
+
21
+ - `@cyanheads/mcp-ts-core` ^0.12.7 → ^0.12.8 — releases now run through a gated release PR: `git-wrapup` stops at a pushed `release/<version>` branch with an open PR, and `release-and-publish` fast-forwards `main` and tags its tip.
22
+ - Skills synced from 0.12.8 — new `release-pr-review` 1.0; `api-config` 1.16, `api-services` 1.5, `api-telemetry` 1.8, `api-utils` 2.9, `code-simplifier` 1.4, `field-test` 2.12, `git-wrapup` 1.13, `orchestrations` 1.8, `release-and-publish` 2.14.
@@ -0,0 +1,15 @@
1
+ ---
2
+ summary: "pubmed_fetch_fulltext now extracts JATS tables as structured cells, finds Europe PMC references nested under <body> at any depth, and matches a sections filter against subsection titles too; mixed-citation author names and Europe PMC's numeric-text coercion are also fixed."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.9 — 2026-09-10
8
+
9
+ ## Fixed
10
+
11
+ - **`pubmed_fetch_fulltext` JATS tables dropped or fused into prose** — a `<table-wrap>` beside a section's paragraphs was silently absent from the response, and one nested inside a `<p>` had its cells concatenated into the surrounding sentence with no delimiter. Tables are now extracted into a new `tables[]` field (article-level, covering body, `<floats-group>`, `<back>`, and appendix deposits) with `colspan`/`rowspan` expanded to one cell per grid column; a deposit with no readable markup (graphic-only, CALS `<tgroup>`) is returned labeled with `unextractableReason` instead of silently missing. New `includeTables` input (default `true`); table text now counts against `maxCharacters` and a table that doesn't fit is dropped whole rather than cut mid-row. ([#111](https://github.com/cyanheads/pubmed-mcp-server/issues/111))
12
+ - **`pubmed_fetch_fulltext` Europe PMC references nested under `<body>` discarded** — `extractReferences` searched only a direct `<back>` child for `<ref-list>`, missing the majority Europe PMC placement (`body/sec/sec/ref-list`) entirely. It now finds `<ref-list>` at any depth under the article, deduplicating by `<ref>` id so a document exposing the same list under both `<back>` and `<body>` still yields each reference once. ([#116](https://github.com/cyanheads/pubmed-mcp-server/issues/116))
13
+ - **`pubmed_fetch_fulltext` mixed-citation author names glued together** — a `<name>`/`<string-name>`/`<person-group>` with no text between `<surname>` and `<given-names>` rendered joined (`NybakkenJW`); `renderMixedCitation` now applies its zero-gap element spacing rule inside author-name wrappers, not just between a citation's direct children. ([#124](https://github.com/cyanheads/pubmed-mcp-server/issues/124))
14
+ - **`pubmed_fetch_fulltext` `sections` filter matched only top-level titles** — a filter term naming a subsection heading (e.g. `"Demographics"` nested under `"Results"`) returned nothing, with no indication that subsections were unmatchable. Matching is now recursive across nesting depth; a section kept only because a descendant matched is returned as a breadcrumb, with its own text cleared and just the matching branch beneath it. ([#126](https://github.com/cyanheads/pubmed-mcp-server/issues/126))
15
+ - **Europe PMC full-text parser coerced numeric-looking text** — `EuropePmcService`'s XML parser ran `parseTagValue: true` against a comment claiming parity with the PMC parser, so bibliographic tokens (page ranges, reference labels like `"1."`) were coerced through `Number` on the Europe PMC path but not the PMC EFetch path. Both parsers now build from one shared `ORDERED_XML_PARSER_OPTIONS` constant, keeping the two paths byte-identical in configuration — which also gives the Europe PMC path the `maxTotalExpansions` entity-expansion cap it had been running without. ([#127](https://github.com/cyanheads/pubmed-mcp-server/issues/127))
@@ -0,0 +1,67 @@
1
+ ---
2
+ summary: "The server was migrated to use the `@cyanheads/mcp-ts-core` framework for MCP plumbing."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.0 — 2026-03-23
7
+
8
+ ## Framework Migration
9
+
10
+ The server was migrated to use the `@cyanheads/mcp-ts-core` framework for MCP plumbing. This will simplify and streamline future development.
11
+
12
+ ## Tool Renames
13
+
14
+ All tools were renamed for clarity. Schemas and capabilities are unchanged.
15
+
16
+ | Previous (v2.1.x) | New (v2.2.0) |
17
+ |:-------------------|:-------------|
18
+ | `pubmed_search` | `pubmed_search_articles` |
19
+ | `pubmed_fetch` | `pubmed_fetch_articles` |
20
+ | `pubmed_pmc_fetch` | `pubmed_fetch_fulltext` |
21
+ | `pubmed_related` | `pubmed_find_related` |
22
+ | `pubmed_cite` | `pubmed_format_citations` |
23
+ | `pubmed_mesh_lookup` | `pubmed_lookup_mesh` |
24
+ | `pubmed_spell` | `pubmed_spell_check` |
25
+
26
+ ## Changed
27
+
28
+ - **Framework migration**: Replaced inline framework code (~58k lines) with `@cyanheads/mcp-ts-core` package dependency. All tools, resources, and prompts now use the framework's declarative builders (`tool()`, `resource()`, `prompt()`)
29
+ - **Tool definitions**: Rewritten from handler-factory pattern to single-file `tool()` builder definitions with Zod input/output schemas, `format` functions, and `annotations`
30
+ - **Resource definition**: `database-info.resource.ts` migrated from custom `ResourceDefinition` type to framework's `resource()` builder with `handler(params, ctx)` pattern
31
+ - **Prompt definition**: `research-plan.prompt.ts` migrated from custom `PromptDefinition` type to framework's `prompt()` builder
32
+ - **Entry point**: `src/index.ts` simplified from DI container + server bootstrap to single `createApp()` call with tool/resource/prompt arrays
33
+ - **NCBI service**: Flattened from `services/ncbi/core/` subdirectory to `services/ncbi/` top-level; uses framework's `logger` instead of custom logger
34
+ - **Config**: Replaced monolithic `src/config/index.ts` with focused `src/config/server-config.ts` (NCBI-specific env vars only; framework handles transport, auth, storage)
35
+ - **Build**: Switched from custom build scripts to framework-provided `tsconfig.base.json`, `biome.json`, and `vitest.config.ts` extensions
36
+ - **Tool file renames**: Files renamed to match tool names (e.g., `pubmed-search.tool.ts` → `search-articles.tool.ts`, `pubmed-spell.tool.ts` → `spell-check.tool.ts`)
37
+ - **CLAUDE.md**: Replaced generic placeholder patterns with actual server examples (spell-check tool, database-info resource, NCBI config), updated structure tree, removed unused context properties
38
+ - **README.md**: Updated all tool names and descriptions to match renames, updated config section
39
+ - **Dockerfile**: Fixed image title/description labels, added `source` label, corrected log directory name and default port
40
+ - **Default HTTP port**: Reverted to `3010` across `.env.example`, `Dockerfile`, `README.md`, and `server.json` (was changed to `3017` in 2.0.1)
41
+ - **server-config.ts**: Replaced `z.string().email()` with `z.email()` shorthand
42
+ - **.env.example**: Added `NCBI_TIMEOUT_MS` entry
43
+
44
+ ## Added
45
+
46
+ - **Test suite**: 178 tests across 17 files in `tests/` mirroring `src/` structure — covers config, NCBI service layer, XML/JSON parsers, citation formatters, all 7 tools, 1 resource, and 1 prompt using `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
47
+ - **Skills directory**: Framework skill files for development workflows (add-tool, add-resource, devcheck, field-test, etc.)
48
+ - **MCP definition linter**: `bun run lint:mcp` validates tool/resource/prompt definitions against the MCP spec at build time
49
+ - **devcheck.config.json**: Centralized devcheck configuration
50
+
51
+ ## Fixed
52
+
53
+ - **fetch-articles**: Added `unavailablePmids` to output — surfaces which requested PMIDs returned no article data
54
+ - **fetch-fulltext**: Added `unavailablePmcIds` to output — tracks which PMC IDs returned no data; fetch failures now return a graceful empty result instead of throwing
55
+ - **research-plan prompt**: Corrected tool reference from `pubmed_mesh_lookup` to `pubmed_lookup_mesh`; clarified `includeAgentPrompts` description
56
+
57
+ ## Security
58
+
59
+ - **package.json**: Added `overrides` to pin transitive dependencies `express-rate-limit` (>=8.2.2) and `hono` (>=4.12.7) to patched versions
60
+
61
+ ## Removed
62
+
63
+ - **Inline framework code**: DI container, transport layer (stdio/HTTP/Workers), storage providers, auth strategies, error handler, logger, telemetry, utilities — all now provided by `@cyanheads/mcp-ts-core`
64
+ - **Legacy tests**: Old test suite removed (covered framework internals, not server logic); replaced by new `tests/` suite
65
+ - **Worker entry point**: `src/worker.ts` removed (framework handles Workers deployment via `createWorkerHandler()`)
66
+ - **Cloudflare config**: `wrangler.toml`, `schemas/cloudflare-d1-schema.sql` removed
67
+ - **Misc**: `.husky/pre-commit`, `smithery.yaml`, `repomix.config.json`, `typedoc.json`, `tsdoc.json`, various README docs in `src/`
@@ -0,0 +1,10 @@
1
+ ---
2
+ summary: "Fix: adds missing `mcpName` field to `package.json` required by the MCP registry for publishing."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.1 — 2026-03-23
7
+
8
+ ## Fixed
9
+
10
+ - **package.json**: Added `mcpName` field required by the MCP registry for publishing
@@ -0,0 +1,20 @@
1
+ ---
2
+ summary: "Format improvements for `fetch-articles`, `fetch-fulltext`, and `find-related`; NCBI raw exception traces replaced with user-friendly messages; `@cyanheads/mcp-ts-core` 0.1.29."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.2 — 2026-03-24
7
+
8
+ ## Changed
9
+
10
+ - **fetch-articles format**: Now displays authors (first 3 + "et al."), journal info (abbreviation, year, volume, issue, pages), publication types, and unavailable PMIDs
11
+ - **fetch-fulltext format**: Renders subsections within body sections
12
+ - **find-related**: Added `source` and `pubDate` fields to output schema and format display
13
+
14
+ ## Fixed
15
+
16
+ - **NCBI error messages**: Raw C++ exception traces from NCBI are now replaced with concise, user-friendly messages
17
+
18
+ ## Updated
19
+
20
+ - `@cyanheads/mcp-ts-core` to 0.1.29
@@ -0,0 +1,17 @@
1
+ ---
2
+ summary: "Retry logic moved to `NcbiService.performRequest` to cover XML-level NCBI errors; backoff changed to 1s base; HTML rate-limit responses now throw `ServiceUnavailable`. 8 new retry integration tests."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.3 — 2026-03-24
7
+
8
+ ## Changed
9
+
10
+ - **Retry logic**: Moved retry with exponential backoff from `NcbiApiClient` (HTTP-only) to `NcbiService.performRequest`, so retries now cover both HTTP-level failures and XML-level NCBI errors (e.g., 200 OK with C++ exception traces in the response body)
11
+ - **Backoff timing**: Retry delays changed from 200ms base (200, 400, 800ms) to 1s base (1s, 2s, 4s) for more conservative backoff
12
+ - **`api-client`**: Simplified to single-attempt; now checks `response.ok` and throws `ServiceUnavailable` for non-OK HTTP status codes
13
+
14
+ ## Added
15
+
16
+ - **HTML response detection**: `NcbiResponseHandler` now detects HTML responses from NCBI (typically rate-limiting pages) and throws `ServiceUnavailable` instead of an opaque XML parse error
17
+ - **Retry integration tests**: New colocated test file `src/services/ncbi/ncbi-service.test.ts` — 8 tests covering HTTP retry, XML-level retry, timeout retry, non-retryable error passthrough, exhaustion messaging, and backoff timing
@@ -0,0 +1,34 @@
1
+ ---
2
+ summary: "Format output enriched — `pubmed_fetch_articles` gains affiliations, MeSH/grants; `pubmed_fetch_fulltext` gains authors, journal, references. Deps: `mcp-ts-core` →0.2.3, `biome` →2.4.9."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.4 — 2026-03-28
7
+
8
+ ## Added
9
+
10
+ - **fetch-articles format**: Affiliations, keywords, MeSH terms (with major topic markers and qualifiers), and grant information now rendered in format output
11
+ - **fetch-fulltext format**: Authors, affiliations, journal info, article type, publication date, PubMed URL, keywords, and reference list now rendered in format output; unavailable PMC IDs surfaced
12
+ - **Skills**: `report-issue-framework` and `report-issue-local` for filing bugs/feature requests against the framework or this server
13
+
14
+ ## Changed
15
+
16
+ - **polish-docs-meta skill**: Updated to v1.2 — added GitHub repo metadata sync step, description propagation rule (`package.json` → README header, `server.json`, Dockerfile), renumbered checklist steps
17
+
18
+ ## Refactored
19
+
20
+ - Optional chaining cleanup in `article-parser.ts` and `fetch-articles.tool.ts` (replaced `x && x.y` with `x?.y`)
21
+
22
+ ## Updated
23
+
24
+ - `@cyanheads/mcp-ts-core` to ^0.2.3
25
+ - `@biomejs/biome` to ^2.4.9
26
+ - `vitest` to ^4.1.2
27
+
28
+ ## Security
29
+
30
+ - Added overrides for `brace-expansion` (>=2.0.3), `path-to-regexp` (>=8.4.0), `picomatch` (>=4.0.4), `yaml` (>=2.8.3)
31
+
32
+ ## Docs
33
+
34
+ - Added `LOGS_DIR` env var to README and reference docs
@@ -0,0 +1,10 @@
1
+ ---
2
+ summary: "Maintenance — `@cyanheads/mcp-ts-core` bumped to ^0.2.8. No runtime API changes."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.5 — 2026-03-28
7
+
8
+ ## Updated
9
+
10
+ - `@cyanheads/mcp-ts-core` to ^0.2.8
@@ -0,0 +1,17 @@
1
+ ---
2
+ summary: "Skill updates — `add-tool` v1.1 expands `format()` template and adds Tool Response Design section; `add-resource` and `design-mcp-server` gain coverage guidance and tools-first patterns. Bumps `mcp-ts-core` ^0.2.10, `biome` ^2.4.10."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.2.6 — 2026-03-30
7
+
8
+ ## Changed
9
+
10
+ - **add-tool skill** (v1.1): Content-complete `format()` template; new Tool Response Design section covering batch input, partial success, empty results, error classification, operational metadata, and context budget
11
+ - **add-resource skill** (v1.1): Added tool coverage guidance — verify data is reachable via the tool surface for tool-only clients
12
+ - **design-mcp-server skill** (v2.1): Tools-first design philosophy, live API probing step, batch input design patterns, convenience shortcuts, error design table with classification, resilience and API efficiency planning, naming convention refinement
13
+
14
+ ## Updated
15
+
16
+ - `@cyanheads/mcp-ts-core` to ^0.2.10
17
+ - `@biomejs/biome` to ^2.4.10
@@ -0,0 +1,27 @@
1
+ ---
2
+ summary: "Adds `pubmed_lookup_citation` (ECitMatch, batch 25) and `pubmed_convert_ids` (DOI/PMID/PMCID via PMC ID Converter, batch 50). Refactored retry logic and HTTP error classification in `NcbiApiClient`."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.0 — 2026-03-31
7
+
8
+ ## Added
9
+
10
+ - **`pubmed_lookup_citation` tool**: Resolve partial bibliographic references (journal, year, volume, page, author) to PubMed IDs via NCBI ECitMatch. Batch up to 25 citations per request with deterministic matching.
11
+ - **`pubmed_convert_ids` tool**: Convert between DOI, PMID, and PMCID using the PMC ID Converter API. Batch up to 50 IDs per request; returns all available identifier mappings.
12
+ - **`NcbiService.eCitMatch()`**: ECitMatch service method — formats bdata pipe-delimited strings, parses multi-line responses, handles NOT_FOUND/AMBIGUOUS results.
13
+ - **`NcbiService.idConvert()`**: PMC ID Converter service method — JSON-based external API call with error classification.
14
+ - **`NcbiApiClient.makeExternalRequest()`**: HTTP client method for non-eutils NCBI endpoints (e.g., PMC ID Converter). Uses plain fetch with `AbortSignal.timeout` for response body access on error status codes.
15
+ - **Test coverage**: Full test suites for both new tools and service methods (eCitMatch parsing, idConvert JSON handling, input validation, format output)
16
+
17
+ ## Changed
18
+
19
+ - **Retry logic**: Extracted inline retry loop from `performRequest` into reusable `withRetry()` method, shared by both eutils and external API calls
20
+ - **HTTP error classification**: `NcbiApiClient` now distinguishes 4xx (InvalidRequest) from 5xx (ServiceUnavailable) errors instead of treating all non-OK responses as ServiceUnavailable
21
+ - **Endpoint suffix handling**: `api-client.ts` skips `.fcgi` suffix for endpoints that already contain a dot (e.g., `ecitmatch.cgi`)
22
+
23
+ ## Docs
24
+
25
+ - Updated README tool count (7 → 9), added tool descriptions and detail sections for both new tools
26
+ - Updated CLAUDE.md tool count (7 → 9)
27
+ - Regenerated `docs/tree.md` with new files
@@ -0,0 +1,15 @@
1
+ ---
2
+ summary: "Fix `pubmed_search_articles` ignoring empty `dateRange` strings — skips date clause instead of producing a malformed NCBI query (closes [#14](https://github.com/cyanheads/pubmed-mcp-server/issues/14))."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.1 — 2026-04-01
7
+
8
+ ## Fixed
9
+
10
+ - **`pubmed_search_articles`**: Empty `dateRange` strings (e.g., `{ minDate: "", maxDate: "" }`) no longer produce a malformed NCBI query returning 0 results — the handler now skips the date clause when either date is empty ([#14](https://github.com/cyanheads/pubmed-mcp-server/issues/14))
11
+ - **`pubmed_search_articles`**: Date field descriptions updated to reflect accepted NCBI formats (`YYYY/MM/DD`, `YYYY/MM`, or `YYYY`)
12
+
13
+ ## Added
14
+
15
+ - **Test coverage**: 7 new tests for `dateRange` handling — empty strings, omitted dateRange, partial dates, valid dates, and dash-to-slash conversion
@@ -0,0 +1,20 @@
1
+ ---
2
+ summary: "PMC full-text parser preserves document order via `preserveOrder: true` (closes [#19](https://github.com/cyanheads/pubmed-mcp-server/issues/19)); all-invalid-ID fetches now surface failures (closes #20)."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.10 — 2026-04-20
7
+
8
+ ## Fixed
9
+
10
+ - **`pubmed_fetch_fulltext` — PMC parser lost document order on mixed-content markup** (`pmc-article-parser.ts`, `response-handler.ts`, `pmc-xml-helpers.ts`): `extractTextContent` consumed `fast-xml-parser` output in `preserveOrder: false` mode, which collapses `#text` fragments and reorders inline children by property-key order. Older Science/Nature PMC deposits (e.g. `PMC4089965`) came back with garbled abstracts — sentences interleaved, gene lists detached — and `sections: []` because the body's direct `<p>` children were skipped whenever a trailing supplementary `<sec>` was present. Added a second `FastXmlParser` instance with `preserveOrder: true, trimValues: false` dedicated to PMC, routed via a new `useOrderedParser` flag on `NcbiRequestOptions`. New `pmc-xml-helpers.ts` module provides typed helpers (`tagNameOf`, `childrenOf`, `attrOf`, `textContent`, `findOne`, `findAll`) over the ordered shape. Rewrote `pmc-article-parser.ts` around them; `extractBodySections` now walks body children in document order so mixed `<p>` + `<sec>` bodies preserve their main text. PubMed E-utilities parsing is unchanged — the ordered parser is opt-in.
11
+ - **`pubmed_fetch_articles` / `pubmed_fetch_fulltext` — silent empty payload when all requested IDs are invalid** (`fetch-articles.tool.ts`, `fetch-fulltext.tool.ts`): Both tools short-circuited before computing `unavailablePmids` / `unavailablePmcIds`, so a caller passing a single bad ID received `{ articles: [], totalReturned: 0 }` with no signal about what failed. The mixed-input path (some valid, some invalid) worked correctly; only the all-invalid path regressed. Dropped the early returns; the existing post-parse set-difference logic now covers every case uniformly. Also narrowed the ordered-mode error tag regex in `response-handler.ts` to case-sensitive `<ERROR>` so PMC's lowercase per-ID `<error id="…">` element (returned for missing PMCIDs) falls through as data rather than triggering a retry-throw cascade.
12
+
13
+ ## Changed
14
+
15
+ - **Removed obsolete `XmlJats*` types** (`types.ts`): The unordered JATS element types no longer describe the parser output for PMC responses. Replaced with a short note pointing readers at `pmc-xml-helpers.ts` for the `JatsNode` / `JatsNodeList` interface.
16
+
17
+ ## References
18
+
19
+ - Closes [#19](https://github.com/cyanheads/pubmed-mcp-server/issues/19) — PMC full-text parser lost document order on complex inline markup.
20
+ - Closes [#20](https://github.com/cyanheads/pubmed-mcp-server/issues/20) — `fetch_articles` / `fetch_fulltext` silently returned empty when all IDs were invalid.
@@ -0,0 +1,21 @@
1
+ ---
2
+ summary: "`pubmed_fetch_articles` format() renders all schema fields in `content[]` (closes [#26](https://github.com/cyanheads/pubmed-mcp-server/issues/26)); actionable PMID validation errors (#27); parser omits empty arrays for absent fields (#28)."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.11 — 2026-04-20
7
+
8
+ ## Fixed
9
+
10
+ - **`pubmed_fetch_articles` — `format()` silently dropped schema fields from `content[]`** (`fetch-articles.tool.ts`): The rendered markdown that most LLM clients forward to the model omitted `articleDates`, per-author `firstName` / `orcid` / `affiliationIndices`, journal `issn` / `eIssn` / full publication date (month, day, medlineDate), the raw `pmcId`, grant `acronym`, and collapsed authors beyond the third to `et al.` — even though every value was present in `structuredContent`. Rewrote the formatter: authors render as a bulleted list with `firstName lastName`, 1-based affiliation markers that point into a new numbered `Affiliations` section, and an inline ORCID suffix when present; the journal line now renders year/month/day (or `medlineDate`) and the preferred ISSN; a new `Article Dates` line surfaces electronic/received/revised dates; grants show the acronym alongside the ID; and empty-result responses include a one-line hint that points the caller at `pubmed_search_articles`. Field-tested end-to-end over HTTP with PMIDs 13054692, 17960126, 36813558 to confirm every field in the output schema now appears in `content[]`.
11
+
12
+ ## Changed
13
+
14
+ - **PMID schema validation error message — actionable across all tools accepting PMIDs** (`fetch-articles.tool.ts`, `fetch-fulltext.tool.ts`, `find-related.tool.ts`, `format-citations.tool.ts`): The shared `z.string().regex(/^\d+$/)` guard previously produced the raw `Invalid string: must match pattern /^\d+$/` message for every failure mode — trailing whitespace, comma-joined IDs, and non-digit input all looked identical. Supplied an explicit regex message that names the domain concept, shows an example (`"13054692"`), and lists the common pitfalls (whitespace, commas, non-digit characters) so the caller can self-correct without inspecting the regex.
15
+ - **Article parser omits empty arrays for absent optional fields** (`article-parser.ts`): `parseFullArticle` no longer returns `publicationTypes: []`, `keywords: []`, `articleDates: []`, `meshTerms: []` (when `includeMesh: true` but none present), or `grantList: []` (when `includeGrants: true` but none present). The schema already marked these `.optional()`; now the runtime matches the types. Reduces payload size on older papers (Watson & Crick 1953 no longer carries three empty arrays) and tightens the contract between parser output and the output schema.
16
+
17
+ ## References
18
+
19
+ - Closes [#26](https://github.com/cyanheads/pubmed-mcp-server/issues/26) — `fetch_articles` format() dropped fields from `content[]` that were present in `structuredContent`.
20
+ - Closes [#27](https://github.com/cyanheads/pubmed-mcp-server/issues/27) — PMID validation error was opaque; now surfaces actionable guidance.
21
+ - Closes [#28](https://github.com/cyanheads/pubmed-mcp-server/issues/28) — Parser returned empty arrays for absent fields instead of omitting them.
@@ -0,0 +1,27 @@
1
+ ---
2
+ summary: "ESummary date parsing fix (`parseNcbiDate`), `pubmed_fetch_fulltext` now propagates eFetch errors, `pubmed_search_articles` uses `effectiveQuery` for PubMed link, `pubmed_format_citations` adds POST mode for large batches."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.2 — 2026-04-04
7
+
8
+ ## Fixed
9
+
10
+ - **ESummary date parsing**: Added dedicated `parseNcbiDate()` for NCBI's non-standard date formats (`YYYY Mon`, `YYYY Mon DD`, `YYYY Mon-Mon`, `YYYY`). chrono-node's `forwardDate` option was misinterpreting past months as future dates (e.g., "2018 Jun" resolved to a future June). The new parser handles all known NCBI formats as a fast path, falling back to chrono-node only for unrecognized strings.
11
+ - **`pubmed_search_articles`**: Search URL now uses `effectiveQuery` (post-filter) instead of raw `input.query`, so the PubMed link matches the actual search executed
12
+ - **`pubmed_fetch_fulltext`**: Removed try/catch that silently swallowed eFetch errors and returned empty results — errors now propagate per the "handlers throw" convention
13
+ - **`pubmed_format_citations`**: Added explicit `retmode: 'xml'` and POST mode for batches >= 25 PMIDs
14
+
15
+ ## Changed
16
+
17
+ - **`pubmed_fetch_articles`**: Lowered POST threshold from > 200 to >= 100 PMIDs for more reliable large batch requests
18
+
19
+ ## Added
20
+
21
+ - **Test coverage**: Comprehensive `parseNcbiDate` unit tests covering all 12 months, year-only, month ranges (dash/slash separators), whitespace handling, and rejection of invalid formats. Integration tests through `standardizeESummaryDate` and `extractBriefSummaries`. Optional live NCBI API integration tests (`NCBI_INTEGRATION=1`).
22
+
23
+ ## Updated
24
+
25
+ - `@cyanheads/mcp-ts-core` to ^0.2.12
26
+ - `fast-xml-parser` to ^5.5.10
27
+ - `@types/node` to ^25.5.2
@@ -0,0 +1,38 @@
1
+ ---
2
+ summary: "Output enrichments — `pubmed_search_articles` adds `effectiveQuery` and `appliedFilters`, `pubmed_lookup_citation` adds per-citation `status`, `pubmed_format_citations` adds partial-result counters."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.3 — 2026-04-09
7
+
8
+ ## Added
9
+
10
+ - **`pubmed_search_articles`**: Returned `effectiveQuery` and normalized `appliedFilters` metadata so clients can inspect the exact filters sent to PubMed
11
+ - **`pubmed_lookup_citation`**: Returned per-citation `status` (`matched`, `not_found`, `ambiguous`) and ECitMatch detail for non-exact outcomes
12
+ - **`pubmed_format_citations`**: Returned `totalSubmitted`, `totalFormatted`, and `unavailablePmids` for partial-result handling
13
+ - **Skill**: Added the `code-simplifier` agent skill for cleanup/refinement passes after edits
14
+ - **Test coverage**: Added `tests/index.test.ts` for `createApp()` registration/setup and expanded tool/service tests for search, citation lookup, citation formatting, related articles, and fulltext flows, including regression coverage for normalized `appliedFilters` output and summary clamping in `pubmed_search_articles`
15
+
16
+ ## Fixed
17
+
18
+ - **`pubmed_search_articles`**: History-backed summary fetches now clamp to the returned PMID page, and `appliedFilters` now reports the normalized/sanitized values actually sent to PubMed
19
+
20
+ ## Changed
21
+
22
+ - **`pubmed_search_articles`**: Format output now shows the effective query and a normalized Applied Filters section
23
+ - **`pubmed_lookup_citation`**: Format output now gives next-step guidance for ambiguous and unmatched citations instead of a flat status table
24
+
25
+ ## Updated
26
+
27
+ - `@cyanheads/mcp-ts-core` to ^0.3.4
28
+ - `fast-xml-parser` to ^5.5.11
29
+ - `vitest` to ^4.1.4
30
+ - Added `@vitest/coverage-istanbul` for coverage support
31
+
32
+ ## Security
33
+
34
+ - Added/raised overrides for `@hono/node-server` (>=1.19.13), `hono` (>=4.12.12), and `vite` (>=8.0.8)
35
+
36
+ ## Docs
37
+
38
+ - Marked `docs/design.md` as historical and regenerated `docs/tree.md` for the current project layout
@@ -0,0 +1,21 @@
1
+ ---
2
+ summary: "HTTP 429 now classified as `RateLimited` and retried; default `maxRetries` raised 3 → 6 with 30s backoff cap and ±25% jitter."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.4 — 2026-04-12
7
+
8
+ ## Fixed
9
+
10
+ - **HTTP 429 classification**: NCBI rate-limit responses (HTTP 429) were misclassified as `InvalidRequest` and failed immediately without retrying. Now correctly classified as `RateLimited`.
11
+ - **Retry resilience**: `RateLimited` errors are now included in the retryable error set alongside `ServiceUnavailable` and `Timeout`.
12
+
13
+ ## Changed
14
+
15
+ - **Retry defaults**: Increased default `maxRetries` from 3 to 6, extending the retry window from ~7s to ~45-75s before giving up.
16
+ - **Backoff strategy**: Added 30s cap on exponential backoff (prevents explosion at high retry counts) and ±25% jitter (prevents thundering herd on concurrent retries).
17
+
18
+ ## Updated
19
+
20
+ - `@biomejs/biome` to ^2.4.11
21
+ - `@types/node` to ^25.6.0
@@ -0,0 +1,24 @@
1
+ ---
2
+ summary: "XML handling — raised entity expansion ceiling, preserved diacritics, wrapped parser failures as `SerializationError`. Retry tightened: only transient `McpError` retries. `fast-xml-parser` ^5.5.12."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.5 — 2026-04-13
7
+
8
+ ## Fixed
9
+
10
+ - **XML response handling**: Raised the numeric entity expansion ceiling for trusted NCBI XML, preserved decoded punctuation/diacritics in parsed metadata, and wrapped parser failures as `SerializationError`.
11
+ - **Retry behavior**: Stopped retrying unexpected plain errors in `NcbiService`; only transient `McpError` responses are retried now.
12
+
13
+ ## Added
14
+
15
+ - **Regression coverage**: Added end-to-end and unit tests for Unicode metadata, en-dash page ranges, parser failure wrapping, and entity-heavy XML payloads.
16
+
17
+ ## Updated
18
+
19
+ - `@cyanheads/mcp-ts-core` to ^0.3.5
20
+ - `fast-xml-parser` to ^5.5.12
21
+
22
+ ## Docs
23
+
24
+ - Updated the `design-mcp-server` and `add-test` skills for MCP Apps planning guidance and default test layout guidance.
@@ -0,0 +1,26 @@
1
+ ---
2
+ summary: "Maintenance — five deps updated, `overrides` block removed (all nine pinned transitives patched upstream), tool description strings collapsed to single-paragraph convention."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.6 — 2026-04-19
7
+
8
+ ## Updated
9
+
10
+ - `@cyanheads/mcp-ts-core` to ^0.3.7
11
+ - `fast-xml-parser` to ^5.7.1
12
+ - `sanitize-html` to ^2.17.3
13
+ - `@biomejs/biome` to ^2.4.12
14
+ - `typescript` to ^6.0.3
15
+
16
+ ## Removed
17
+
18
+ - **Dependency overrides**: Removed the `overrides` block from `package.json`. All nine pinned transitive deps (`hono`, `@hono/node-server`, `brace-expansion`, `express-rate-limit`, `path-to-regexp`, `picomatch`, `vite`, `yaml`, `lodash`) have since shipped patched versions upstream, making the overrides dead weight. `bun audit` remains clean.
19
+
20
+ ## Changed
21
+
22
+ - **Tool descriptions**: Collapsed multi-line `+` string concatenation in `pubmed_search_articles`, `pubmed_fetch_fulltext`, and `pubmed_convert_ids` to single strings, aligning with the project's description convention and the updated `add-tool` / `design-mcp-server` skill guidance (single cohesive paragraph, no structural noise).
23
+
24
+ ## Docs
25
+
26
+ - Synced `add-tool` (v1.4) and `design-mcp-server` (v2.3) skills from the framework — both now emphasize single-paragraph tool descriptions over bullet lists or blank-line-separated sections.
@@ -0,0 +1,31 @@
1
+ ---
2
+ summary: "Citation formatter fixes — APA collective-author period, RIS page expansion, BibTeX double-period; adds pub-type mapping, ISSN, PMC URL, MeSH keywords (closes [#15](https://github.com/cyanheads/pubmed-mcp-server/issues/15))."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.7 — 2026-04-20
7
+
8
+ ## Fixed
9
+
10
+ - **APA — missing period before year with collective authors** (`citation-formatter.ts`): `formatApa` now coerces a trailing period on the author block. Individual author initials already end with `.`, but collective names (e.g., `ATLAS Collaboration`, `KEYNOTE-024 Investigators`, `ACTT-1 Study Group Members`) did not, producing `Name (Year).` instead of the APA 7 §9.8-compliant `Name. (Year).`. Fix mirrors the `endsWith('.')` idiom already used in `formatMla`.
11
+ - **RIS — truncated-end page ranges emitted as absolute pages** (`splitPages`): `737-8` now expands to `SP 737 / EP 738`, `1639-41` to `SP 1639 / EP 1641`, etc. PubMed uses a truncated-end convention for page ranges; downstream RIS importers (Zotero, EndNote, Mendeley) treat `EP` as an absolute page number, so the unexpanded form rendered wrong page ranges in compiled bibliographies.
12
+ - **BibTeX — trailing period retained inside `title = {...}`** (`formatBibtex`): titles ending with `.` are now stripped before emission. biblatex styles append their own terminal period, so the prior behavior produced `...Final Report..` (double period) in compiled bibliographies. Mirrors the existing APA/MLA title handling.
13
+
14
+ ## Changed
15
+
16
+ - **MLA `p.` vs `pp.`** (`formatMla`): single-page citations now use `p.`, page ranges continue to use `pp.`, per MLA 9 §6.56.
17
+ - **RIS abstract whitespace** (`formatRis`): structured-abstract newlines (`BACKGROUND:\n\nMETHODS:\n\n...`) are collapsed to single spaces before emission. Strict RIS parsers treat blank lines as record terminators, so the prior output could truncate records at the first `\n\n` boundary.
18
+ - **`getYear` fallback** (`citation-formatter.ts`): falls back to `articleDates` (typically the electronic pub date) when `journalInfo.publicationDate.year` is absent, instead of emitting `n.d.` prematurely.
19
+
20
+ ## Added
21
+
22
+ - **Publication type → entry/reference type mapping**: `publicationTypes` now drives BibTeX entry types (`@book`, `@inbook`, `@misc`) and RIS `TY` codes (`BOOK`, `CHAP`, `GEN`) for `Book`, `Book Chapter`, and `Preprint`. Unmapped types fall back to `@article` / `TY - JOUR`.
23
+ - **RIS `SN` (ISSN) tag**: `journalInfo.issn` (with `eIssn` fallback) now emitted in RIS records.
24
+ - **BibTeX `issn`, `pmcid` fields**: surfaced from parsed metadata when present.
25
+ - **PMC URL in RIS**: second `UR` tag emitted when `pmcId` is present (`https://pmc.ncbi.nlm.nih.gov/articles/PMC.../`).
26
+ - **Merged keywords + MeSH**: RIS `KW` tags and BibTeX `keywords` now include MeSH descriptor names alongside article keywords, deduplicated.
27
+ - **Test coverage**: 11 new test cases covering the three bug fixes, MLA `p.`/`pp.` branching, abstract whitespace normalization, pub-type mapping, ISSN, PMC URL, MeSH merging, and `articleDates` year fallback.
28
+
29
+ ## References
30
+
31
+ - Closes [#15](https://github.com/cyanheads/pubmed-mcp-server/issues/15) — field-testing report identifying the three APA/RIS/BibTeX correctness issues.
@@ -0,0 +1,19 @@
1
+ ---
2
+ summary: "`pubmed_fetch_fulltext` PMID→PMCID resolution switched from eLink to PMC ID Converter (closes [#16](https://github.com/cyanheads/pubmed-mcp-server/issues/16)); `@cyanheads/mcp-ts-core` 0.3.7 → 0.4.1."
3
+ breaking: false
4
+ ---
5
+
6
+ # 2.3.8 — 2026-04-20
7
+
8
+ ## Fixed
9
+
10
+ - **`pubmed_fetch_fulltext` — PMID→PMCID resolution via PMC ID Converter instead of eLink** (`fetch-fulltext.tool.ts`): `resolvePmidsToPmcIds` now calls `NcbiService.idConvert()` — the purpose-built DOI/PMID/PMCID mapping endpoint — rather than `eLink(cmd=neighbor, linkname=pubmed_pmc)`. Triggered by a sustained NCBI outage on 2026-04-20 where eLink's `exLinkSrv2` backend returned `Couldn't resolve #exLinkSrv2, the address table is empty.` for every request, breaking all fulltext calls; the ID Converter runs on a different backend and stayed up throughout. Equivalent coverage (both require the article be in PMC), batch-friendly (up to 200 IDs/request vs. the tool's 10 cap), and drops ~30 lines of ELink XML type shims.
11
+
12
+ ## Changed
13
+
14
+ - **Dependency updates**: `@cyanheads/mcp-ts-core` 0.3.7 → 0.4.1, picking up OTel prompt telemetry (0.4.1), Vitest 4 `projects` testing helpers (0.4.0), and the duplicate `"Error:"` prefix fix (0.3.8). No handler-facing API changes.
15
+ - **Skill sync**: `skills/api-utils` refreshed from the package — adds `withRetry` options reference and partial-success batch metric documentation.
16
+
17
+ ## References
18
+
19
+ - Closes [#16](https://github.com/cyanheads/pubmed-mcp-server/issues/16) — feature request to swap the fulltext resolution path off eLink, filed after the 2026-04-20 outage.