Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms.
One address for any MCP client. No sign-in needed on this server.
10 read1 other
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://pubmed.caseyjhand.com/mcp
Biomedical literature from PubMed, PubMed Central, and Europe PMC. Search it, fetch metadata and full text, resolve identifiers and partial citations, format references, and ground queries in MeSH vocabulary. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
pubmed_search_articles | Search PubMed with full query syntax, structured filters, date ranges, and optional summaries |
pubmed_fetch_articles | Fetch article metadata by PMID: abstract, authors, journal, MeSH terms, databank accessions such as trial registration IDs, grants, a collective author's investigators on request, linked retraction and correction notices |
pubmed_fetch_fulltext | Fetch full text by PMID, PMCID, or DOI, falling back from PMC to Europe PMC to Unpaywall |
pubmed_europepmc_search | Search Europe PMC for preprints, patents, Agricola, and open-access records PubMed doesn't carry |
pubmed_europepmc_fetch | Fetch complete Europe PMC records, including the untruncated abstract, as records: [{ source, epmcId }] |
pubmed_fetch_citation_metrics | Fetch NIH iCite citation metrics by PMID: Relative Citation Ratio, NIH percentile, citation counts and rates |
pubmed_format_citations | Format citations in APA 7th, MLA 9th, BibTeX, RIS, or Vancouver (ICMJE/NLM), with linked retraction and correction notices |
pubmed_find_related | Find similar articles, citing articles, or references for a PMID |
pubmed_spell_check | Correct a misspelled PubMed query via NCBI ESpell |
pubmed_lookup_mesh | Look up MeSH headings with tree numbers, scope notes, and entry terms |
pubmed_lookup_citation | Resolve partial references, up to 25 at a time, to PMIDs via ECitMatch |
pubmed_convert_ids | Convert between DOI, PMID, and PMCID via the PMC ID Converter |
| Resource | Description |
|---|---|
pubmed://database/info | PubMed database metadata via EInfo (field list, record count, last update) |
| Prompt | Description |
|---|---|
research_plan | Generate a structured four-phase biomedical research plan |
pubmed_search_articles toolmaxResults: 0 for the match count alone; summaryCount adds briefs for up to 50 hitstotalCount, the effectiveQuery PubMed ran, and the appliedFilterspubmed_fetch_articles toolunavailablePmidsincludeGrants, and the members of a collective author (its InvestigatorList, thousands on some consortium trials) via includeInvestigatorspubmed_format_citations, directly under its titledataBanks lists the record's trial registrations and data deposits (ClinicalTrials.gov NCT IDs, GenBank and PDB accessions, Dryad DOIs) verbatim; search pubmed_search_articles for <accession>[si] to find other records citing onerecordType separates journal articles from Bookshelf chapters and books; opt-in maxResponseCharacters defers overflow to deferred.idspubmed_fetch_fulltext toolpmcids, 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 answeredsource: "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 bodylicenses their PMC JATS statesreason and triedTiers; sections, maxSections, maxCharacters, overflowMode, and maxResponseCharacters bound the response, truncation reports what was cut, and sectionCap names the sections maxSections cutpubmed_europepmc_search toolPPR), patents (PAT), and Agricola (AGR) to MED and PMC; defaults to MED, PMC, PPRcursorMark / nextCursorMark, up to 100 per page; abstracts arrive as 400-character snippetssort of comma-separated <field> asc|desc keys; relevance is the default order, and a sort Europe PMC cannot use fails as europepmc_invalid_inputlicense and hasFullTextXmlEUROPEPMC_ENABLED=falsepubmed_europepmc_fetch toolrecords: [{ source, epmcId }], each pair copied from a search hit, with the full abstract, license, and hasFullTextXml; unresolved ids land in notFound; opt-in maxResponseCharacters defers overflow to deferred.recordsEUROPEPMC_ENABLED=falsepubmed_fetch_citation_metrics tooloverLimit, PMIDs NIH iCite has no record for in unavailablePmidspubmed_fetch_articles for noticespubmed_format_citations toolapa, mla, bibtex, ris, and vancouver1637-1645 → 1637-45); BibTeX issn and RIS SN carry the journal's linking ISSN, and RIS TI drops its trailing periodnotices and are shown ahead of its citations; Vancouver adds a Retraction in: / Erratum in: note and APA a retraction parentheticalpubmed_find_related toolsimilar, cited_by, or references, up to 50 per page with offset paginationall_providers_failed if none canpubmed_spell_check tooloriginal, corrected, and hasSuggestion; corrections land in place, so field tags, operators, spacing, and gene symbols stay as written, and a single letter such as an author initial is never swapped for anotheraged[mh], zhang y[au], free full text[sb]) are kept as written; free-text fields ([tiab], [ti], [tw]) are still correctedpubmed_search_articles with correctedpubmed_lookup_mesh toolnextOffsetmeshId, e.g. D000072657) beside the Entrez UID; includeDetails (default on) adds tree numbers, scope notes, and entry termspubmed_lookup_citation toolmatched, not_found, or ambiguous, with recovery detailpubmed_convert_ids toolidType (doi, pmid, pmcid); only PMC-indexed articles resolvepubmed://database/info resourcepubmed database, returned as application/jsoncount, lastUpdate, and fields[], whose names are the search tags pubmed_search_articles acceptsresearch_plan prompttitle, goal, and keywords required; organism and includeAgentPrompts optionalincludeAgentPrompts: "true" adds an agent-guidance block under each of its nine sub-steps, two of which point at tools: the literature review names pubmed_search_articles and pubmed_lookup_mesh, and the interpretation step names pubmed_search_articlesBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
PubMed-specific:
pubmed_fetch_fulltext also matches DOIs case-insensitively and PMC IDs with or without the PMC prefixAgent-friendly output:
source: "pmc" | "unpaywall", typed unavailable reasons, viaSource and triedTiers fields, so callers branch on data, not string parsingConnect to the hosted instance, no install:
claude mcp add --transport http pubmed-mcp-server https://pubmed.caseyjhand.com/mcp
Or run it locally:
claude mcp add pubmed-mcp-server -- npx -y @cyanheads/pubmed-mcp-server
For other clients, add this to the MCP config file:
{
"mcpServers": {
"pubmed-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/pubmed-mcp-server"]
}
}
}
An NCBI API key in NCBI_API_KEY raises the rate limit from 3 to 10 requests per second (-e NCBI_API_KEY=your-key, or an env entry). For Docker or a source build, see Running the server.
| Variable | Description | Default |
|---|---|---|
NCBI_API_KEY | NCBI API key; raises the rate limit from 3 to 10 req/s. | none |
NCBI_ADMIN_EMAIL | Contact email sent with NCBI requests, as NCBI recommends. | none |
NCBI_REQUEST_DELAY_MS | Minimum gap between NCBI request starts, in ms. | 400 (100 with a key) |
NCBI_MAX_CONCURRENT | Max concurrent in-flight NCBI requests. | 8 |
NCBI_MAX_RETRIES | Retry attempts for failed NCBI requests. | 6 |
NCBI_TIMEOUT_MS | Per-request HTTP timeout, in ms. | 30000 |
NCBI_TOTAL_DEADLINE_MS | Deadline for one NCBI call across queue wait, retries, and backoff, in ms. A call that can't start in time is rejected at once. | 60000 |
UNPAYWALL_EMAIL | Contact email for Unpaywall. Setting it enables the Unpaywall tier of pubmed_fetch_fulltext for non-PMC DOIs. | none |
UNPAYWALL_TIMEOUT_MS | Per-request timeout for Unpaywall lookups and content fetches, in ms. | 20000 |
EUROPEPMC_ENABLED | Set false to disable every Europe PMC call: both Europe PMC tools go unregistered, pubmed_fetch_fulltext skips that tier, and pubmed_find_related skips that fallback. | true |
EUROPEPMC_EMAIL | Optional contact email sent with Europe PMC requests. | none |
EUROPEPMC_REQUEST_DELAY_MS | Minimum gap between Europe PMC request starts, in ms. | 200 |
EUROPEPMC_MAX_RETRIES | Retry attempts for failed Europe PMC requests. | 3 |
EUROPEPMC_TIMEOUT_MS | Per-request timeout for Europe PMC calls, in ms. | 20000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for every server setting and the common framework overrides.
Requires Bun v1.4.0 or higher (or Node.js v24+).
Clone and install:
git clone https://github.com/cyanheads/pubmed-mcp-server.git
cd pubmed-mcp-server && bun install
cp .env.example .env # set NCBI_API_KEY, NCBI_ADMIN_EMAIL, and UNPAYWALL_EMAIL as needed
Build and run the production version:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more
bun run test # Runs the test suite
docker run --rm -p 3010:3010 ghcr.io/cyanheads/pubmed-mcp-server:latest
The image defaults to HTTP transport, stateless session mode, and logs to /var/log/pubmed-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/pubmed-mcp-server:latest"]. To build locally, docker build -t pubmed-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/mcp-server/tools | Tool definitions (*.tool.ts). Twelve tools across PubMed, PMC, Europe PMC, and NIH iCite. |
src/mcp-server/resources | Resource definitions. Database info resource. |
src/mcp-server/prompts | Prompt definitions. Research plan prompt. |
src/services/ncbi | NCBI E-utilities service layer — API client, queue, parser, formatter. |
src/services/europe-pmc | Europe PMC service — search + fullTextXML JATS retrieval. Reuses the NCBI JATS parser. |
src/services/unpaywall | Unpaywall service — DOI → OA location resolution and content fetch (HTML/PDF). |
src/services/openalex | OpenAlex service — last-resort provider for pubmed_find_related. |
src/config | Server-specific environment variable parsing and validation with Zod. |
tests/ | Unit and integration tests, mirroring the src/ structure. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagecreateApp() arrays in src/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.