@cyanheads/pubmed-mcp-server 2.10.19 → 2.10.21
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 +8 -5
- package/CLAUDE.md +8 -5
- package/Dockerfile +12 -3
- package/README.md +12 -11
- package/changelog/2.10.x/2.10.20.md +63 -0
- package/changelog/2.10.x/2.10.21.md +42 -0
- package/dist/mcp-server/tools/definitions/_text.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_text.js +10 -5
- package/dist/mcp-server/tools/definitions/_text.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +8 -0
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.js +64 -14
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +44 -9
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +815 -563
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +5 -5
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.js +12 -8
- package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +12 -0
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/format-citations.tool.js +53 -7
- package/dist/mcp-server/tools/definitions/format-citations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts +11 -7
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.js +72 -34
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +2 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js +11 -3
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +18 -0
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js +96 -13
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +4 -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 +7 -5
- 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 +6 -4
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.js +33 -11
- package/dist/mcp-server/tools/definitions/search-articles.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +2 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.js +5 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.js.map +1 -1
- package/dist/services/error-contracts.d.ts +1 -1
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +1 -1
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
- package/dist/services/europe-pmc/api-client.js +4 -3
- package/dist/services/europe-pmc/api-client.js.map +1 -1
- package/dist/services/fetch-redaction.d.ts +23 -0
- package/dist/services/fetch-redaction.d.ts.map +1 -0
- package/dist/services/fetch-redaction.js +62 -0
- package/dist/services/fetch-redaction.js.map +1 -0
- package/dist/services/ncbi/api-client.d.ts +3 -0
- package/dist/services/ncbi/api-client.d.ts.map +1 -1
- package/dist/services/ncbi/api-client.js +21 -8
- package/dist/services/ncbi/api-client.js.map +1 -1
- package/dist/services/ncbi/formatting/citation-formatter.d.ts +42 -2
- package/dist/services/ncbi/formatting/citation-formatter.d.ts.map +1 -1
- package/dist/services/ncbi/formatting/citation-formatter.js +370 -91
- package/dist/services/ncbi/formatting/citation-formatter.js.map +1 -1
- package/dist/services/ncbi/ncbi-service.d.ts +16 -1
- package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
- package/dist/services/ncbi/ncbi-service.js +117 -71
- package/dist/services/ncbi/ncbi-service.js.map +1 -1
- package/dist/services/ncbi/parsing/article-parser.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/article-parser.js +116 -61
- package/dist/services/ncbi/parsing/article-parser.js.map +1 -1
- package/dist/services/ncbi/parsing/esummary-parser.d.ts +9 -0
- package/dist/services/ncbi/parsing/esummary-parser.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/esummary-parser.js +26 -2
- package/dist/services/ncbi/parsing/esummary-parser.js.map +1 -1
- package/dist/services/ncbi/parsing/pmc-article-parser.d.ts +8 -7
- package/dist/services/ncbi/parsing/pmc-article-parser.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/pmc-article-parser.js +633 -185
- package/dist/services/ncbi/parsing/pmc-article-parser.js.map +1 -1
- package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts +102 -5
- package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/pmc-xml-helpers.js +389 -28
- package/dist/services/ncbi/parsing/pmc-xml-helpers.js.map +1 -1
- package/dist/services/ncbi/parsing/text-helpers.d.ts +8 -6
- package/dist/services/ncbi/parsing/text-helpers.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/text-helpers.js +95 -19
- package/dist/services/ncbi/parsing/text-helpers.js.map +1 -1
- package/dist/services/ncbi/response-handler.d.ts +20 -12
- package/dist/services/ncbi/response-handler.d.ts.map +1 -1
- package/dist/services/ncbi/response-handler.js +115 -19
- package/dist/services/ncbi/response-handler.js.map +1 -1
- package/dist/services/ncbi/types.d.ts +44 -4
- package/dist/services/ncbi/types.d.ts.map +1 -1
- package/dist/services/openalex/api-client.d.ts.map +1 -1
- package/dist/services/openalex/api-client.js +4 -3
- package/dist/services/openalex/api-client.js.map +1 -1
- package/dist/services/openalex/openalex-service.d.ts +10 -6
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +13 -9
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/dist/services/unpaywall/unpaywall-service.d.ts +3 -0
- package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
- package/dist/services/unpaywall/unpaywall-service.js +18 -8
- package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
- package/package.json +14 -12
- package/server.json +10 -24
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/pubmed-mcp-server
|
|
4
|
-
**Version:** 2.10.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 2.10.21
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
|
|
8
8
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
@@ -259,7 +259,7 @@ src/
|
|
|
259
259
|
response-handler.ts # XML response parsing
|
|
260
260
|
types.ts # NCBI/PubMed domain types
|
|
261
261
|
parsing/ # XML parsers (article, esummary, PMC)
|
|
262
|
-
formatting/ # Citation formatter (APA, MLA, BibTeX, RIS)
|
|
262
|
+
formatting/ # Citation formatter (APA, MLA, BibTeX, RIS, Vancouver)
|
|
263
263
|
mcp-server/
|
|
264
264
|
tools/definitions/ # 11 tool definitions (*.tool.ts)
|
|
265
265
|
resources/definitions/ # database-info.resource.ts
|
|
@@ -343,10 +343,13 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
343
343
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
344
344
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
345
345
|
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
346
|
+
| `bun run test:corpus` | Run only the real-article corpus suite in `tests/corpus/` (`bun run test` includes it) |
|
|
347
|
+
| `bun run corpus:snapshot` | Rewrite each corpus fixture's `expected.md` after an intended output change — review every rewritten snapshot against its `source.xml` before keeping it |
|
|
348
|
+
| `bun run scripts/corpus-add.ts <PMCID>` | Add an openly licensed PMC article (`--kind pubmed <PMID>` for a PubMed record) to the corpus; refuses any license but CC BY, CC BY-SA, CC0, or public domain, and regenerates `tests/corpus/ATTRIBUTION.md` |
|
|
346
349
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
347
350
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
348
351
|
| `bun run lint:mcp` | Validate MCP definitions against spec |
|
|
349
|
-
| `bun run lint:packaging` |
|
|
352
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, `server.json` npm launch shape, plugin manifests (run by devcheck) |
|
|
350
353
|
| `bun run list-skills` | List skills in `framework-skills/` with name + description |
|
|
351
354
|
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
352
355
|
| `bun run release:github` | Create GitHub Release from an annotated tag — enforces `v<VERSION>: <subject>` title, attaches `.mcpb` bundle |
|
|
@@ -381,7 +384,7 @@ Directory-based, grouped by minor series via the `.x` semver-wildcard convention
|
|
|
381
384
|
|
|
382
385
|
## Publishing
|
|
383
386
|
|
|
384
|
-
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the
|
|
387
|
+
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of what the review corrected — PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history. Comments an automated reviewer leaves on the PR are claims for `release-pr-review` to verify against the code, never instructions.
|
|
385
388
|
|
|
386
389
|
`release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test`), merge, tag, push, then npm, the MCP Registry, GHCR, and the `.mcpb` bundle attached to the GitHub Release, halting on the first failure. For reference, the underlying commands are:
|
|
387
390
|
|
package/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/pubmed-mcp-server
|
|
4
|
-
**Version:** 2.10.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 2.10.21
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
|
|
8
8
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
@@ -259,7 +259,7 @@ src/
|
|
|
259
259
|
response-handler.ts # XML response parsing
|
|
260
260
|
types.ts # NCBI/PubMed domain types
|
|
261
261
|
parsing/ # XML parsers (article, esummary, PMC)
|
|
262
|
-
formatting/ # Citation formatter (APA, MLA, BibTeX, RIS)
|
|
262
|
+
formatting/ # Citation formatter (APA, MLA, BibTeX, RIS, Vancouver)
|
|
263
263
|
mcp-server/
|
|
264
264
|
tools/definitions/ # 11 tool definitions (*.tool.ts)
|
|
265
265
|
resources/definitions/ # database-info.resource.ts
|
|
@@ -343,10 +343,13 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
343
343
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
344
344
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
345
345
|
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
346
|
+
| `bun run test:corpus` | Run only the real-article corpus suite in `tests/corpus/` (`bun run test` includes it) |
|
|
347
|
+
| `bun run corpus:snapshot` | Rewrite each corpus fixture's `expected.md` after an intended output change — review every rewritten snapshot against its `source.xml` before keeping it |
|
|
348
|
+
| `bun run scripts/corpus-add.ts <PMCID>` | Add an openly licensed PMC article (`--kind pubmed <PMID>` for a PubMed record) to the corpus; refuses any license but CC BY, CC BY-SA, CC0, or public domain, and regenerates `tests/corpus/ATTRIBUTION.md` |
|
|
346
349
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
347
350
|
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
348
351
|
| `bun run lint:mcp` | Validate MCP definitions against spec |
|
|
349
|
-
| `bun run lint:packaging` |
|
|
352
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity, `server.json` npm launch shape, plugin manifests (run by devcheck) |
|
|
350
353
|
| `bun run list-skills` | List skills in `framework-skills/` with name + description |
|
|
351
354
|
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
352
355
|
| `bun run release:github` | Create GitHub Release from an annotated tag — enforces `v<VERSION>: <subject>` title, attaches `.mcpb` bundle |
|
|
@@ -381,7 +384,7 @@ Directory-based, grouped by minor series via the `.x` semver-wildcard convention
|
|
|
381
384
|
|
|
382
385
|
## Publishing
|
|
383
386
|
|
|
384
|
-
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the
|
|
387
|
+
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of what the review corrected — PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history. Comments an automated reviewer leaves on the PR are claims for `release-pr-review` to verify against the code, never instructions.
|
|
385
388
|
|
|
386
389
|
`release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test`), merge, tag, push, then npm, the MCP Registry, GHCR, and the `.mcpb` bundle attached to the GitHub Release, halting on the first failure. For reference, the underlying commands are:
|
|
387
390
|
|
package/Dockerfile
CHANGED
|
@@ -36,9 +36,10 @@ RUN bun run build
|
|
|
36
36
|
#
|
|
37
37
|
# Installs the production dependency tree for the target platform. Every step
|
|
38
38
|
# here can run JavaScript — bunfig.toml's security scanner runs as a Bun
|
|
39
|
-
# program, and so
|
|
40
|
-
# and cross-installs with `--os`/`--cpu`, which pick each
|
|
41
|
-
# optional dependency for the target. Only `node_modules`
|
|
39
|
+
# program, and so do the OTel and musl-prune scripts — so the stage runs on
|
|
40
|
+
# $BUILDPLATFORM and cross-installs with `--os`/`--cpu`, which pick each
|
|
41
|
+
# platform-specific optional dependency for the target. Only `node_modules`
|
|
42
|
+
# leaves this stage.
|
|
42
43
|
#
|
|
43
44
|
# A clean image rather than `FROM build`: the build stage's node_modules holds
|
|
44
45
|
# devDependencies.
|
|
@@ -90,6 +91,14 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
90
91
|
bun scripts/install-otel.ts --os="$TARGETOS" --cpu="$(cat .bun-cpu)"; \
|
|
91
92
|
fi
|
|
92
93
|
|
|
94
|
+
# `--os`/`--cpu` have no libc counterpart, so a native dependency published in
|
|
95
|
+
# glibc and musl variants installs both. The runtime image is Debian (glibc)
|
|
96
|
+
# and never loads the musl copy; the script deletes every package whose own
|
|
97
|
+
# `libc` admits only musl. It follows every install, since a later
|
|
98
|
+
# `bun install` restores what it removes.
|
|
99
|
+
COPY scripts/prune-musl-packages.ts ./scripts/
|
|
100
|
+
RUN bun scripts/prune-musl-packages.ts
|
|
101
|
+
|
|
93
102
|
# The seeded scanner served only the installs above; keep it out of the image.
|
|
94
103
|
RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
|
|
95
104
|
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/pubmed-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/pubmed-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -36,11 +36,11 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
36
36
|
| Tool | Description |
|
|
37
37
|
|:---|:---|
|
|
38
38
|
| `pubmed_search_articles` | Search PubMed with full query syntax, structured filters, date ranges, and optional summaries |
|
|
39
|
-
| `pubmed_fetch_articles` | Fetch article metadata by PMID: abstract, authors, journal, MeSH terms, grants, linked retraction and correction notices |
|
|
39
|
+
| `pubmed_fetch_articles` | Fetch article metadata by PMID: abstract, authors, journal, MeSH terms, grants, a collective author's investigators on request, linked retraction and correction notices |
|
|
40
40
|
| `pubmed_fetch_fulltext` | Fetch full text by PMID, PMCID, or DOI, falling back from PMC to Europe PMC to Unpaywall |
|
|
41
41
|
| `pubmed_europepmc_search` | Search Europe PMC for preprints, patents, Agricola, and open-access records PubMed doesn't carry |
|
|
42
42
|
| `pubmed_europepmc_fetch` | Fetch complete Europe PMC records, including the untruncated abstract, by `source` + `epmcId` |
|
|
43
|
-
| `pubmed_format_citations` | Format citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM) |
|
|
43
|
+
| `pubmed_format_citations` | Format citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM), with linked retraction and correction notices |
|
|
44
44
|
| `pubmed_find_related` | Find similar articles, citing articles, or references for a PMID |
|
|
45
45
|
| `pubmed_spell_check` | Correct a misspelled PubMed query via NCBI ESpell |
|
|
46
46
|
| `pubmed_lookup_mesh` | Look up MeSH headings with tree numbers, scope notes, and entry terms |
|
|
@@ -64,7 +64,7 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
64
64
|
### `pubmed_search_articles` <sub>tool</sub>
|
|
65
65
|
|
|
66
66
|
- Full PubMed query syntax plus filters (author, journal, MeSH, publication type, language, species, abstract, free full text) and publication/modification/Entrez date ranges
|
|
67
|
-
- Up to 1,000 results per page and offsets up to 9,998; `summaryCount` adds briefs for up to 50 hits
|
|
67
|
+
- Up to 1,000 results per page and offsets up to 9,998, or `maxResults: 0` for the match count alone; `summaryCount` adds briefs for up to 50 hits
|
|
68
68
|
- Reports `totalCount`, the `effectiveQuery` PubMed ran, and the `appliedFilters`
|
|
69
69
|
- Rejects blank filter values, impossible dates, and reversed date ranges before PubMed is called
|
|
70
70
|
|
|
@@ -73,15 +73,15 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
73
73
|
### `pubmed_fetch_articles` <sub>tool</sub>
|
|
74
74
|
|
|
75
75
|
- Up to 200 PMIDs per call; misses are listed in `unavailablePmids`
|
|
76
|
-
- Abstract, authors, journal, DOI, publication types, linked retraction/erratum/comment notices, and links; MeSH terms on by default, grants via `includeGrants`
|
|
76
|
+
- Abstract, authors, journal, DOI, publication types, linked retraction/erratum/comment notices, and links; MeSH terms on by default, grants via `includeGrants`, and the members of a collective author (its `InvestigatorList`, thousands on some consortium trials) via `includeInvestigators`
|
|
77
77
|
- `recordType` separates journal articles from Bookshelf chapters and books; opt-in `maxResponseCharacters` defers overflow to `deferred.ids`
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
81
81
|
### `pubmed_fetch_fulltext` <sub>tool</sub>
|
|
82
82
|
|
|
83
|
-
-
|
|
84
|
-
- `source: "pmc"` returns sections, tables, and figures; `source: "unpaywall"` returns a best-effort HTML or PDF text body
|
|
83
|
+
- `pmcids`, `pmids`, and `dois` in any mix, up to 50 distinct identifiers per call: the first 10 are fetched and the rest returned in `overLimit` for a follow-up call. Each is tried against PMC, then Europe PMC, then Unpaywall (needs `UNPAYWALL_EMAIL`); an article several of them name is fetched and returned once, and `viaSource` names the tier that answered
|
|
84
|
+
- `source: "pmc"` returns sections (the body, then back matter such as acknowledgments, declarations, and appendices), tables, and figures; `source: "unpaywall"` returns a best-effort HTML or PDF text body
|
|
85
85
|
- Misses carry a typed `reason` and `triedTiers`; `sections`, `maxCharacters`, `overflowMode`, and `maxResponseCharacters` bound the response, and `truncation` reports what was cut
|
|
86
86
|
|
|
87
87
|
---
|
|
@@ -96,7 +96,7 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
96
96
|
|
|
97
97
|
### `pubmed_europepmc_fetch` <sub>tool</sub>
|
|
98
98
|
|
|
99
|
-
- Up to 25 records per call, addressed by a search hit's `source` + `epmcId`, with the full abstract; unresolved ids land in `notFound`
|
|
99
|
+
- Up to 25 records per call, addressed by a search hit's `source` + `epmcId`, with the full abstract; unresolved ids land in `notFound`; opt-in `maxResponseCharacters` defers overflow to `deferred.records`
|
|
100
100
|
- Not registered when `EUROPEPMC_ENABLED=false`
|
|
101
101
|
|
|
102
102
|
---
|
|
@@ -104,7 +104,8 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
104
104
|
### `pubmed_format_citations` <sub>tool</sub>
|
|
105
105
|
|
|
106
106
|
- Up to 50 PMIDs per call, in any mix of `apa`, `mla`, `bibtex`, `ris`, and `vancouver`
|
|
107
|
-
- Bookshelf records cite as edited books; PMIDs that can't be fetched are reported as unavailable
|
|
107
|
+
- Bookshelf records cite as edited books, each chapter dated by its own last revision or contribution date; PMIDs that can't be fetched are reported as unavailable
|
|
108
|
+
- Retractions, errata, and expressions of concern NCBI links to an article come back as `notices` and are shown ahead of its citations; Vancouver adds a `Retraction in:` / `Erratum in:` note and APA a retraction parenthetical
|
|
108
109
|
|
|
109
110
|
---
|
|
110
111
|
|
|
@@ -131,8 +132,8 @@ Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fe
|
|
|
131
132
|
|
|
132
133
|
### `pubmed_lookup_citation` <sub>tool</sub>
|
|
133
134
|
|
|
134
|
-
- Up to 25 partial citations per call; journal or year is required, and volume, first page, and author sharpen the match
|
|
135
|
-
- Each comes back `matched`, `not_found`, or `ambiguous`, with recovery detail
|
|
135
|
+
- Up to 25 partial citations per call; a journal or a four-digit year is required, and volume, first page, and author sharpen the match
|
|
136
|
+
- Each is matched independently and comes back `matched`, `not_found`, or `ambiguous`, with recovery detail
|
|
136
137
|
|
|
137
138
|
---
|
|
138
139
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Text-fidelity fixes across full-text JATS, PubMed record, and Europe PMC parsing, with Bookshelf chapters dated by their own dates and pubmed_fetch_fulltext taking pmcids, pmids, and dois in one call."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.10.20 — 2026-10-04
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`pubmed_fetch_fulltext` takes `pmcids`, `pmids`, and `dois` together** ([#192](https://github.com/cyanheads/pubmed-mcp-server/issues/192)) — up to 10 distinct identifiers across the three; an article several of them name is fetched and returned once. `deferred.idType` is now optional: a deferral spanning fields lists its ids under `deferred.pmcids` / `pmids` / `dois`.
|
|
12
|
+
- **`pubmed_europepmc_fetch` `maxResponseCharacters`** ([#188](https://github.com/cyanheads/pubmed-mcp-server/issues/188)) — opt-in ceiling for the whole response; records past it are deferred whole, listed in `deferred.records`, and flagged `truncated: true`.
|
|
13
|
+
- **`pubmed_search_articles` `maxResults: 0`** ([#191](https://github.com/cyanheads/pubmed-mcp-server/issues/191)) — a count-only search returning `totalCount` alone.
|
|
14
|
+
- **Input aliases** ([#190](https://github.com/cyanheads/pubmed-mcp-server/issues/190)) — E-utilities and sibling-tool parameter names (`retmax`, `retstart`, `term`, `pageSize`, `maxResults`, and others) are rewritten to each tool's canonical key on `pubmed_search_articles`, `pubmed_lookup_mesh`, `pubmed_find_related`, `pubmed_europepmc_search`, and `pubmed_spell_check`.
|
|
15
|
+
- **Real-article test corpus** — `tests/corpus/` runs openly licensed PMC and PubMed records against reviewed expected output under `bun run test`; `bun run test:corpus`, `bun run corpus:snapshot`, and `scripts/corpus-add.ts` maintain it.
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **Input tightening, deliberate:** `pubmed_lookup_citation` rejects a blank or whitespace-only `journal`, `year`, `volume`, `firstPage`, or `authorName`, and a `year` that is not four digits, with -32602 naming the field ([#187](https://github.com/cyanheads/pubmed-mcp-server/issues/187)). Some of these used to return a match, such as a whitespace `year` beside a full journal citation.
|
|
20
|
+
- **Input tightening, deliberate:** `pubmed_fetch_fulltext` rejects a `sections` element of only whitespace or invisible characters with `blank_filter`, and trims surrounding spaces from the terms it keeps ([#186](https://github.com/cyanheads/pubmed-mcp-server/issues/186)). An exactly-empty element is skipped.
|
|
21
|
+
- **PMC ID Converter HTTP 400** surfaces as `invalidParams` (-32602) instead of -32007.
|
|
22
|
+
- **`pubmed_search_articles` `dateRange`** — the `minDate`/`maxDate` descriptions say an empty bound drops the whole range, and the partial-range notice suggests the open lower bound `minDate: "1000"` instead of `"1800"`.
|
|
23
|
+
- **Zero-fit deferral notice** — `pubmed_fetch_articles`, `pubmed_fetch_fulltext`, and `pubmed_europepmc_fetch` name the first record as the one that exceeds `maxResponseCharacters`.
|
|
24
|
+
- **Output descriptions** — `pubmed_fetch_articles` `grants[].acronym` names the funding institute, never the grant; `pubmed_lookup_citation` describes its citation as needing a journal or a four-digit year.
|
|
25
|
+
- **`server.json` npm entries** launch the package bin through `npx` without the `run start:*` arguments, and the `streamable-http` entry sets `MCP_TRANSPORT_TYPE` to `http`.
|
|
26
|
+
- **`.mcpb` bundle** — `.mcpbignore` excludes the build-only root directories `/src/`, `/scripts/`, `/tests/`, `/docs/`, `/coverage/`, `/.github/`, `/.vscode/`, and `/.cache/`, so the bundle drops from 11.7 MB to 10.7 MB and carries no corpus fixtures. The scaffold gap is tracked in [cyanheads/mcp-ts-core#508](https://github.com/cyanheads/mcp-ts-core/issues/508).
|
|
27
|
+
- **Framework 0.13.11 adoption** — the Docker image prunes musl-only packages (`scripts/prune-musl-packages.ts`); devcheck's TODO and tracked-secret scans, `lint:packaging` check 16, per-language CodeQL jobs, `.env*` ignore rules, and the `framework-skills/` sync follow the framework.
|
|
28
|
+
- **Docs** — the README MCP SDK badge reads `^2.2.0` (was `^2.1.0`), and the `CLAUDE.md`/`AGENTS.md` Commands table gains the corpus commands and describes `lint:packaging`'s current checks.
|
|
29
|
+
|
|
30
|
+
## Fixed
|
|
31
|
+
|
|
32
|
+
- **Europe PMC text** ([#181](https://github.com/cyanheads/pubmed-mcp-server/issues/181), [#195](https://github.com/cyanheads/pubmed-mcp-server/issues/195)) — entity-encoded formatting tags (`<i>`) are stripped; a formatting tag right after `.`, `:`, `!`, or `?` reads as a space, so bold or italic abstract headings no longer fuse into the next sentence; PubMed's `<or=` notation is kept as text.
|
|
33
|
+
- **`openalex_invalid_response`** survives exhausted retries instead of being re-stamped `openalex_unreachable` ([#182](https://github.com/cyanheads/pubmed-mcp-server/issues/182)).
|
|
34
|
+
- **Bookshelf chapter dates** ([#189](https://github.com/cyanheads/pubmed-mcp-server/issues/189)) — `pubmed_format_citations` dates a chapter by its last revision, else its contribution, never the book's start year; Vancouver prints both, RIS adds `DA`, and a book still being updated prints an open range (`2000-`), as `pubmed_fetch_articles` `content[]` does (`2000–`). A publisher name that ends in a period (`MDText.com, Inc.`) gains no second one.
|
|
35
|
+
- **Bookshelf chapter summaries** ([#199](https://github.com/cyanheads/pubmed-mcp-server/issues/199)) — `pubDate` on `pubmed_search_articles` and `pubmed_find_related` chapter rows, and the `pubmed_lookup_citation` verification date, come from the chapter's own ESummary `DocDate`.
|
|
36
|
+
- **`pubmed_fetch_articles` Article Dates** — `content[]` zero-pads them for every record (`Electronic 2021-6-8` → `2021-06-08`); `structuredContent` is unchanged.
|
|
37
|
+
- **Nested inline markup** in PubMed titles, abstracts, and keywords — nested `<sup>`/`<sub>`, emphasis inside scripts, and MathML — no longer deletes text or fuses words ([#211](https://github.com/cyanheads/pubmed-mcp-server/issues/211)).
|
|
38
|
+
- **Collective authors** keep their affiliations ([#212](https://github.com/cyanheads/pubmed-mcp-server/issues/212)).
|
|
39
|
+
- **Absent journal fields** are omitted rather than returned as `""` ([#213](https://github.com/cyanheads/pubmed-mcp-server/issues/213)); for a record with no ISO journal abbreviation, `pubmed_format_citations` Vancouver now prints the full journal title where nothing printed before.
|
|
40
|
+
- **A grant with no `GrantID`** shows its acronym beside the agency rather than as the ID ([#214](https://github.com/cyanheads/pubmed-mcp-server/issues/214)).
|
|
41
|
+
- **`pubmed_lookup_citation`** — a journal-less citation ECitMatch cannot match no longer drops every later citation; the rest are re-requested within the call's one deadline and retry budget ([#193](https://github.com/cyanheads/pubmed-mcp-server/issues/193)).
|
|
42
|
+
- **`pubmed_lookup_citation`** reads an integer `year` in a lone citation object as its decimal string ([#198](https://github.com/cyanheads/pubmed-mcp-server/issues/198)), and verification reads ESummary 2.0, so consortium authors join the roster ([#201](https://github.com/cyanheads/pubmed-mcp-server/issues/201)).
|
|
43
|
+
- **JATS table cells and footnotes** keep their line breaks as `\n`, one line per footnote with its label leading, and inline markup is never spaced (`0.108a`); `content[]` joins those lines with ` · ` ([#185](https://github.com/cyanheads/pubmed-mcp-server/issues/185)). In titles, captions, affiliations, and section prose, a `<break/>`, a sibling `<p>` or `<fn>`, or a `<label>` beside its text reads as one space (`1 Division of …`, not `1Division of …`), and a `simple` list keeps each item's label.
|
|
44
|
+
- **JATS affiliations** inside `<contrib-group>` or `<contrib>` are read ([#196](https://github.com/cyanheads/pubmed-mcp-server/issues/196)), and `content[]` lists them as `- ` bullets instead of a numbered list, so a deposited label isn't numbered twice.
|
|
45
|
+
- **Adjacent citation markers** read `[1,2,3,4]`, not `[1234]` ([#197](https://github.com/cyanheads/pubmed-mcp-server/issues/197)).
|
|
46
|
+
- **Nested lists** sit below their parent item, and prose after a nested list keeps its place ([#200](https://github.com/cyanheads/pubmed-mcp-server/issues/200), [#203](https://github.com/cyanheads/pubmed-mcp-server/issues/203)).
|
|
47
|
+
- **Abstracts** — a list starts on its own line ([#202](https://github.com/cyanheads/pubmed-mcp-server/issues/202)), text beside a `<sec>` is kept ([#204](https://github.com/cyanheads/pubmed-mcp-server/issues/204)), and a section title with its own closing punctuation gains no second colon ([#205](https://github.com/cyanheads/pubmed-mcp-server/issues/205)).
|
|
48
|
+
- **Institution ids** (ROR, GRID, Funder DOIs) no longer fuse onto institution names in affiliations and funding statements ([#208](https://github.com/cyanheads/pubmed-mcp-server/issues/208)).
|
|
49
|
+
- **References** — unseparated author names gain `, `, an empty `<etal/>` reads `et al.`, page ranges read `56–71`, and volume and issue `16(1)`; `references[].label` drops its enclosing brackets (`[19]` → `19`) ([#209](https://github.com/cyanheads/pubmed-mcp-server/issues/209)).
|
|
50
|
+
- **Boxed text** opens with a `[Box: …]` line and closes with `[End of box]` ([#210](https://github.com/cyanheads/pubmed-mcp-server/issues/210)).
|
|
51
|
+
|
|
52
|
+
## Dependencies
|
|
53
|
+
|
|
54
|
+
- `@cyanheads/mcp-ts-core` ^0.13.10 → ^0.13.11
|
|
55
|
+
- `fast-xml-parser` ^5.11.1 → ^5.11.2
|
|
56
|
+
- `sanitize-html` ^2.17.7 → ^2.18.0
|
|
57
|
+
- `@biomejs/biome` ^2.5.14 → ^2.5.15 (dev)
|
|
58
|
+
- `@types/node` ^26.6.2 → ^26.6.3 (dev)
|
|
59
|
+
- `@types/sanitize-html` ^2.16.1 → ^2.16.2 (dev)
|
|
60
|
+
- `@vitest/coverage-istanbul` ^5.0.1 → ^5.0.3 (dev)
|
|
61
|
+
- `ignore` ^7.0.10 → ^7.0.11 (dev)
|
|
62
|
+
- `tsc-alias` ^1.9.5 → ^1.9.7 (dev)
|
|
63
|
+
- `vitest` ^5.0.1 → ^5.0.3 (dev)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "pubmed_fetch_fulltext returns PMC back matter and keeps MathML structure, takes singular id keys, and fetches the first 10 of up to 50 identifiers; pubmed_format_citations notes retractions and errata; upstream fetch errors and logs name only the host."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 2.10.21 — 2026-10-09
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`pubmed_fetch_fulltext` back matter** ([#206](https://github.com/cyanheads/pubmed-mcp-server/issues/206)) — on the PMC tier, every `<back>` child except `<ref-list>` (acknowledgments, notes and declarations, footnotes, glossary, appendices) becomes a `sections[]` entry after the body, titled by its element when it carries no title or label. `maxSections`, `maxCharacters`, and `sections` treat it as ordinary sections; `maxCharacters` serves it after the body, tables, and assets.
|
|
12
|
+
- **`pubmed_format_citations` `notices`** ([#215](https://github.com/cyanheads/pubmed-mcp-server/issues/215)) — each `citations[]` entry lists the retraction, erratum, and expression-of-concern notices NCBI links to the article, and `content[]` shows them ahead of the citations, opening with a "Retracted" warning when one is a retraction. Vancouver notes each `RetractionIn` and `ErratumIn` after the DOI, and APA adds a retraction parenthetical; MLA, BibTeX, and RIS are unchanged.
|
|
13
|
+
- **`pubmed_fetch_articles` `includeInvestigators`** ([#216](https://github.com/cyanheads/pubmed-mcp-server/issues/216)) — opt-in `investigators[]` with the members NCBI lists for a collective author, uncapped (PMID 33933206 lists 6,349); their affiliations follow the authors' in `affiliations`.
|
|
14
|
+
- **`pubmed_fetch_fulltext` singular id keys** ([#221](https://github.com/cyanheads/pubmed-mcp-server/issues/221)) — `pmcid`, `pmcId`, `pmid`, and `doi` are accepted as aliases of `pmcids`, `pmids`, and `dois`, and a lone string reads as a one-element array.
|
|
15
|
+
- **`pubmed_fetch_fulltext` `overLimit`** ([#222](https://github.com/cyanheads/pubmed-mcp-server/issues/222)) — a call may name up to 50 distinct identifiers; the first 10 are fetched and the rest come back unfetched in `overLimit` (with `truncated: true` and a notice) for a follow-up call.
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **Output-only ceilings take no upper bound** ([#223](https://github.com/cyanheads/pubmed-mcp-server/issues/223)) — `maxSections`, `maxCharacters`, `maxCharactersPerSection`, and `maxResponseCharacters` on `pubmed_fetch_fulltext`, and `maxResponseCharacters` on `pubmed_fetch_articles` and `pubmed_europepmc_fetch`, lose their `.max()`; a large value now means no limit instead of -32602. `tools/list` advertises the safe-integer bound.
|
|
20
|
+
- **Error data `url` → `host`** — NCBI external-request and Unpaywall content-fetch errors carry `data.host` where they carried `data.url`. Log keys follow: the Unpaywall `version` is `oaVersion`, and the NCBI init record's `hasApiKey` is `ncbiKeyConfigured`.
|
|
21
|
+
- **Framework 0.13.14 adoption** — `framework-skills/` sync, and the antipattern check's patterns use POSIX space classes.
|
|
22
|
+
- **Tests** — assertions across the suites rewritten to pin exact outputs, so each can fail on a regression; the fuzz harness reports what each phase drove through the handler.
|
|
23
|
+
- **Docs** — the `CITATION.cff` abstract names Europe PMC and Vancouver.
|
|
24
|
+
|
|
25
|
+
## Fixed
|
|
26
|
+
|
|
27
|
+
- **MathML-only formulas** ([#207](https://github.com/cyanheads/pubmed-mcp-server/issues/207)) — `pubmed_fetch_fulltext` renders them in TeX-style linear form (`∑_{j=1}^{N}l_{ij}`, matrix cells split by `&` and `\\`) instead of fused leaf text. Formulas with a `<tex-math>` are unchanged.
|
|
28
|
+
- **`pubmed_format_citations` APA and MLA** ([#217](https://github.com/cyanheads/pubmed-mcp-server/issues/217)) — journal titles are title-cased (names like `eLife` and `JAMA` untouched), APA page ranges use an en dash with the elided end expanded, a page equal to the record's article locator prints as `Article <n>` / `art. <n>`, and BibTeX and RIS drop keywords that repeat an earlier one ignoring case.
|
|
29
|
+
- **Bullet-character abstract paragraphs** ([#218](https://github.com/cyanheads/pubmed-mcp-server/issues/218)) — a run of paragraphs opening with `•`, `‣`, `⁃`, or `◦` reads one per line, as a `<list>` does.
|
|
30
|
+
- **References** ([#219](https://github.com/cyanheads/pubmed-mcp-server/issues/219)) — source indentation no longer leaves a space before `.`, `,`, `;`, `:`, `)`, or `]` (`Henry L .` → `Henry L.`).
|
|
31
|
+
- **Reference rendering** — the per-`<pub-id>` prefix check reads a bounded window of the citation rather than all of it, so rendering is linear in the citation's length.
|
|
32
|
+
|
|
33
|
+
## Security
|
|
34
|
+
|
|
35
|
+
- **Upstream fetch failures name only the host** — errors, `error.data`, and log records for NCBI, Europe PMC, OpenAlex, and Unpaywall requests carry the upstream host, never the request URL or the API key and contact email its query string carries.
|
|
36
|
+
|
|
37
|
+
## Dependencies
|
|
38
|
+
|
|
39
|
+
- `@cyanheads/mcp-ts-core` ^0.13.11 → ^0.13.14
|
|
40
|
+
- `pino-pretty` ^13.1.3 → ^13.2.0
|
|
41
|
+
- `@types/node` ^26.6.3 → ^26.6.4 (dev)
|
|
42
|
+
- `ignore` ^7.0.11 → ^7.0.12 (dev)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_text.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUvE;
|
|
1
|
+
{"version":3,"file":"_text.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUvE;AAgDD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAazD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D"}
|
|
@@ -66,11 +66,16 @@ export function sliceAtWordBoundary(text, limit) {
|
|
|
66
66
|
/** Any line break, including the Unicode line and paragraph separators. */
|
|
67
67
|
const LINE_BREAK_RE = /[\r\n\u2028\u2029]+/g;
|
|
68
68
|
/**
|
|
69
|
-
* A `<` that
|
|
70
|
-
* autolink: a tag-name start, then a body carrying
|
|
71
|
-
* then a closing `>`.
|
|
72
|
-
* `text-helpers.ts`
|
|
73
|
-
*
|
|
69
|
+
* A `<` that could open something a Markdown renderer reads as raw HTML or an
|
|
70
|
+
* autolink: a tag-name start (a letter, `/`, `!`, or `?`), then a body carrying
|
|
71
|
+
* no further angle bracket, then a closing `>`. Looser than `MARKUP_TAG_RE` in
|
|
72
|
+
* `text-helpers.ts` on purpose: that pattern deletes what it matches, so its tag
|
|
73
|
+
* name ends only at whitespace, `/`, or `>` to keep PubMed's `<or= 20 or > 20`
|
|
74
|
+
* as text, while an escape here costs only a backslash the renderer hides, so
|
|
75
|
+
* that `<` is escaped. The two agree that a `<` with no `>` after it (`n<N`) or
|
|
76
|
+
* one followed by a digit (`P<0.001`) cannot open a tag and is left alone. An
|
|
77
|
+
* entity-encoded tag (`<i>`), which `MARKUP_TAG_RE` also strips, is literal
|
|
78
|
+
* text to a renderer and is not escaped here.
|
|
74
79
|
*/
|
|
75
80
|
const HTML_TAG_OPENER_RE = /<(?=[A-Za-z/!?][^<>]*>)/g;
|
|
76
81
|
/** A character that makes an adjacent `_` intraword, where it cannot emphasize. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_text.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,IAAI,oBAAoB,IAAI,IAAI,IAAI,mBAAmB,CAAC;IAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,KAAa,EAAW,EAAE,CAC5D,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAEzC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAa;IAC7D,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IAE3C,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;YAAE,GAAG,IAAI,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,IAAI,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IACvD,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;AACrD,CAAC;AAED,2EAA2E;AAC3E,MAAM,aAAa,GAAG,sBAAsB,CAAC;AAC7C
|
|
1
|
+
{"version":3,"file":"_text.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ export declare const fetchArticlesTool: import("@cyanheads/mcp-ts-core").ToolDef
|
|
|
11
11
|
pmids: z.ZodArray<z.ZodString>;
|
|
12
12
|
includeMesh: z.ZodDefault<z.ZodBoolean>;
|
|
13
13
|
includeGrants: z.ZodDefault<z.ZodBoolean>;
|
|
14
|
+
includeInvestigators: z.ZodDefault<z.ZodBoolean>;
|
|
14
15
|
maxResponseCharacters: z.ZodOptional<z.ZodNumber>;
|
|
15
16
|
}, z.core.$strip>, z.ZodObject<{
|
|
16
17
|
articles: z.ZodArray<z.ZodObject<{
|
|
@@ -31,6 +32,13 @@ export declare const fetchArticlesTool: import("@cyanheads/mcp-ts-core").ToolDef
|
|
|
31
32
|
affiliationIndices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
32
33
|
orcid: z.ZodOptional<z.ZodString>;
|
|
33
34
|
}, z.core.$strip>>>;
|
|
35
|
+
investigators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
36
|
+
lastName: z.ZodOptional<z.ZodString>;
|
|
37
|
+
firstName: z.ZodOptional<z.ZodString>;
|
|
38
|
+
initials: z.ZodOptional<z.ZodString>;
|
|
39
|
+
affiliationIndices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
40
|
+
orcid: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>>>;
|
|
34
42
|
journalInfo: z.ZodOptional<z.ZodObject<{
|
|
35
43
|
title: z.ZodOptional<z.ZodString>;
|
|
36
44
|
isoAbbreviation: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-articles.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-articles.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-articles.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-articles.tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAoWjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAWhB,yBAAyB;;mBAE3B,sEAAsE;uBAE1E,qFAAqF;;;;EA6T3F,CAAC"}
|
|
@@ -19,7 +19,10 @@ const AuthorSchema = z
|
|
|
19
19
|
lastName: z.string().optional().describe('Last name'),
|
|
20
20
|
firstName: z.string().optional().describe('First/given name'),
|
|
21
21
|
initials: z.string().optional().describe('Author initials'),
|
|
22
|
-
collectiveName: z
|
|
22
|
+
collectiveName: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Group/collective author name. NCBI may list the members of the group separately; set `includeInvestigators` to return them as `investigators`.'),
|
|
23
26
|
affiliationIndices: z
|
|
24
27
|
.array(z.number())
|
|
25
28
|
.optional()
|
|
@@ -27,6 +30,18 @@ const AuthorSchema = z
|
|
|
27
30
|
orcid: z.string().optional().describe('ORCID identifier'),
|
|
28
31
|
})
|
|
29
32
|
.describe('Author record');
|
|
33
|
+
const InvestigatorSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
lastName: z.string().optional().describe('Last name'),
|
|
36
|
+
firstName: z.string().optional().describe('First/given name'),
|
|
37
|
+
initials: z.string().optional().describe('Initials'),
|
|
38
|
+
affiliationIndices: z
|
|
39
|
+
.array(z.number())
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Indices into the top-level affiliations array, where an investigator's affiliations follow the authors'"),
|
|
42
|
+
orcid: z.string().optional().describe('ORCID identifier'),
|
|
43
|
+
})
|
|
44
|
+
.describe("One member of a collective author, from NCBI's `InvestigatorList`");
|
|
30
45
|
const JournalPublicationDateSchema = z
|
|
31
46
|
.object({
|
|
32
47
|
year: z.string().optional().describe('Publication year'),
|
|
@@ -149,7 +164,10 @@ const MeshTermSchema = z
|
|
|
149
164
|
const GrantSchema = z
|
|
150
165
|
.object({
|
|
151
166
|
grantId: z.string().optional().describe('Grant identifier'),
|
|
152
|
-
acronym: z
|
|
167
|
+
acronym: z
|
|
168
|
+
.string()
|
|
169
|
+
.optional()
|
|
170
|
+
.describe('Acronym of the funding institute or agency from `Grant/Acronym` (e.g. "EY" for the National Eye Institute) — it names the funder, never the grant, so it is no substitute for `grantId`.'),
|
|
153
171
|
agency: z.string().optional().describe('Funding agency'),
|
|
154
172
|
country: z.string().optional().describe('Agency country'),
|
|
155
173
|
})
|
|
@@ -191,11 +209,18 @@ const FetchedArticleSchema = z
|
|
|
191
209
|
.optional()
|
|
192
210
|
.describe('Article title — the chapter title on a `book-chapter`, and the book title on a `book` record, where it repeats `book.title`.'),
|
|
193
211
|
abstractText: z.string().optional().describe('Abstract text'),
|
|
194
|
-
affiliations: z
|
|
212
|
+
affiliations: z
|
|
213
|
+
.array(z.string())
|
|
214
|
+
.optional()
|
|
215
|
+
.describe("Deduplicated affiliations, referenced by `affiliationIndices`: the authors' entries first, then any that only `investigators` carry"),
|
|
195
216
|
authors: z
|
|
196
217
|
.array(AuthorSchema)
|
|
197
218
|
.optional()
|
|
198
219
|
.describe("Author list. On a `book-chapter` these are the chapter's own authors, never the book's editors, which are in `book.editors`. Empty on a Bookshelf record that credits neither."),
|
|
220
|
+
investigators: z
|
|
221
|
+
.array(InvestigatorSchema)
|
|
222
|
+
.optional()
|
|
223
|
+
.describe("Members of the article's collective author, from NCBI's `InvestigatorList`: every list flattened in NCBI's order, uncapped. They are not authors, and no citation style lists them. Present only when `includeInvestigators` is set and the record lists any; never on `book-chapter` or `book` records."),
|
|
199
224
|
journalInfo: JournalInfoSchema.optional(),
|
|
200
225
|
book: BookInfoSchema.optional(),
|
|
201
226
|
doi: z
|
|
@@ -213,7 +238,10 @@ const FetchedArticleSchema = z
|
|
|
213
238
|
keywords: z.array(z.string()).optional().describe('Keywords'),
|
|
214
239
|
meshTerms: z.array(MeshTermSchema).optional().describe('MeSH terms'),
|
|
215
240
|
grantList: z.array(GrantSchema).optional().describe('Grant information'),
|
|
216
|
-
articleDates: z
|
|
241
|
+
articleDates: z
|
|
242
|
+
.array(ArticleDateSchema)
|
|
243
|
+
.optional()
|
|
244
|
+
.describe("Dates of the record itself, each named by `dateType`. On a journal article, PubMed's `ArticleDate` entries (`Electronic` — the electronic publication date). On a Bookshelf record, its own `ContributionDate` (when the chapter was contributed) and `DateRevised` (when it was last updated), distinct from the containing book's dates in `book`; `pubmed_format_citations` cites a chapter by `DateRevised` when present, otherwise `ContributionDate`. Absent when NCBI supplies none."),
|
|
217
245
|
})
|
|
218
246
|
.describe('Parsed PubMed article');
|
|
219
247
|
const DeferredSchema = z
|
|
@@ -236,7 +264,7 @@ const DeferredSchema = z
|
|
|
236
264
|
})
|
|
237
265
|
.describe('Continuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.');
|
|
238
266
|
export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
239
|
-
description: 'Fetch full article metadata by PubMed IDs. Returns detailed article information including abstract, authors, journal, MeSH terms, and linked retraction, erratum, and comment notices. Set `maxResponseCharacters` to bound the whole response: articles past the ceiling are deferred whole and listed in `deferred.ids` for a follow-up call.',
|
|
267
|
+
description: 'Fetch full article metadata by PubMed IDs. Returns detailed article information including abstract, authors, journal, MeSH terms, and linked retraction, erratum, and comment notices; grants and the members of a collective author are opt-in (`includeGrants`, `includeInvestigators`). Set `maxResponseCharacters` to bound the whole response: articles past the ceiling are deferred whole and listed in `deferred.ids` for a follow-up call.',
|
|
240
268
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
241
269
|
_meta: conceptMeta([SCHEMA_SCHOLARLY_ARTICLE, EDAM_DATA_RETRIEVAL, EDAM_PUBMED_ID]),
|
|
242
270
|
sourceUrl: 'https://github.com/cyanheads/pubmed-mcp-server/blob/main/src/mcp-server/tools/definitions/fetch-articles.tool.ts',
|
|
@@ -255,11 +283,14 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
255
283
|
pmids: z.array(pmidStringSchema).min(1).max(200).describe('PubMed IDs to fetch'),
|
|
256
284
|
includeMesh: z.boolean().default(true).describe('Include MeSH terms'),
|
|
257
285
|
includeGrants: z.boolean().default(false).describe('Include grant information'),
|
|
286
|
+
includeInvestigators: z
|
|
287
|
+
.boolean()
|
|
288
|
+
.default(false)
|
|
289
|
+
.describe('Include `investigators`: the members NCBI lists for a collective author such as "TGU Investigators". Off by default because the list is unbounded: consortium trials name thousands, at about 60 characters of JSON each — PMID 33933206 lists 6,349, adding about 372,000 characters to its record. Under `maxResponseCharacters` they count toward the size of their article, which is deferred whole when it no longer fits.'),
|
|
258
290
|
maxResponseCharacters: z
|
|
259
291
|
.number()
|
|
260
292
|
.int()
|
|
261
293
|
.min(1)
|
|
262
|
-
.max(1_000_000)
|
|
263
294
|
.optional()
|
|
264
295
|
.describe('Opt-in ceiling for the whole response, in characters. Each article is measured as the JSON record it is returned as — title, abstract, authors, journal, MeSH terms, grants, identifiers, every field it carries. Articles are kept in response order until the next one would cross the ceiling; that article and the rest are deferred whole (never partially populated) and listed in `deferred.ids`. Response envelope fields — counts, `unavailablePmids`, `deferred` itself — are not counted. Omit to return every resolved article.'),
|
|
265
296
|
}),
|
|
@@ -303,6 +334,7 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
303
334
|
const articles = parseArticleSet(xmlData.PubmedArticleSet, {
|
|
304
335
|
includeMesh: input.includeMesh,
|
|
305
336
|
includeGrants: input.includeGrants,
|
|
337
|
+
includeInvestigators: input.includeInvestigators,
|
|
306
338
|
}).map((parsed) => ({
|
|
307
339
|
...parsed,
|
|
308
340
|
pubmedUrl: `https://pubmed.ncbi.nlm.nih.gov/${parsed.pmid}/`,
|
|
@@ -373,6 +405,12 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
373
405
|
lines.push(`- ${formatAuthor(au)}`);
|
|
374
406
|
}
|
|
375
407
|
}
|
|
408
|
+
if (a.investigators?.length) {
|
|
409
|
+
lines.push(`\n**Investigators (${a.investigators.length}):**`);
|
|
410
|
+
for (const inv of a.investigators) {
|
|
411
|
+
lines.push(`- ${formatAuthor(inv)}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
376
414
|
if (a.affiliations?.length) {
|
|
377
415
|
lines.push(`\n**Affiliations:**`);
|
|
378
416
|
for (const [i, aff] of a.affiliations.entries()) {
|
|
@@ -429,10 +467,11 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
429
467
|
if (bk.publisherLocation)
|
|
430
468
|
bookLines.push(`**Publisher Location:** ${bk.publisherLocation}`);
|
|
431
469
|
// A book published over several years carries a closed range
|
|
432
|
-
// (GeneReviews 1993–2026)
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
|
|
470
|
+
// (GeneReviews 1993–2026), one still being updated an open one
|
|
471
|
+
// (Endotext 2000–); otherwise the single publication year. A publication
|
|
472
|
+
// year that differs from the range's start is kept beside it. (#189)
|
|
473
|
+
const range = bk.beginningDate && bk.beginningDate !== bk.endingDate
|
|
474
|
+
? `${bk.beginningDate}–${bk.endingDate ?? ''}`
|
|
436
475
|
: undefined;
|
|
437
476
|
const bookDate = range && bk.pubDate && bk.pubDate !== bk.beginningDate
|
|
438
477
|
? `${bk.pubDate} (${range})`
|
|
@@ -509,8 +548,13 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
509
548
|
if (a.grantList?.length) {
|
|
510
549
|
lines.push(`\n#### Grants`);
|
|
511
550
|
for (const g of a.grantList) {
|
|
512
|
-
|
|
513
|
-
|
|
551
|
+
// The acronym names the funder, never the grant: without a GrantID it
|
|
552
|
+
// follows the agency instead of taking the ID's slot. (#214)
|
|
553
|
+
const grantId = g.grantId && g.acronym ? `${g.grantId} (${g.acronym})` : g.grantId;
|
|
554
|
+
const agency = !g.grantId && g.acronym
|
|
555
|
+
? [g.agency, `(${g.acronym})`].filter(Boolean).join(' ')
|
|
556
|
+
: g.agency;
|
|
557
|
+
const parts = [grantId, agency, g.country].filter(Boolean);
|
|
514
558
|
lines.push(`- ${parts.join(' — ')}`);
|
|
515
559
|
}
|
|
516
560
|
}
|
|
@@ -526,7 +570,7 @@ export const fetchArticlesTool = tool('pubmed_fetch_articles', {
|
|
|
526
570
|
*/
|
|
527
571
|
function buildDeferralNotice(deferred) {
|
|
528
572
|
const spent = deferred.returnedCharacters === 0
|
|
529
|
-
? `
|
|
573
|
+
? `The first article alone exceeds the requested maxResponseCharacters of ${deferred.maxResponseCharacters}, so none were returned.`
|
|
530
574
|
: `Response character budget reached: ${deferred.returnedCharacters} of ${deferred.maxResponseCharacters} characters returned.`;
|
|
531
575
|
return `${spent} ${deferred.deferredCount} resolved article(s) were deferred whole: ${deferred.ids.join(', ')}. Re-call pubmed_fetch_articles with those PMIDs to retrieve them, or raise maxResponseCharacters to at least ${deferred.nextDeferredCharacters} — the size of the next deferred article.`;
|
|
532
576
|
}
|
|
@@ -556,8 +600,14 @@ function formatPublicationDate(pd) {
|
|
|
556
600
|
return `${pd.medlineDate} (${ymd})`;
|
|
557
601
|
return pd.medlineDate || ymd || undefined;
|
|
558
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* `ContributionDate 2025-07-03`. A one-digit month or day is zero-padded for
|
|
605
|
+
* display only — the parser reads `<Month>07</Month>` as 7, and Bookshelf writes
|
|
606
|
+
* `<Month>7</Month>` outright — so structuredContent keeps the value as parsed.
|
|
607
|
+
*/
|
|
559
608
|
function formatArticleDate(ad) {
|
|
560
|
-
const
|
|
609
|
+
const pad = (part) => part?.length === 1 && part >= '0' && part <= '9' ? `0${part}` : part;
|
|
610
|
+
const datePart = [ad.year, pad(ad.month), pad(ad.day)].filter(Boolean).join('-');
|
|
561
611
|
return ad.dateType ? `${ad.dateType} ${datePart}` : datePart;
|
|
562
612
|
}
|
|
563
613
|
//# sourceMappingURL=fetch-articles.tool.js.map
|