@cyanheads/pubmed-mcp-server 2.10.13 → 2.10.15
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.
- package/AGENTS.md +24 -6
- package/CLAUDE.md +24 -6
- package/README.md +7 -7
- package/changelog/2.0.x/2.0.0.md +32 -0
- package/changelog/2.0.x/2.0.1.md +32 -0
- package/changelog/2.1.x/2.1.0.md +17 -0
- package/changelog/2.1.x/2.1.1.md +29 -0
- package/changelog/2.1.x/2.1.2.md +18 -0
- package/changelog/2.1.x/2.1.3.md +10 -0
- package/changelog/2.1.x/2.1.4.md +12 -0
- package/changelog/2.1.x/2.1.5.md +18 -0
- package/changelog/2.1.x/2.1.6.md +15 -0
- package/changelog/2.10.x/2.10.0.md +17 -0
- package/changelog/2.10.x/2.10.1.md +14 -0
- package/changelog/2.10.x/2.10.10.md +22 -0
- package/changelog/2.10.x/2.10.11.md +21 -0
- package/changelog/2.10.x/2.10.12.md +14 -0
- package/changelog/2.10.x/2.10.13.md +28 -0
- package/changelog/2.10.x/2.10.14.md +29 -0
- package/changelog/2.10.x/2.10.15.md +28 -0
- package/changelog/2.10.x/2.10.2.md +13 -0
- package/changelog/2.10.x/2.10.3.md +26 -0
- package/changelog/2.10.x/2.10.4.md +11 -0
- package/changelog/2.10.x/2.10.5.md +31 -0
- package/changelog/2.10.x/2.10.6.md +30 -0
- package/changelog/2.10.x/2.10.7.md +17 -0
- package/changelog/2.10.x/2.10.8.md +22 -0
- package/changelog/2.10.x/2.10.9.md +15 -0
- package/changelog/2.2.x/2.2.0.md +67 -0
- package/changelog/2.2.x/2.2.1.md +10 -0
- package/changelog/2.2.x/2.2.2.md +20 -0
- package/changelog/2.2.x/2.2.3.md +17 -0
- package/changelog/2.2.x/2.2.4.md +34 -0
- package/changelog/2.2.x/2.2.5.md +10 -0
- package/changelog/2.2.x/2.2.6.md +17 -0
- package/changelog/2.3.x/2.3.0.md +27 -0
- package/changelog/2.3.x/2.3.1.md +15 -0
- package/changelog/2.3.x/2.3.10.md +20 -0
- package/changelog/2.3.x/2.3.11.md +21 -0
- package/changelog/2.3.x/2.3.2.md +27 -0
- package/changelog/2.3.x/2.3.3.md +38 -0
- package/changelog/2.3.x/2.3.4.md +21 -0
- package/changelog/2.3.x/2.3.5.md +24 -0
- package/changelog/2.3.x/2.3.6.md +26 -0
- package/changelog/2.3.x/2.3.7.md +31 -0
- package/changelog/2.3.x/2.3.8.md +19 -0
- package/changelog/2.3.x/2.3.9.md +22 -0
- package/changelog/2.4.x/2.4.0.md +34 -0
- package/changelog/2.4.x/2.4.1.md +32 -0
- package/changelog/2.5.x/2.5.0.md +35 -0
- package/changelog/2.5.x/2.5.1.md +32 -0
- package/changelog/2.5.x/2.5.2.md +23 -0
- package/changelog/2.5.x/2.5.3.md +22 -0
- package/changelog/2.5.x/2.5.5.md +52 -0
- package/changelog/2.5.x/2.5.6.md +33 -0
- package/changelog/2.6.x/2.6.0.md +32 -0
- package/changelog/2.6.x/2.6.1.md +26 -0
- package/changelog/2.6.x/2.6.10.md +16 -0
- package/changelog/2.6.x/2.6.11.md +24 -0
- package/changelog/2.6.x/2.6.12.md +29 -0
- package/changelog/2.6.x/2.6.2.md +23 -0
- package/changelog/2.6.x/2.6.3.md +17 -0
- package/changelog/2.6.x/2.6.4.md +21 -0
- package/changelog/2.6.x/2.6.5.md +30 -0
- package/changelog/2.6.x/2.6.6.md +25 -0
- package/changelog/2.6.x/2.6.7.md +37 -0
- package/changelog/2.6.x/2.6.8.md +15 -0
- package/changelog/2.6.x/2.6.9.md +36 -0
- package/changelog/2.7.x/2.7.0.md +41 -0
- package/changelog/2.7.x/2.7.1.md +21 -0
- package/changelog/2.7.x/2.7.10.md +13 -0
- package/changelog/2.7.x/2.7.11.md +15 -0
- package/changelog/2.7.x/2.7.2.md +22 -0
- package/changelog/2.7.x/2.7.3.md +18 -0
- package/changelog/2.7.x/2.7.4.md +15 -0
- package/changelog/2.7.x/2.7.5.md +34 -0
- package/changelog/2.7.x/2.7.6.md +14 -0
- package/changelog/2.7.x/2.7.7.md +14 -0
- package/changelog/2.7.x/2.7.8.md +18 -0
- package/changelog/2.7.x/2.7.9.md +16 -0
- package/changelog/2.8.x/2.8.0.md +23 -0
- package/changelog/2.9.x/2.9.0.md +21 -0
- package/changelog/2.9.x/2.9.1.md +12 -0
- package/changelog/2.9.x/2.9.10.md +15 -0
- package/changelog/2.9.x/2.9.2.md +21 -0
- package/changelog/2.9.x/2.9.3.md +11 -0
- package/changelog/2.9.x/2.9.4.md +24 -0
- package/changelog/2.9.x/2.9.5.md +20 -0
- package/changelog/2.9.x/2.9.6.md +22 -0
- package/changelog/2.9.x/2.9.7.md +26 -0
- package/changelog/2.9.x/2.9.8.md +15 -0
- package/changelog/2.9.x/2.9.9.md +35 -0
- package/changelog/template.md +151 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +14 -5
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +0 -60
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.js +4 -4
- package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +6 -3
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +6 -3
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
- package/dist/services/error-contracts.d.ts +30 -13
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +30 -13
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/api-client.d.ts +14 -2
- package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
- package/dist/services/europe-pmc/api-client.js +30 -4
- package/dist/services/europe-pmc/api-client.js.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts +20 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.js +98 -50
- package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
- package/dist/services/ncbi/ncbi-service.d.ts +18 -26
- package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
- package/dist/services/ncbi/ncbi-service.js +111 -127
- package/dist/services/ncbi/ncbi-service.js.map +1 -1
- package/dist/services/ncbi/request-queue.d.ts +22 -30
- package/dist/services/ncbi/request-queue.d.ts.map +1 -1
- package/dist/services/ncbi/request-queue.js +29 -128
- package/dist/services/ncbi/request-queue.js.map +1 -1
- package/dist/services/ncbi/response-handler.d.ts +14 -1
- package/dist/services/ncbi/response-handler.d.ts.map +1 -1
- package/dist/services/ncbi/response-handler.js +65 -9
- package/dist/services/ncbi/response-handler.js.map +1 -1
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +3 -10
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/package.json +20 -11
- package/server.json +3 -3
- package/dist/services/retry-policy.d.ts +0 -18
- package/dist/services/retry-policy.d.ts.map +0 -1
- package/dist/services/retry-policy.js +0 -21
- package/dist/services/retry-policy.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "NCBI queue wait now counts toward `totalDeadlineMs`, so a slow upstream can't backlog tools into 10+ min latencies ([#50](https://github.com/cyanheads/pubmed-mcp-server/issues/50)). `pubmed_find_related` similar switches to `cmd=neighbor` to dodge NCBI's unstable scoring backend ([#53](https://github.com/cyanheads/pubmed-mcp-server/issues/53))."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.6.12 — 2026-05-16
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`NCBI_MAX_CONCURRENT` env var** — caps in-flight NCBI requests (default `8`, range 1–16). Decouples concurrency from `NCBI_REQUEST_DELAY_MS` so a slow upstream doesn't block new dispatches behind a single worker. Documented in `.env.example` and the README config table.
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- **`@cyanheads/mcp-ts-core` ^0.9.0 → ^0.9.1.**
|
|
16
|
+
- **`NcbiRequestQueue` rewritten as a token-bucket scheduler** (`src/services/ncbi/request-queue.ts`). Two independent ceilings: `minStartGapMs` enforces NCBI's per-second start-rate cap, `maxConcurrent` caps in-flight requests. `enqueue(task, endpoint, params, signal?)` now accepts an `AbortSignal` and rejects a still-waiting waiter immediately when its signal fires, splicing it out of the queue without dispatching.
|
|
17
|
+
- **`NCBI_REQUEST_DELAY_MS` description** refined in `.env.example` and README — names what it actually does ("minimum gap between request starts") instead of the older "delay between requests".
|
|
18
|
+
- **Dev deps.** `sanitize-html` ^2.17.3 → ^2.17.4, `@types/node` ^25.7.0 → ^25.8.0.
|
|
19
|
+
- **`skills/api-workers/SKILL.md`** 1.3 → 1.4 — documents the `instructions` resolver form `(env: CloudflareBindings) => string` so env-derived orientation text reaches the model inside `initializeApp(env)`, after `injectEnvVars()`.
|
|
20
|
+
|
|
21
|
+
## Fixed
|
|
22
|
+
|
|
23
|
+
- **Queue wait now counts toward `totalDeadlineMs`** ([#50](https://github.com/cyanheads/pubmed-mcp-server/issues/50)). `NcbiService.performRequest` and `idConvert` previously constructed `runWithDeadline` *inside* the enqueued task, so during NCBI eutils incidents a queued request could sit for minutes before its deadline started ticking — observed `pubmed_fetch_fulltext` and `pubmed_find_related` durations climbing in lockstep to 10+ minutes. `runWithDeadline` now wraps the enqueue, threading the combined deadline+caller signal into queue wait, retry backoff sleeps, and the HTTP fetch.
|
|
24
|
+
- **`pubmed_find_related` similar wedge on high-traffic PMIDs** ([#53](https://github.com/cyanheads/pubmed-mcp-server/issues/53)). NCBI's `cmd=neighbor_score` backend was returning a 200 OK with a `TXCLIENT::readAll EOF` C++ exception trace for popular source PMIDs (1, 12345, 33567185, 37952131), so the retry chain ran for ~60s before surfacing `-32003 ServiceUnavailable`. Switched to `cmd=neighbor` with explicit `linkname=pubmed_pubmed`, which returns the same neighbor list in NCBI's natural relevance order. Output schema drops the `score` field — it was an arbitrary-scale integer, so ordering is preserved without it.
|
|
25
|
+
- **`devcheck` outdated-packages parser** — `bun outdated` emits markdown-style rows (`| col1 | ... |`), so package names live at `split('|')[1]`, not `[0]`. The previous parser was matching the empty leading cell as the package name, so the allowlist always missed and every outdated dep surfaced as unexpected. Also strips the trailing `(dev|peer|prod|optional)` workspace-type marker so allowlist entries match the bare name.
|
|
26
|
+
|
|
27
|
+
## Tests
|
|
28
|
+
|
|
29
|
+
- 565 passed / 4 skipped. `bun run devcheck` clean. Regression coverage added for the deadline-during-queue-wait path (`tests/services/ncbi/ncbi-service.test.ts`) and the new scheduler's abort-during-wait, concurrency, FIFO, and gap-enforcement behavior (`tests/services/ncbi/request-queue.test.ts`).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Maintenance — `@cyanheads/mcp-ts-core` 0.7.0 → 0.7.5, `fast-xml-parser` 5.7.1 → 5.7.2. No runtime API changes for this server.
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2.6.2 — 2026-04-26
|
|
7
|
+
|
|
8
|
+
Drop-in dependency bumps. Field-tested against the live NCBI surface to confirm the v2.6.1 inline-markup flatten path still survives the parser update (PMID 38785209 abstract continues to render `1.73 m²`).
|
|
9
|
+
|
|
10
|
+
## Changed
|
|
11
|
+
|
|
12
|
+
- **`@cyanheads/mcp-ts-core` 0.7.0 → 0.7.5** — picks up HTTP Origin guard fail-closed default, landing-page bearer validation, raw-payload removal from default request-completion logs, `format-parity` numeric normalization, and a `describe-on-fields` linter exemption for `z.literal` union variants. None of the affected runtime surfaces are used here, so adoption is transparent. MCP SDK pinned at `^1.29.0`.
|
|
13
|
+
- **`fast-xml-parser` 5.7.1 → 5.7.2** — stack-overflow fix on very long tag expressions ([NaturalIntelligence/fast-xml-parser#817](https://github.com/NaturalIntelligence/fast-xml-parser/issues/817)). Marginal hardening for deeply nested PMC full-text articles.
|
|
14
|
+
|
|
15
|
+
## Synced
|
|
16
|
+
|
|
17
|
+
- **Skills** from the framework: `api-linter` 1.1 → 1.2 (canonical form-client safety pattern), `maintenance` 1.5 → 1.6 (Step 4 surfaces new/changed skills explicitly), content refreshes for `field-test` and `design-mcp-server` (description leak audits). Mirrored into `.agents/skills/` and `.claude/skills/`.
|
|
18
|
+
- **Scripts** from the framework: `scripts/devcheck.ts` (now wires `Framework Antipatterns` as a check step) and the new `scripts/check-framework-antipatterns.ts` (no consumer-side findings).
|
|
19
|
+
- **`CLAUDE.md` / `AGENTS.md`** — form-client safety bullet now names the union+literal alternative for cases where regex/length constraints matter enough to surface in JSON Schema.
|
|
20
|
+
|
|
21
|
+
## Tests
|
|
22
|
+
|
|
23
|
+
- **466 passed** / 4 skipped. No suite changes — the dependency bumps are drop-in.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Cross-field input violations on `pubmed_fetch_fulltext` and `pubmed_lookup_citation` now classify as `-32602` instead of `-32603` (closes [#46](https://github.com/cyanheads/pubmed-mcp-server/issues/46)).
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2.6.3 — 2026-04-26
|
|
7
|
+
|
|
8
|
+
Two tools were enforcing cross-field input constraints with imperative `throw new Error(...)` after schema parsing, so violations leaked as JSON-RPC `-32603 Internal error` (with a level-50 server log) instead of `-32602 Invalid params`. Production telemetry on `fetch_fulltext` showed 13 sessions hitting this in 30 minutes — real LLM callers were inferring incorrect call shapes from the schema.
|
|
9
|
+
|
|
10
|
+
## Fixed
|
|
11
|
+
|
|
12
|
+
- **`pubmed_fetch_fulltext` — XOR `pmcids` / `pmids` constraint moved into the Zod schema** ([#46](https://github.com/cyanheads/pubmed-mcp-server/issues/46)). The "exactly one of" check now lives in `.refine()` on the input object. Violations surface as `-32602 Input validation error` with the message ``Provide exactly one of `pmcids` or `pmids` (not both, not neither).`` Caveat: Zod `.refine()` doesn't serialize into JSON Schema, so the published `inputSchema` still shows both fields as independently optional — the constraint is communicated through the description prose and the runtime error.
|
|
13
|
+
- **`pubmed_lookup_citation` — same fix.** The "each citation must include at least one bibliographic field (journal, year, volume, firstPage, or authorName)" check moved from a per-item handler loop into a per-item `.refine()`. Bonus: Zod attaches `path: ["citations", N]` so callers learn which array index failed.
|
|
14
|
+
|
|
15
|
+
## Tests
|
|
16
|
+
|
|
17
|
+
- **462 passed** / 4 skipped. Two tests rewritten to assert on `input.safeParse()` failure rather than handler rejection, matching where validation now runs.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Description audit follow-up to v2.6.3 — cross-field constraints surface in tool descriptions, and `pubmed_format_citations` no longer throws on zero matches.
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2.6.4 — 2026-04-26
|
|
7
|
+
|
|
8
|
+
Follow-up to v2.6.3's classification fix. Tool descriptions now declare their cross-field input contracts inline so callers don't learn them from a rejection, and one more imperative throw was lifted into a structured empty result.
|
|
9
|
+
|
|
10
|
+
## Fixed
|
|
11
|
+
|
|
12
|
+
- **`pubmed_format_citations` no longer throws when EFetch returns no articles** ([#46](https://github.com/cyanheads/pubmed-mcp-server/issues/46) class). Returns `{ citations: [], totalFormatted: 0, totalSubmitted: N, unavailablePmids }` instead of raising `-32603`. `format()` appends a one-line pointer to `pubmed_search_articles` / `pubmed_spell_check` when the result is empty.
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **Top-level descriptions on `pubmed_fetch_fulltext` and `pubmed_lookup_citation` now state the cross-field contract.** The runtime `.refine()` from v2.6.3 catches violations either way; this surfaces the rule upfront. `fetch_fulltext` declares the `pmcids` / `pmids` XOR; `lookup_citation` requires at least one bibliographic field per citation.
|
|
17
|
+
- **Field-level `.describe()` audit across 5 tools.** Removes implementation leaks (`pubmed_lookup_citation.candidatePmids` no longer mentions "raw `AMBIGUOUS` CSV"), drops meta-coaching ("downstream code should key on…"), and documents under-specified semantics: `pubmed_convert_ids` outputs (`pmid` / `pmcid` / `doi` / `errmsg`) call out the absent-on-failure / presence-as-signal contract; `pubmed_search_articles.dateRange` documents the form-client empty-string-disables pattern; `pubmed_fetch_fulltext.unavailablePmcIds` corrected to cover both direct-request and PMID-resolved failures.
|
|
18
|
+
|
|
19
|
+
## Tests
|
|
20
|
+
|
|
21
|
+
- **462 passed** / 4 skipped. `format-citations.tool.test.ts`: `throws when no articles found` → `returns structured empty result when no articles match`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Typed error contracts on NCBI tools, service-layer logger metadata typed end-to-end, fuzz coverage for all 9 tools, plus DX renames on `pubmed_format_citations` (`styles` → `format`) and `pubmed_lookup_mesh` (`term` → `query`).
|
|
3
|
+
breaking: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2.6.5 — 2026-04-28
|
|
7
|
+
|
|
8
|
+
Three issues land together: typed error contracts on the NCBI-driven tools, the last 23 `as never` logger casts swept out of the service layer, and property-based fuzz coverage for every tool. Two ergonomic input renames ship alongside — `pubmed_format_citations` and `pubmed_lookup_mesh` callers need to update their argument names.
|
|
9
|
+
|
|
10
|
+
## Added
|
|
11
|
+
|
|
12
|
+
- **Typed error contracts on NCBI tools** ([#47](https://github.com/cyanheads/pubmed-mcp-server/issues/47)). New `src/mcp-server/tools/definitions/_error-contracts.ts` exports a shared `NCBI_SERVICE_ERRORS` tuple (`queue_full` / `ncbi_unreachable` / `ncbi_deadline_exceeded`). `pubmed_fetch_articles`, `pubmed_fetch_fulltext`, and `pubmed_find_related` now declare the baseline plus tool-specific reasons (`invalid_efetch_response`, `invalid_pmc_efetch_response`, `elink_error`) and route their failures through `ctx.fail(reason, …)`. Every contract is published in `tools/list` under `_meta["mcp-ts-core/errors"]`.
|
|
13
|
+
- **Service-layer reason tagging.** `request-queue.ts` stamps `reason: 'queue_full'` on overflow; `ncbi-service.ts` tags `ncbi_deadline_exceeded` on `runWithDeadline()` timeouts and `ncbi_unreachable` when retries-exhausted hits `ServiceUnavailable`. Wire payloads carry the same `error.data.reason` regardless of throw site, so callers can switch on a stable key.
|
|
14
|
+
- **Property-based fuzz coverage for all 9 tools** ([#49](https://github.com/cyanheads/pubmed-mcp-server/issues/49)). New `tests/mcp-server/tools/definitions/tools.fuzz.test.ts` uses a custom `fuzzToolStrict()` runner (in `_fuzz-helpers.ts`) that pre-parses generated inputs through each tool's Zod schema before invoking the handler — defaults resolve, `min(1)` constraints honor, and the framework's input-parsing gap is worked around locally. Pinned seed (`42`), `numRuns: 50`, `numAdversarial: 30`. Suite finishes in ~2.5s. Filed upstream as [cyanheads/mcp-ts-core#83](https://github.com/cyanheads/mcp-ts-core/issues/83).
|
|
15
|
+
- **`pubmed_lookup_mesh` recovery notice.** When no MeSH descriptors match, the tool now returns a `notice` field pointing the caller at `pubmed_spell_check` and `pubmed_search_articles` for free-text discovery. Rendered as a blockquote in `format()`.
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **`pubmed_format_citations` input renamed `styles` → `format`** ⚠️ **breaking**. Now accepts a single style as a string or multiple styles as an array (`format: 'apa'` or `format: ['apa', 'mla']`); default is `'apa'`. Empty arrays are rejected.
|
|
20
|
+
- **`pubmed_lookup_mesh` input renamed `term` → `query`** ⚠️ **breaking**. The output also renames `term` → `query` for parity. Description and field text reworded to clarify it accepts both descriptor names and free-text.
|
|
21
|
+
- **Replaced 23 `as never` casts** across 5 service files with proper `requestContextService.createRequestContext({ operation, ...meta })` calls ([#48](https://github.com/cyanheads/pubmed-mcp-server/issues/48)). Logger metadata is now type-checked against `RequestContext` end-to-end. Each call gets a domain-tagged `operation` field (`NcbiHttpRequest`, `NcbiQueueWait`, `NcbiXmlParseError`, etc.) so log lines self-identify their origin.
|
|
22
|
+
- **NCBI / Unpaywall HTTP error mapping switched to `httpErrorFromResponse()`** in `api-client.ts` and `unpaywall-service.ts`. Replaces hand-rolled 4xx/5xx → `JsonRpcErrorCode` ladders; picks up the full status table and `Retry-After` header capture. Hand-rolled `new McpError(...)` throws normalized to `serviceUnavailable()` / `serializationError()` / `internalError()` / `timeout()` factories with `cause:` plumbed through.
|
|
23
|
+
- **`pubmed_find_related` ELink error path** now uses `ctx.fail('elink_error', …)` and preserves the raw NCBI ERROR payload in `data.ncbiError` instead of stringifying it into the message.
|
|
24
|
+
- **Skill / agent-protocol sync.** `CLAUDE.md` and `AGENTS.md` updated to lead with the typed error contract pattern and reference the `add-app-tool` skill. Local skill copies (`skills/`, `.agents/skills/`, `.claude/skills/`) re-synced from upstream `mcp-ts-core` so `add-tool`, `add-service`, `api-errors`, `field-test`, `maintenance`, `release-and-publish`, `report-issue-framework`, `security-pass`, and `setup` carry the latest framework guidance.
|
|
25
|
+
- **`research-plan` prompt** description for `includeAgentPrompts` reworded to drop consumer-aware phrasing.
|
|
26
|
+
|
|
27
|
+
## Tests
|
|
28
|
+
|
|
29
|
+
- **476 passed** / 4 skipped. Added: 9 fuzz cases (one per tool), 4 contract-reason assertions on `fetch-articles` / `fetch-fulltext` / `find-related`, 5 `pubmed_format_citations` input-shape cases (single/array/empty/unknown), 3 `pubmed_lookup_mesh` notice cases. Service tests extended their `@cyanheads/mcp-ts-core/utils` mocks to stub `requestContextService.createRequestContext`.
|
|
30
|
+
- Field-tested against the live HTTP server: every contract surfaces under `_meta["mcp-ts-core/errors"]` with correct `code` / `reason` / `when` / `retryable`. Happy paths verified for `spell_check`, `search_articles`, `fetch_articles`, `find_related`. Input validation returns clean `-32602` with Zod issue paths.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: Recovery hints on every error contract; `pubmed_convert_ids` input renamed `idtype` → `idType` for camelCase parity; adopts `@cyanheads/mcp-ts-core` v0.8.6.
|
|
3
|
+
breaking: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2.6.6 — 2026-04-29
|
|
7
|
+
|
|
8
|
+
Polish patch on top of v2.6.5. Every error contract now declares a `recovery` hint that the framework mirrors into `content[]` text via `data.recovery.hint`, so callers see actionable next steps inline with the error message. Hints are LLM-targeted — each carries at least one concrete second action when the obvious "retry" doesn't recover.
|
|
9
|
+
|
|
10
|
+
## Changed
|
|
11
|
+
|
|
12
|
+
- **Recovery hints on every error contract.** `_error-contracts.ts` (`queue_full`, `ncbi_unreachable`, `ncbi_deadline_exceeded`) plus the three tool-specific entries (`invalid_efetch_response`, `invalid_pmc_efetch_response`, `elink_error`) all carry `recovery: '...'`. Throw sites in `pubmed_fetch_articles`, `pubmed_fetch_fulltext`, and `pubmed_find_related` spread `ctx.recoveryFor('reason')` into `data` so the framework mirrors `data.recovery.hint` into the rendered text. Hints rewritten for an LLM caller — no "report the issue" or "check status" filler; each names a concrete next action.
|
|
13
|
+
- **`pubmed_convert_ids` input renamed `idtype` → `idType`** ⚠️ **breaking**. CamelCase parity with the rest of the input surface. The tool itself only shipped in v2.6.5 a day prior; the service-layer parameter stays lowercase to match NCBI's API verbatim.
|
|
14
|
+
- **`pubmed_format_citations` format union** describes both inner variants (single-style and array-of-styles) so `inputSchema.properties.format.anyOf[*].description` carries field-level guidance instead of relying solely on the parent `.describe()`. Conforms to the `describe-on-fields` linter rule for non-literal union members.
|
|
15
|
+
- **`@cyanheads/mcp-ts-core` 0.8.2 → 0.8.6**: typed `ctx.recoveryFor` resolver landed in 0.8.4–0.8.5, `_meta['mcp-ts-core/errors']` wire publication was dropped in 0.8.3 (replaced by `structuredContent.error` parity), and `dev:stdio` / `dev:http` watch scripts were removed framework-wide in 0.8.6. Smoke-test path standardized to `bun run rebuild && bun run start:stdio`.
|
|
16
|
+
- **`unpdf` 1.6.1 → 1.6.2.**
|
|
17
|
+
- **Skill / agent-protocol sync.** `AGENTS.md` and `CLAUDE.md` document `ctx.recoveryFor` on the Context table and rework the error-contract example to spread the resolver into `data`. Local skill copies re-synced from upstream `mcp-ts-core` (`add-tool`, `add-service`, `api-context`, `api-errors`, `design-mcp-server`, `field-test`).
|
|
18
|
+
|
|
19
|
+
## Removed
|
|
20
|
+
|
|
21
|
+
- **`dev:stdio` / `dev:http` scripts** from `package.json`. Use `bun run rebuild && bun run start:stdio` (or `start:http`) for the same execution path as production smoke tests.
|
|
22
|
+
|
|
23
|
+
## Tests
|
|
24
|
+
|
|
25
|
+
- 476 passed / 4 skipped. `convert-ids.tool.test.ts` updated for the `idType` rename. Field-tested against the live HTTP server: schema correctly advertises `idType`, the old `idtype` is rejected with a clean validation error referencing the new field, both single-string and array-of-styles forms of `pubmed_format_citations` work, and the `unpdf` bump survives the live PDF-extraction path used by the Unpaywall fallback.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Adopts `@cyanheads/mcp-ts-core` 0.8.6 → 0.8.19 — HTTP SSE per-request leak fix, OTel double-write fix, `ctx.sessionId` / `ctx.auth.token`. Node engine ≥24.0.0; Dockerfile pinned to `oven/bun:1.3`."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.6.7 — 2026-05-09
|
|
8
|
+
|
|
9
|
+
Maintenance release rolling up 13 framework patch versions plus a dep refresh. No tool, resource, or prompt API changes — handlers, schemas, and wire formats are byte-identical to v2.6.6.
|
|
10
|
+
|
|
11
|
+
## Changed
|
|
12
|
+
|
|
13
|
+
- **`@cyanheads/mcp-ts-core` ^0.8.6 → ^0.8.19.** Notable picks for this server's deployment shape:
|
|
14
|
+
- **HTTP SSE per-request retention leak** ([mcp-ts-core#50](https://github.com/cyanheads/mcp-ts-core/issues/50), 0.8.16) — per-request `McpServer` + `McpSessionTransport` pairs survived ungraceful client disconnects on SSE GET streams (~200 KB × GET-rate heap growth across 12h+ production telemetry). Cleanup now binds to the request `AbortSignal`, with failures tagged `trigger=sse-abort` on `mcp.http.close_failures`.
|
|
15
|
+
- **`ErrorHandler` OTel double-write fix** ([mcp-ts-core#93](https://github.com/cyanheads/mcp-ts-core/issues/93), 0.8.8) — eliminates two `Cannot execute the operation on ended Span` warnings per tool failure under HTTP+OTel.
|
|
16
|
+
- **Storage `decodeCursor` stack-trace leak** (0.8.8) — malformed cursors no longer surface server stacks through `McpError.data`.
|
|
17
|
+
- **`ctx.sessionId`** ([mcp-ts-core#116](https://github.com/cyanheads/mcp-ts-core/issues/116), 0.8.17) — HTTP handlers can now read the `Mcp-Session-Id` header off the public `Context` for session-scoped state. Fail-closed under stateless HTTP; opt in via `createApp({ context: { exposeStatelessSessionId: true } })`. Not consumed by this server yet; available for future session-keyed features.
|
|
18
|
+
- **`ctx.auth.token`** ([mcp-ts-core#121](https://github.com/cyanheads/mcp-ts-core/issues/121), 0.8.18) — `toAuthContext` no longer strips `info.token`, so PAT pass-through / on-behalf-of patterns have a public path to the validated bearer.
|
|
19
|
+
- **`disabledTool()` wrapper + landing-page `unspecified` mutability bucket** ([mcp-ts-core#92](https://github.com/cyanheads/mcp-ts-core/issues/92), 0.8.11) — feature-gated tools and unannotated tools are now first-class concepts; this server's nine tools all carry explicit `readOnlyHint: true` so the bucket change is a no-op here.
|
|
20
|
+
- **Telemetry visualization docs** ([mcp-ts-core#125](https://github.com/cyanheads/mcp-ts-core/issues/125), 0.8.19) — example Grafana dashboard JSON and vendor-agnostic query recipes ship with the framework, plus the new `api-telemetry` skill.
|
|
21
|
+
- **Node engine: `>=22.0.0` → `>=24.0.0`** (matches framework 0.8.19's engine bump). `Dockerfile` pinned to `oven/bun:1.3` / `oven/bun:1.3-slim` (was floating `:1` / `:1-slim`) so production images track the same Bun line we test against.
|
|
22
|
+
- **Local skill mirror.** Adds three skills synced from upstream `mcp-ts-core`: `api-canvas` (Tier 3 SQL workspace; not used here), `api-telemetry` (OTel catalog), `tool-defs-analysis` (read-only audit of definition language). Existing skills resynced from upstream.
|
|
23
|
+
- **Changelog frontmatter.** Adds optional `security: bool` flag rendered as a 🛡️ Security badge in the rollup. `scripts/build-changelog.ts` parses the new key; `changelog/template.md` rewritten as an authoring guide. One-shot `scripts/split-changelog.ts` retained for reference.
|
|
24
|
+
|
|
25
|
+
### Dependency bumps
|
|
26
|
+
|
|
27
|
+
| Package | From | To |
|
|
28
|
+
|:---|:---|:---|
|
|
29
|
+
| `@cyanheads/mcp-ts-core` | `^0.8.6` | `^0.8.19` |
|
|
30
|
+
| `fast-xml-parser` | `^5.7.2` | `^5.7.3` |
|
|
31
|
+
| `@biomejs/biome` (dev) | `^2.4.13` | `^2.4.14` |
|
|
32
|
+
| `@types/node` (dev) | `^25.6.0` | `^25.6.2` |
|
|
33
|
+
| `tsc-alias` (dev) | `^1.8.16` | `^1.8.17` |
|
|
34
|
+
|
|
35
|
+
## Tests
|
|
36
|
+
|
|
37
|
+
- 476 passed / 4 skipped. No suite changes — handlers, schemas, and outputs are unchanged. `bun run devcheck` clean.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Fix `server.json` top-level `version` field missed in v2.6.7 — kept the registry stuck at 2.6.6 even though the two `packages[*].version` entries advanced. No code changes."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.6.8 — 2026-05-09
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
- **`server.json` top-level `version`** — the v2.6.7 release bumped both `packages[*].version` entries but missed the top-level field, so `mcp-publisher` rejected the publish with `cannot publish duplicate version` and the MCP Registry stayed pinned to 2.6.6. v2.6.7 still ships on npm; the MCP Registry skips straight from 2.6.6 → 2.6.8.
|
|
12
|
+
|
|
13
|
+
## Tests
|
|
14
|
+
|
|
15
|
+
- 476 passed / 4 skipped. No code changes.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Service-layer error-contracts module with `recoveryFor()` helper; `pubmed_convert_ids` 400-leak rewrite; `pubmed_find_related` ELink `<ERROR>` folded into ESummary disambiguation. Adopts `@cyanheads/mcp-ts-core` 0.8.20."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.6.9 — 2026-05-10
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`src/services/error-contracts.ts`** — single source of truth for the failure modes both services throw and tools declare in `errors[]`. Exports `NCBI_SERVICE_ERRORS` (queue_full, ncbi_unreachable, ncbi_deadline_exceeded, ncbi_invalid_response, ncbi_resource_not_found), `UNPAYWALL_SERVICE_ERRORS` (unpaywall_unreachable), and a `recoveryFor(reason)` service-layer counterpart to `ctx.recoveryFor` so service throws carry the same actionable hint a tool-level `ctx.fail` would.
|
|
12
|
+
- **`pubmed_fetch_fulltext`** — `format()` now appends a notice when zero articles are returned, pointing to `pubmed_fetch_articles` for metadata-only paths and noting the open-access requirement.
|
|
13
|
+
- **Service tests.** ~600 lines added across `error-contracts`, `api-client`, `request-queue`, `response-handler`, `ncbi-service`, `pmc-article-parser`, `citation-formatter`, and `unpaywall-service` covering the new reason/recovery wiring, NotFound short-circuit patterns, HTML-rate-limit detection, and Unicode initials.
|
|
14
|
+
|
|
15
|
+
## Changed
|
|
16
|
+
|
|
17
|
+
- **Error-contracts module relocation.** `src/mcp-server/tools/definitions/_error-contracts.ts` → `src/services/error-contracts.ts`. The old file held three NCBI reasons used only by tool definitions; the service layer is now the authoring site so handler-side and service-side throws share one declaration. Two new NCBI reasons added (`ncbi_invalid_response`, `ncbi_resource_not_found`) and Unpaywall lifted to its own array.
|
|
18
|
+
- **All 9 tools declare `errors: [...NCBI_SERVICE_ERRORS]`** (and `[...UNPAYWALL_SERVICE_ERRORS]` on `pubmed_fetch_fulltext`). The six previously-uncontracted tools (`spell_check`, `lookup_mesh`, `search_articles`, `convert_ids`, `format_citations`, `lookup_citation`) now expose the baseline failure surface to the LLM.
|
|
19
|
+
- **NCBI service throws stamp `data.reason` + `data.recovery.hint`** at every site — `api-client` (transport), `request-queue` (queue full), `response-handler` (XML/JSON parse, structured `<ERROR>` payloads, HTML-rate-limit detection), `ncbi-service` (deadline, retries, ID Converter parse). Wire payloads now carry the same actionable recovery hint clients see from handler-level `ctx.fail`.
|
|
20
|
+
- **`pubmed_convert_ids`** — PMC ID Converter `400 Bad Request` now rewrites to a typed `validationError` with idType-specific format hints (`pmid: numeric digits`, `pmcid: "PMC" + digits`, `doi: starts with "10."`). Drops the leaky upstream HTML/text body that previously surfaced in `data.body`.
|
|
21
|
+
- **`pubmed_find_related`** — ELink `<ERROR>` payload no longer throws. The branch now falls through to the existing empty-LinkSet ESummary disambiguation path, producing the same `{ articles: [], totalFound: 0, notice }` shape for both invalid PMIDs and valid-but-empty PMIDs. Drops `elink_error` from the contract; the previous `ServiceUnavailable` classification was wrong (the response is user-recoverable, not a service outage).
|
|
22
|
+
- **`pubmed_fetch_fulltext`** description tightened — drops the `UNPAYWALL_EMAIL` setup mention in favor of the configured behavior. `pubmed_lookup_citation` description leads with "deterministic citation matching" framing.
|
|
23
|
+
- **`response-handler.ts`** — NCBI errors matching `cannot get document summary`, `UID=…: not found`, or `Empty id list` now throw `notFound()` (`reason: ncbi_resource_not_found`) instead of `serviceUnavailable()`, so the retry loop short-circuits on permanent "no such record" responses. HTML-instead-of-XML detection moved before the XML validator so rate-limit pages don't pollute the parse-error path.
|
|
24
|
+
- **`citation-formatter.ts`** — author initials extraction is now Unicode-aware (`/[^\p{L}]/u`) so accented forms like `Á`, `Ö`, `É` survive. Previous `[A-Za-z]` regex stripped them.
|
|
25
|
+
- **`@cyanheads/mcp-ts-core` ^0.8.19 → ^0.8.20** ([0.8.20 changelog](https://github.com/cyanheads/mcp-ts-core/blob/main/changelog/0.8.x/0.8.20.md)) — adds `mcp_tool_scopes` JWT claim union and `MCP_AUTH_DISABLE_SCOPE_CHECKS` bypass for OIDC operator scope-injection escape hatches. `.env.example` documents the new bypass flag. No tool/resource/prompt API impact.
|
|
26
|
+
- **`@biomejs/biome` (dev) ^2.4.14 → ^2.4.15.**
|
|
27
|
+
- **Skill bumps mirrored from upstream.** `api-auth` 1.0 → 1.1, `api-config` 1.3 → 1.4, `security-pass` 1.3 → 1.4, `tool-defs-analysis` 1.0 → 1.1.
|
|
28
|
+
|
|
29
|
+
## Removed
|
|
30
|
+
|
|
31
|
+
- **`src/mcp-server/tools/definitions/_error-contracts.ts`** — moved to `src/services/error-contracts.ts`.
|
|
32
|
+
- **`elink_error` contract entry** on `pubmed_find_related` — replaced by the unified empty-LinkSet disambiguation path.
|
|
33
|
+
|
|
34
|
+
## Tests
|
|
35
|
+
|
|
36
|
+
- 539 passed / 4 skipped. `bun run devcheck` clean.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "New `pubmed_europepmc_search` tool surfaces preprints, patents, Agricola, and EPMC-only OA records. `pubmed_fetch_fulltext` chain expands to NCBI PMC → Europe PMC `fullTextXML` → Unpaywall with a `dois` input branch ([#52](https://github.com/cyanheads/pubmed-mcp-server/issues/52))."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.0 — 2026-05-18
|
|
8
|
+
|
|
9
|
+
Europe PMC integration ([#52](https://github.com/cyanheads/pubmed-mcp-server/issues/52)). One new tool, one expanded chain, one new service module. Default-on; `EUROPEPMC_ENABLED=false` reverts to PMC + Unpaywall and skips tool registration so no EBI calls are made.
|
|
10
|
+
|
|
11
|
+
## Added
|
|
12
|
+
|
|
13
|
+
- **`pubmed_europepmc_search`** (`src/mcp-server/tools/definitions/pubmed-europepmc-search.tool.ts`). Searches Europe PMC across MED, PMC, PPR (preprints), PAT (patents), AGR (Agricola). Default `sources: ["MED", "PMC", "PPR"]`. Cursor-based pagination via `cursorMark` (`*` for the first page; return `nextCursorMark` for the next) — distinct from `pubmed_search_articles`'s offset paging because EPMC's deep results aren't offset-addressable. Output discriminator on `source` plus optional `pmid` / `pmcId` / `doi` cross-walking.
|
|
14
|
+
- **`dois` input branch on `pubmed_fetch_fulltext`** — third XOR alternative to `pmids` / `pmcids`. Resolves DOI → EPMC search → `/{epmcId}/fullTextXML` → Unpaywall. Closes the EPMC search → fetch workflow gap for records that lack PMID/PMCID (preprints, EPMC-only OA).
|
|
15
|
+
- **`viaSource` on every `pubmed_fetch_fulltext` article** — `"pmc" | "europepmc" | "unpaywall"`. Records origin tier separately from `source` (which still discriminates the payload schema: structured JATS sections vs Unpaywall HTML/PDF body). EPMC JATS reuses the `source: "pmc"` schema; `viaSource: "europepmc"` distinguishes origin.
|
|
16
|
+
- **`triedTiers[]` per-tier execution trace on every `unavailable` entry** — ordered list of `{ tier, outcome, detail }` covering `pmc`, `europepmc`, `unpaywall`. `outcome` is a separate enum (`not-attempted`, `miss`, `no-fulltext`, `no-doi`, `no-oa`, `fetch-failed`, `parse-failed`, `service-error`) from the terminal `reason`. Callers see which stage failed and why without parsing error text.
|
|
17
|
+
- **`idType` discriminator on every `unavailable` entry** — `"pmid" | "pmcid" | "doi"`. Replaces the parallel `unavailablePmcIds[]` and `unavailableDois[]` arrays from the spec with a unified `unavailable[]` keyed by input type.
|
|
18
|
+
- **`not-found` and `no-epmc-fulltext` reasons** on `pubmed_fetch_fulltext` `unavailable.reason`. `not-found` separates "upstream returned no record" from "record exists but no full text"; `no-epmc-fulltext` covers preprints-with-PDF-only and other EPMC records without a `fullTextXML`.
|
|
19
|
+
- **`epmcId` / `epmcSource` fields on `PmcArticleSchema`** — populated when the article came via EPMC. Lets callers correlate back to Europe PMC search results.
|
|
20
|
+
- **5 `EUROPEPMC_*` env vars** documented in `.env.example` and the README config table. `EUROPEPMC_ENABLED` (default `true`), `EUROPEPMC_EMAIL`, `EUROPEPMC_REQUEST_DELAY_MS` (default `200`), `EUROPEPMC_MAX_RETRIES` (default `3`), `EUROPEPMC_TIMEOUT_MS` (default `20000`).
|
|
21
|
+
- **`EUROPEPMC_SERVICE_ERRORS` contract pair** in `src/services/error-contracts.ts` — `europepmc_unreachable` (`ServiceUnavailable`, retryable) and `europepmc_invalid_response` (`SerializationError`, retryable). Folded into `ServiceErrorReason` and the `REASON_TO_RECOVERY` map.
|
|
22
|
+
- **`src/services/europe-pmc/` service module** — `api-client.ts`, `europe-pmc-service.ts`, `request-queue.ts`, `types.ts`. Rate-limited request queue is separate from NCBI's (different rate domain). Reuses the NCBI JATS parser for `fullTextXML` so EPMC JATS lands in the same `PmcArticle` schema as NCBI PMC EFetch output.
|
|
23
|
+
- **`envBoolean` preprocessor in `src/config/server-config.ts`** for `EUROPEPMC_ENABLED`. `z.coerce.boolean()` is unusable for env vars because JS truthy semantics coerce `"false"` to `true`; this mirrors the framework's helper so `EUROPEPMC_ENABLED=false` actually disables.
|
|
24
|
+
|
|
25
|
+
## Changed
|
|
26
|
+
|
|
27
|
+
- **`pubmed_fetch_fulltext` chain order** is now NCBI PMC EFetch → Europe PMC `fullTextXML` → Unpaywall. EPMC recovers PMC-counterpart records that NCBI PMC EFetch missed, and resolves DOI input to PMC counterparts when one exists. EPMC's `fullTextXML` is PMC-keyed, so PPR / PAT / AGR records are reachable via `pubmed_europepmc_search` for metadata but produce `no-epmc-fulltext` here.
|
|
28
|
+
- **PMC EFetch failures fall through gracefully.** Previously a PMC EFetch error aborted the whole batch; now it's logged, recorded as `pmc:service-error` in `triedTiers[]`, and EPMC / Unpaywall still run for the remaining IDs. The old `invalid_pmc_efetch_response` error contract was removed in favor of this per-tier outcome.
|
|
29
|
+
- **Unpaywall `McpError(NotFound | ValidationError)` from `fetchWithTimeout` translates to `no-oa`** (`src/services/unpaywall/unpaywall-service.ts`). Mid-chain 404s and shape failures from the framework's transport layer now surface as a clean "no open-access copy" outcome rather than bubbling as raw `McpError`.
|
|
30
|
+
- **Server `instructions`** text rewritten to describe the broader surface: PMC + EPMC, when to broaden via `pubmed_europepmc_search`, when to prefer deterministic resolvers (`pubmed_lookup_citation`, `pubmed_convert_ids`).
|
|
31
|
+
- **`pubmed_fetch_fulltext` tool description** in the README rewrites the chain explainer and enumerates the new reasons and `triedTiers` outcomes.
|
|
32
|
+
- **`@types/node` ^25.8.0 → ^25.9.0.**
|
|
33
|
+
|
|
34
|
+
## Removed
|
|
35
|
+
|
|
36
|
+
- **`unavailablePmcIds[]` and `unavailableDois[]` on `pubmed_fetch_fulltext` output.** Replaced by a unified `unavailable[]` with `idType` discriminator and `triedTiers[]` execution trace. Callers reading either array must migrate to `unavailable[].pmcid` / `unavailable[].doi` filtered by `idType`.
|
|
37
|
+
- **`invalid_pmc_efetch_response` error contract** (`NCBI_SERVICE_ERRORS`). Replaced by a per-tier `pmc:service-error` outcome inside `triedTiers[]`; the batch no longer aborts on PMC EFetch failures.
|
|
38
|
+
|
|
39
|
+
## Tests
|
|
40
|
+
|
|
41
|
+
- 618 passed / 4 skipped. `bun run devcheck` clean. New coverage in `tests/services/europe-pmc/europe-pmc-service.test.ts` (282 lines), `tests/mcp-server/tools/definitions/pubmed-europepmc-search.tool.test.ts` (216 lines), plus expanded `tests/mcp-server/tools/definitions/fetch-fulltext.tool.test.ts` (+960 lines covering DOI input, EPMC tier, `triedTiers[]` outcomes, graceful PMC fall-through).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "`pubmed_europepmc_search` now surfaces silent EPMC rejections (invalid `sort` field, empty query) as `ValidationError` instead of falling through to a fake 0-hit response."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.1 — 2026-05-19
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
- **`pubmed_europepmc_search` silent rejections.** EPMC returns a `{ version }`-only envelope (no `hitCount`, no `request` echo, no `resultList`) when it rejects an undocumented `sort` field, and a separate `{ errCode, errMsg }` shape when it rejects structured input like an empty query. Both formerly normalized to `hitCount: 0` with an empty `hits[]` — the caller saw "no matches" and never learned the request was malformed. `EuropePmcService.search` now detects both shapes and throws `ValidationError` with `data.reason: "europepmc_invalid_input"` and a sort-aware recovery hint.
|
|
12
|
+
- **`pubmed_europepmc_search` `sort` description.** Previously cited `FIRST_PIDATE desc` as an example, which EPMC silently rejects. The description now enumerates the documented sortable fields (`P_PDATE_D`, `CITED`, `AUTH_FIRST`, `PUB_YEAR`) with their semantics.
|
|
13
|
+
|
|
14
|
+
## Added
|
|
15
|
+
|
|
16
|
+
- **`europepmc_invalid_input` error contract** in `EUROPEPMC_SERVICE_ERRORS` — `ValidationError`, non-retryable. Folded into `REASON_TO_RECOVERY`.
|
|
17
|
+
- **`errCode` / `errMsg` fields on `EuropePmcSearchResponse`** for the structured rejection shape.
|
|
18
|
+
|
|
19
|
+
## Changed
|
|
20
|
+
|
|
21
|
+
- **`@types/node` ^25.9.0 → ^25.9.1.**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Three ECitMatch fixes: HTTP 500 reclassified as retryable, dropped citations synthesized as not_found, citation validation requires journal or year"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.10 — 2026-06-04
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
- **`NcbiApiClient.makeRequest`** — NCBI HTTP 500 now reclassified as `ServiceUnavailable` via `httpErrorFromResponse` `codeOverride`, so transient eutils proxy failures are retried under the existing bounded-deadline backoff; 501 Not Implemented stays `InternalError` (non-retryable). ([#62](https://github.com/cyanheads/pubmed-mcp-server/issues/62))
|
|
12
|
+
- **`NcbiService.eCitMatch`** — results are reconciled against submitted citations after parsing; any citation the upstream drops (NCBI omits lines it cannot classify) is synthesized as `{ matched: false, pmid: null, status: 'not_found' }`, guaranteeing `results.length === citations.length` and stable key-based correlation. ([#54](https://github.com/cyanheads/pubmed-mcp-server/issues/54))
|
|
13
|
+
- **`pubmed_lookup_citation` input validation** — citation `.refine()` now requires at least `journal` or `year`; author-only and volume-only inputs (which ECitMatch primary-keys on journal+volume+page and cannot match) are rejected at the schema boundary before the API round-trip. ([#39](https://github.com/cyanheads/pubmed-mcp-server/issues/39))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "fetch_fulltext: DOI→PMCID resolution via PMC ID Converter (#64), config-aware tool description (#65)"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.11 — 2026-06-04
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`pubmed_fetch_fulltext` config-aware description** — the tool description is now composed at startup from the fallback tiers actually enabled (`EUROPEPMC_ENABLED`, `UNPAYWALL_EMAIL`), so `tools/list` advertises only what the deployment can deliver; PMC EFetch is always present, Europe PMC and Unpaywall clauses appear only when their config is set. ([#65](https://github.com/cyanheads/pubmed-mcp-server/issues/65))
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **`pubmed_fetch_fulltext` `dois` branch** — DOI input now calls the PMC ID Converter before PMC EFetch, mirroring the `pmids` branch; a DOI whose article is indexed in PMC returns structured JATS (`viaSource: "pmc"`) instead of a false `unavailable`. Previously the `dois` branch skipped PMC EFetch entirely, giving DOI input a narrower resolution path than `pmids` or `pmcids`. ([#64](https://github.com/cyanheads/pubmed-mcp-server/issues/64))
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Framework `^0.9.1 → ^0.9.3` (zod peer-dep migration). MCPB bundle scaffolding (`manifest.json` + install badges). `fast-xml-parser` unpinned to `^5.8.0` (closes #55)."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.2 — 2026-05-22
|
|
8
|
+
|
|
9
|
+
Maintenance release. Picks up `@cyanheads/mcp-ts-core` 0.9.2 + 0.9.3, unpins `fast-xml-parser` per [#55](https://github.com/cyanheads/pubmed-mcp-server/issues/55), and adopts the framework's MCPB bundle scaffolding for one-click Claude Desktop install. No server behavior changes.
|
|
10
|
+
|
|
11
|
+
## Added
|
|
12
|
+
|
|
13
|
+
- **MCPB bundle support** — `manifest.json` (with `user_config` for `NCBI_API_KEY`, `NCBI_ADMIN_EMAIL`, `UNPAYWALL_EMAIL`), `.mcpbignore`, and `bundle` / `lint:packaging` / `list-skills` / `audit:refresh` npm scripts. Install badges (Claude Desktop / Cursor / VS Code) added to README. `Bundling` section added to `CLAUDE.md` / `AGENTS.md`. MCPB is stdio-only; HTTP and Docker deployments are unaffected.
|
|
14
|
+
- **`zod ^4.4.3`** declared as a direct dependency — `mcp-ts-core` 0.9.2 moved it to `peerDependencies`.
|
|
15
|
+
|
|
16
|
+
## Changed
|
|
17
|
+
|
|
18
|
+
- **`@cyanheads/mcp-ts-core` `^0.9.1 → ^0.9.3`**. Notable upstream changes: MCPB scaffolding (adopted, above); `format-parity` lint now walks each union branch (no unions in our definitions, no impact); canvas API widened to `RequestContextLike` (no DataCanvas usage, no impact).
|
|
19
|
+
- **`fast-xml-parser` `~5.7.3 → ^5.8.0`** (closes [#55](https://github.com/cyanheads/pubmed-mcp-server/issues/55)). Upstream marked the in-tree `XMLValidator` as `@deprecated` in favor of a new sibling `fast-xml-validator` package. The export still works, and both call sites (`src/services/ncbi/response-handler.ts`, `src/services/europe-pmc/europe-pmc-service.ts`) strip `<!DOCTYPE …>` before validation, so 5.8.0's tightened DOCTYPE entity validation cannot reach us. Sibling package not mature enough to migrate to. Biome `noDeprecatedImports` suppressed at both import sites with a comment explaining the stance.
|
|
20
|
+
- **README badge layout** per `polish-docs-meta` 1.9 — Framework badge promoted to its own spotlight row (recolored cyan-300 `67E8F9`); remaining badges grouped across two rows. Docker (ghcr.io) badge added.
|
|
21
|
+
- **Dev deps**: `@vitest/coverage-istanbul` `^4.1.6 → ^4.1.7`, `vitest` `^4.1.6 → ^4.1.7`.
|
|
22
|
+
- **Project skills synced** from framework: `field-test` 2.4 → 2.5, `maintenance` 2.1 → 2.3, `polish-docs-meta` 1.8 → 1.9, `release-and-publish` 2.2 → 2.4. Framework scripts: `devcheck.ts` updated; `lint-packaging.ts` + `list-skills.ts` added.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Framework `^0.9.3 → ^0.9.4`. Documents opt-in `MCP_GC_PRESSURE_INTERVAL_MS` for HTTP heap-growth mitigation."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.3 — 2026-05-22
|
|
8
|
+
|
|
9
|
+
Maintenance release. Picks up `@cyanheads/mcp-ts-core` 0.9.4 and surfaces the new opt-in Bun GC loop to operators of HTTP deployments.
|
|
10
|
+
|
|
11
|
+
## Added
|
|
12
|
+
|
|
13
|
+
- **`MCP_GC_PRESSURE_INTERVAL_MS`** documented in `.env.example` and the README config table — opt-in Bun-only forced-GC loop ([cyanheads/mcp-ts-core#50](https://github.com/cyanheads/mcp-ts-core/issues/50)) for HTTP deployments exhibiting heap growth from the per-request `McpServer`/`McpSessionTransport` reference cycle. Default `0` (disabled); recommended starting point if growth is observed: `60000`. Relevant to the public hosted instance at `pubmed.caseyjhand.com`.
|
|
14
|
+
|
|
15
|
+
## Changed
|
|
16
|
+
|
|
17
|
+
- **`@cyanheads/mcp-ts-core` `^0.9.3 → ^0.9.4`**. Notable upstream changes: the GC loop above (adopted, surfaced to operators); skill-versioning policy extended to cover all reference files under `skills/<name>/` (framework-internal policy, no consumer action); README install-button URL fix landed upstream — Cursor `https://cursor.com/en/install-mcp` + VS Code `https://vscode.dev/redirect?url=vscode:mcp/install?...` — already applied manually in 2.7.2, so this server's badges now match the framework template.
|
|
18
|
+
- **Framework version reference** bumped in `CLAUDE.md` + `AGENTS.md`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Framework `^0.9.4 → ^0.9.6`. `.mcpbignore` recursive-match fix. Skills synced."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.4 — 2026-05-23
|
|
8
|
+
|
|
9
|
+
Maintenance release. Picks up `@cyanheads/mcp-ts-core` 0.9.6 and fixes `.mcpbignore` pattern handling.
|
|
10
|
+
|
|
11
|
+
## Changed
|
|
12
|
+
|
|
13
|
+
- **`@cyanheads/mcp-ts-core` `^0.9.4 → ^0.9.6`**. Notable upstream changes: `.mcpbignore` recursive-match fix (patterns like `node_modules` now correctly exclude nested paths); `maintenance` skill v2.4, `polish-docs-meta` skill v2.2, `release-and-publish` skill v2.5 with improved orchestration.
|
|
14
|
+
- **Skills synced** — `maintenance` 2.4, `polish-docs-meta` 2.2, `release-and-publish` 2.5 copied from updated framework.
|
|
15
|
+
- **keywords** added to `package.json` for npm discoverability.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Framework `^0.9.6 → ^0.9.10`. Plugin metadata scaffolds. Field-test fixes: tool description ↔ enum alignment, PMC ID schema validation, EuropePMC cursor round-trip."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.5 — 2026-05-27
|
|
8
|
+
|
|
9
|
+
Framework upgrade plus a field-test pass across the live HTTP deployment.
|
|
10
|
+
|
|
11
|
+
## Added
|
|
12
|
+
|
|
13
|
+
- **`.claude-plugin/plugin.json`**, **`.codex-plugin/plugin.json`**, **`.codex-plugin/mcp.json`** — Claude Code and Codex plugin metadata scaffolds adopted from `@cyanheads/mcp-ts-core` `^0.9.10` templates.
|
|
14
|
+
- **`git-wrapup` skill** synced from framework `^0.9.8`.
|
|
15
|
+
- **"Close the loop on issues"** core rule in `CLAUDE.md` / `AGENTS.md` (framework `^0.9.9` alignment).
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **`@cyanheads/mcp-ts-core`** `^0.9.6 → ^0.9.10`. Notable for this server's hosted HTTP instance: stateful HTTP mode now rejects non-`initialize` requests missing `Mcp-Session-Id` with HTTP 400 ([#154](https://github.com/cyanheads/mcp-ts-core/issues/154)); expected client errors (401/403/404) use `classifyOnly` + `logger.warning` instead of the full `ErrorHandler` pipeline, cutting stack-trace noise in error logs ([#158](https://github.com/cyanheads/mcp-ts-core/issues/158)); `fast-check` moved to optional peer dep with `loadFc()` lazy loader ([#83](https://github.com/cyanheads/mcp-ts-core/issues/83)).
|
|
20
|
+
- **`@biomejs/biome`** `^2.4.15 → ^2.4.16`.
|
|
21
|
+
- **`request-queue.ts`** — rate-limit throw migrated from `new McpError(JsonRpcErrorCode.RateLimited, …)` to the `rateLimited()` factory.
|
|
22
|
+
- **`tests/_fuzz-helpers.ts`** — `loadFc()` lazy import to track framework `^0.9.9` peer-dep change.
|
|
23
|
+
- **`pubmed_format_citations`** — description text aligned to enum casing (`apa, mla, bibtex, ris`) instead of mismatched uppercase prose.
|
|
24
|
+
- **`pubmed_find_related`** — description surfaces the relationship enum values (`similar`, `cited_by`, `references`) instead of prose ("citing articles") that didn't match the schema.
|
|
25
|
+
- **Skills** — 28+ files synced from the framework package (`maintenance`, `polish-docs-meta`, `release-and-publish`, `design-mcp-server`, `field-test`, `add-*`, `api-*`).
|
|
26
|
+
- **`scripts/check-framework-antipatterns.ts`** — header re-synced from framework `^0.9.9`.
|
|
27
|
+
- **`scripts/split-changelog.ts`** removed — no longer shipped by the framework (one-shot migration script).
|
|
28
|
+
- **README** — install-button URLs updated (`cursor.com/en/install-mcp`, `vscode.dev/redirect?url=…`).
|
|
29
|
+
|
|
30
|
+
## Fixed
|
|
31
|
+
|
|
32
|
+
- **`pubmed_fetch_fulltext`** — `pmcids[]` items now validate `^(?:PMC)?\d+$/i` at the schema boundary. Garbage like `"NOT-A-PMC-ID"` is rejected up front instead of being auto-prefixed to `"PMCNOT-A-PMC-ID"` and triggering an upstream error. Bare-digit inputs (`"3531190"`) still resolve correctly.
|
|
33
|
+
- **`pubmed_fetch_fulltext`** — `format()` sanitizes URLs in `triedTiers.detail` strings to `<upstream>` in the rendered text. Raw URLs are preserved in `structuredContent` for debugging.
|
|
34
|
+
- **`EuropePmcService.search`** — cursorMark echo now uses the caller's raw input instead of EPMC's URL-encoded `request.cursorMark`. Also fixes a latent end-of-pagination bug where the equality check compared the encoded echo against the raw `nextCursorMark`, silently breaking detection for base64 cursors containing `/` or `=`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "MCPB install fix — handle unsubstituted `${user_config.X}` env values when optional fields are left blank."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.6 — 2026-05-27
|
|
8
|
+
|
|
9
|
+
Patch fix for `.mcpb` bundle installs (Claude Desktop, other MCPB hosts). When all three `user_config` fields (`ncbi_api_key`, `ncbi_admin_email`, `unpaywall_email`) are optional and left blank, the host passes the literal `${user_config.X}` placeholder string through to the process instead of an empty value. `z.email()` then rejects it on first config load and the server exits silently after `initialize`.
|
|
10
|
+
|
|
11
|
+
## Fixed
|
|
12
|
+
|
|
13
|
+
- **`emptyAsUndefined`** (`src/config/server-config.ts`) now treats strings matching `${...}` as `undefined`, alongside `''`. Covers `apiKey`, `adminEmail`, `unpaywallEmail`, `europepmcEmail` — every email-validated optional field.
|
|
14
|
+
- **`manifest.json`** — `ncbi_api_key`, `ncbi_admin_email`, `unpaywall_email` `user_config` entries now declare `"default": ""`. Conformant MCPB hosts will substitute empty string instead of the raw placeholder; the server-side stripper is the belt to that suspender.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "mcp-ts-core ^0.9.10 → ^0.9.13: 413 request-body cap, auth-gated landing page default, GET /mcp keywords; landing.requireAuth: false explicit opt-out for hosted instance"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.7 — 2026-05-28
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`landing.requireAuth`** (`src/index.ts`) — explicitly set to `false`. Framework 0.9.13 changed the default: when `MCP_AUTH_MODE` is `jwt` or `oauth`, the landing page inventory (tool/resource listings, invocation snippets) is now gated behind authentication by default. The hosted pubmed instance is a public catalog, so `requireAuth: false` preserves the previous behavior.
|
|
12
|
+
- **`MCP_HTTP_MAX_BODY_BYTES`** (`.env.example`) — documented. Framework 0.9.13 adds a configurable inbound request-body cap on the HTTP endpoint; oversized requests are rejected with `413` before per-request allocation. Default 1 MiB; set to `0` to disable.
|
|
13
|
+
- **`GET /mcp` response** — `server.keywords` now included (from `package.json` `keywords`). Added `"stdio"` and `"streamable-http"` to the keywords list for better discovery.
|
|
14
|
+
- **`@cyanheads/mcp-ts-core`** `^0.9.10` → `^0.9.13`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "mcp-ts-core ^0.9.13 → ^0.9.16: result-set context (effective query, totals, applied filters, empty-result notices) moved to ctx.enrich and mirrored to structuredContent and content[] via enrichmentTrailer render/label"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.8 — 2026-05-29
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **Result-set context relocated to `ctx.enrich`** across `pubmed_search_articles`, `pubmed_europepmc_search`, `pubmed_find_related`, and `pubmed_lookup_mesh`. The effective query, match totals (`totalFound` / `hitCount`), applied filters, queried sources, and empty-result recovery notices now reach both client surfaces — `structuredContent` and a `content[]` trailer — instead of only the domain output. `structuredContent`-only and `content[]`-only clients see the same context.
|
|
12
|
+
- **`enrichmentTrailer` render/label** on the three tools carrying structured enrichment. `appliedFilters` (object) and `appliedSources` (array) render as markdown bullets in the `content[]` trailer rather than JSON blobs; scalar fields use Title-Case labels (`Effective Query`, `Total Found`, `Total Hits`). `structuredContent` always keeps the raw structured value.
|
|
13
|
+
- **`format` script split** — `format` runs safe Biome autofixes only; `format:unsafe` adds `--unsafe`. `AGENTS.md` ships in `package.json` `files[]`. Tracks mcp-ts-core 0.9.16 templates.
|
|
14
|
+
- **`@cyanheads/mcp-ts-core`** `^0.9.13` → `^0.9.16`
|
|
15
|
+
|
|
16
|
+
## Fixed
|
|
17
|
+
|
|
18
|
+
- **`server-config` test** no longer trips Biome's `noTemplateCurlyInString` — literal MCPB `${user_config.X}` placeholders are built via an escaped template literal.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "mcp-ts-core ^0.9.16 → ^0.9.21: per-request log context fix, fetchWithTimeout secret-scrubbing, withRetry fail-fast on non-retryable errors; new scripts/release-github.ts, skill sync"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.7.9 — 2026-06-02
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`@cyanheads/mcp-ts-core`** `^0.9.16` → `^0.9.21` — HTTP transport per-request log context fix (per-request logs and traces carry fresh request + trace/span IDs instead of the frozen boot context); `fetchWithTimeout` strips query-string secrets (e.g. `?api_key=`) from error messages and logs; `withRetry` fails fast on non-retryable errors; `ctx.fail` auto-populates the `retryable` flag.
|
|
12
|
+
- **`scripts/release-github.ts`** added — creates GitHub Releases from annotated tags, attaches `.mcpb` bundle; `release:github` script wired in `package.json`.
|
|
13
|
+
- **`scripts/devcheck.ts`** — Open-Indexed Interfaces and Skill Versions checks added.
|
|
14
|
+
- **Client-config server key** renamed from `"pubmed"` to `"pubmed-mcp-server"` in README install snippets (HTTP, bunx, npx, Docker).
|
|
15
|
+
- **Skills synced** — `add-service`, `add-tool`, `api-canvas`, `api-context`, `api-linter`, `api-utils`, `design-mcp-server`, `release-and-publish` updated; `api-mirror` and `orchestrations` added.
|
|
16
|
+
- **`vitest`** `^4.1.7` → `^4.1.8`, **`@vitest/coverage-istanbul`** `^4.1.7` → `^4.1.8`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Vancouver citation style, full-text reference extraction fix, BibTeX MeSH keyword fix, empty-result notices"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.8.0 — 2026-06-08
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`pubmed_format_citations`**: Vancouver (ICMJE/NLM) style — fifth citation format alongside APA, MLA, BibTeX, and RIS ([#61](https://github.com/cyanheads/pubmed-mcp-server/issues/61)).
|
|
12
|
+
- **`pubmed_fetch_articles`**: empty-result recovery hint surfaced via `ctx.enrich.notice()` so it reaches `structuredContent`, not only `content[]` ([#58](https://github.com/cyanheads/pubmed-mcp-server/issues/58)).
|
|
13
|
+
- **`pubmed_format_citations`**: same empty-result `ctx.enrich.notice()` treatment ([#59](https://github.com/cyanheads/pubmed-mcp-server/issues/59)).
|
|
14
|
+
|
|
15
|
+
## Fixed
|
|
16
|
+
|
|
17
|
+
- **`pubmed_fetch_fulltext`**: references wrapped in JATS `<citation-alternatives>` were silently dropped; parser now descends into the container element. Fixes 64 of 84 references missing for PMC8371605 — applies to both PMC EFetch and Europe PMC full-text paths ([#66](https://github.com/cyanheads/pubmed-mcp-server/issues/66)).
|
|
18
|
+
- **`pubmed_format_citations` BibTeX**: multi-word MeSH descriptors with internal commas (e.g. `Databases, Protein`) split into two keywords; each term is now brace-wrapped to stay one keyword ([#68](https://github.com/cyanheads/pubmed-mcp-server/issues/68)).
|
|
19
|
+
|
|
20
|
+
## Changed
|
|
21
|
+
|
|
22
|
+
- `@types/node` `^25.9.1` → `^25.9.2`
|
|
23
|
+
- `devcheck.config.json`: `@cyanheads/mcp-ts-core` added to `outdated.allowlist` (held at `^0.9.21`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "find_related: offset pagination, multi-source provider fallback (NCBI → EuropePMC → OpenAlex); europepmc_search: date-sort advisory for PPR-only results"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.9.0 — 2026-06-09
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`pubmed_find_related`**: `offset` input parameter (0-based, default 0) enables paging past the first result window — mirrors `pubmed_search_articles` semantics ([#36](https://github.com/cyanheads/pubmed-mcp-server/issues/36)).
|
|
12
|
+
- **`pubmed_find_related`**: `format()` header now emits `**Returned:** N | **Offset:** Z` alongside the existing `**Relationship:**` line, matching `search_articles` header parity ([#36](https://github.com/cyanheads/pubmed-mcp-server/issues/36)).
|
|
13
|
+
- **`pubmed_find_related`**: multi-source provider fallback — NCBI eLink (primary) → Europe PMC → OpenAlex; first success wins, no merging ([#63](https://github.com/cyanheads/pubmed-mcp-server/issues/63)).
|
|
14
|
+
- `similar`: NCBI `pubmed_pubmed` → OpenAlex `related_works`.
|
|
15
|
+
- `cited_by`: NCBI `citedin` → Europe PMC `/MED/{pmid}/citations` → OpenAlex `cites:`.
|
|
16
|
+
- `references`: NCBI `refs` (PMC-indexed only) → Europe PMC `/MED/{pmid}/references` → OpenAlex `referenced_works`. Supersedes the notice-only mitigation for non-PMC sources — actual coverage now served by EPMC/OpenAlex when NCBI has no PMC-indexed reference list.
|
|
17
|
+
- Provenance surfaced in `enrichment.source` (`"ncbi"` | `"europepmc"` | `"openalex"`); a notice is appended to `enrichment` only when a non-primary provider answered.
|
|
18
|
+
- Records without a PMID are dropped, never minted.
|
|
19
|
+
- **`src/services/openalex/`**: new OpenAlex API client — PMID→OA ID resolve, `related_works`/`referenced_works` batch PMID round-trip, `cites:` filter for `cited_by` ([#63](https://github.com/cyanheads/pubmed-mcp-server/issues/63)).
|
|
20
|
+
- **`src/services/europe-pmc/`**: `citations(pmid, options)` and `references(pmid, options)` methods added to the existing Europe PMC integration ([#63](https://github.com/cyanheads/pubmed-mcp-server/issues/63)).
|
|
21
|
+
- **`pubmed_europepmc_search`**: advisory notice via `ctx.enrich.notice()` when `sort` targets a date field (e.g. `P_PDATE_D`) and the effective sources are preprint-only (`PPR`) — EPMC accepts but silently ignores date sort on PPR records; notice names `firstPublicationDate` as the populated field available for page-local client-side sorting ([#67](https://github.com/cyanheads/pubmed-mcp-server/issues/67)).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "fix(fetch_fulltext): element-citation references now delimited; PMC page/bibliographic tokens preserved verbatim"
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.9.1 — 2026-06-09
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
|
|
11
|
+
- **`pubmed_fetch_fulltext`** (`includeReferences: true`): structured `<element-citation>` references now render field-by-field with separators — author names joined with `, `, typed `<pub-id>` nodes labeled (`PMID`, `DOI`, `PMCID`), fields separated by spaces. `<mixed-citation>` rendering is unchanged. ([#69](https://github.com/cyanheads/pubmed-mcp-server/issues/69))
|
|
12
|
+
- **`pubmed_fetch_fulltext`**: PMC JATS ordered parser (`orderedXmlParser`) now runs with `parseTagValue: false` — page tokens like `4002.e26` are preserved verbatim instead of being coerced to `4.002e+29`; zero-padded values retain their leading zeros. The regular parser (esearch/esummary numeric counts) is untouched. ([#69](https://github.com/cyanheads/pubmed-mcp-server/issues/69))
|