Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Convert between article identifiers (DOI, PMID, PMCID). Accepts up to 50 IDs of a single type per request. Only resolves articles indexed in PubMed Central — for articles not in PMC, use pubmed_search_articles instead.
Article identifiers to convert — one identifier per element, all of the same type. Each element is checked against `idType` before the request: `doi` starts with "10." and carries a "/" ("10.1093/nar/gks1195"); `pmid` is digits ("23193287"); `pmcid` is digits with an optional "PMC" prefix ("PMC3531190" or "3531190"). No element may contain a comma or whitespace — a packed value like "23193287,37952131" is rejected, so split it across elements.
The type of IDs being submitted. Required so the API can unambiguously resolve them.
Convert between article identifiers (DOI, PMID, PMCID). Accepts up to 50 IDs of a single type per request. Only resolves articles indexed in PubMed Central — for articles not in PMC, use pubmed_search_articles instead.
| Argument | Type | Description |
|---|---|---|
| ids* | array | Article identifiers to convert — one identifier per element, all of the same type. Each element is checked against `idType` before the request: `doi` starts with "10." and carries a "/" ("10.1093/nar/gks1195"); `pmid` is digits ("23193287"); `pmcid` is digits with an optional "PMC" prefix ("PMC3531190" or "3531190"). No element may contain a comma or whitespace — a packed value like "23193287,37952131" is rejected, so split it across elements. |
| idType* | string | The type of IDs being submitted. Required so the API can unambiguously resolve them. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"ids",
"idType"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 50,
"minItems": 1,
"description": "Article identifiers to convert — one identifier per element, all of the same type. Each element is checked against `idType` before the request: `doi` starts with \"10.\" and carries a \"/\" (\"10.1093/nar/gks1195\"); `pmid` is digits (\"23193287\"); `pmcid` is digits with an optional \"PMC\" prefix (\"PMC3531190\" or \"3531190\"). No element may contain a comma or whitespace — a packed value like \"23193287,37952131\" is rejected, so split it across elements."
},
"idType": {
"enum": [
"pmcid",
"pmid",
"doi"
],
"type": "string",
"description": "The type of IDs being submitted. Required so the API can unambiguously resolve them."
}
},
"additionalProperties": false
}Fetch complete Europe PMC records — including the full, untruncated abstract — addressed as `records: [{ source, epmcId }]`, an array even for one record. Pairs with `pubmed_europepmc_search`, which returns bounded `abstractSnippet` values and flags cut ones with `abstractTruncated: true`; copy each hit's `source` and `epmcId` into `records` here to read the whole abstract. This is the retrieval path for preprint (`PPR`), patent (`PAT`), and Agricola (`AGR`) records, which frequently carry no PMID and no DOI, so `pubmed_fetch_articles` and `pubmed_fetch_fulltext` cannot address them. Up to 25 records per call. Set `maxResponseCharacters` to bound the whole response: records past the ceiling are deferred whole and listed in `deferred.records` for a follow-up call.
| Argument | Type | Description |
|---|---|---|
| records* | array | Records to retrieve, each addressed by the `source` and `epmcId` of a `pubmed_europepmc_search` hit. The whole batch resolves in one Europe PMC request. |
| maxResponseCharacters | integer | Opt-in ceiling for the whole response, in characters. Each record is measured as the JSON record it is returned as — title, authors, journal, abstract, identifiers, every field it carries. Records are kept in the order Europe PMC returned them until the next one would cross the ceiling; that record and the rest are deferred whole (never partially populated) and listed in `deferred.records`. Response envelope fields — `notFound`, `deferred` itself — are not counted. Omit to return every resolved record. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"records"
],
"properties": {
"records": {
"type": "array",
"items": {
"type": "object",
"required": [
"source",
"epmcId"
],
"properties": {
"epmcId": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$",
"maxLength": 64,
"description": "Europe PMC's own record id within that source. Copy it from the search hit's `epmcId` — for `MED` records this is the PMID; for the other sources it is an EPMC-native accession."
},
"source": {
"enum": [
"MED",
"PMC",
"PPR",
"PAT",
"AGR"
],
"type": "string",
"description": "Europe PMC source corpus — `MED` (PubMed), `PMC` (PubMed Central), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola). Copy it from the search hit's `source`. `PMC` paired with a PMCID resolves whether or not the article is also indexed in PubMed, and a PubMed-indexed one comes back as its canonical `MED` record carrying that PMCID in `pmcId`."
}
},
"description": "One record address: the Europe PMC source corpus plus its id within that corpus"
},
"maxItems": 25,
"minItems": 1,
"description": "Records to retrieve, each addressed by the `source` and `epmcId` of a `pubmed_europepmc_search` hit. The whole batch resolves in one Europe PMC request."
},
"maxResponseCharacters": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Opt-in ceiling for the whole response, in characters. Each record is measured as the JSON record it is returned as — title, authors, journal, abstract, identifiers, every field it carries. Records are kept in the order Europe PMC returned them until the next one would cross the ceiling; that record and the rest are deferred whole (never partially populated) and listed in `deferred.records`. Response envelope fields — `notFound`, `deferred` itself — are not counted. Omit to return every resolved record."
}
},
"additionalProperties": false
}Search Europe PMC, a broad open-access biomedical corpus. Surfaces preprints (`source: PPR`), patents (`source: PAT`), Agricola (`source: AGR`), plus everything in PubMed (`MED`) and PMC. Use when additional coverage is needed — preprints and EPMC-only OA records are the typical recovery. Paginate via `cursorMark`. Defaults to `MED`, `PMC`, and `PPR`; pass `sources` to include `PAT` / `AGR`. Abstracts arrive as a bounded `abstractSnippet` with `abstractTruncated` marking the cut ones — pass a hit to `pubmed_europepmc_fetch` as `records: [{ source, epmcId }]` for the complete abstract.
| Argument | Type | Description |
|---|---|---|
| sort | string | Optional EPMC sort: `<field> asc|desc`, or several comma-separated keys applied in order (`PUB_YEAR desc, CITED desc`). Documented sortable fields: `P_PDATE_D` (publication date), `CITED` (citation count), `AUTH_FIRST` (first author surname), `PUB_YEAR` (publication year), `FIRST_IDATE_D` (date first indexed in Europe PMC). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking; `relevance` or `relevance desc` is sent as no sort. Field and direction match case-insensitively. Each key must be one field plus `asc` or `desc`: a key without a direction, an empty key, or an extra word fails with `europepmc_invalid_input` before the request. A field outside the documented set may be honored, silently ignored, or refused — a sort Europe PMC refuses fails with `europepmc_invalid_input` naming it, and an empty Europe PMC response to a sort with an undocumented field fails the same way, even when Europe PMC honors the field. Note: `P_PDATE_D` is ignored for preprint-only (`sources: ["PPR"]`) result sets — preprints have no populated publication date, so use `PUB_YEAR desc` to order preprints by date. |
| query* | string | Europe PMC search query. Supports field tokens like `AUTH:"<name>"`, `JOURNAL:"<title>"`, `TITLE:"<words>"`, `PUB_YEAR:[2020 TO 2024]`, `DOI:"..."`, `EXT_ID:<pmid> AND SRC:MED`, `PMCID:PMC<digits>`. Identifier tokens may be quoted or unquoted — this tool wraps every query with its `sources` filter, and Europe PMC honors a quoted identifier inside that wrapper. A PubMed-indexed article resolves under `SRC:MED`, not `SRC:PMC`, whichever identifier is used. Free text is matched broadly across abstract/title/keywords. A query with no search term — blank once HTML entities are decoded and markup, parentheses, and invisible characters are disregarded, such as `()` or `<b></b>` — is rejected; any other query is sent as written. |
Fetch full article metadata by PubMed IDs. Returns detailed article information including abstract, authors, journal, MeSH terms, linked retraction, erratum, and comment notices, and databank accessions such as trial registration IDs; 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.
| Argument | Type | Description |
|---|---|---|
| pmids* | array | PubMed IDs to fetch |
| includeMesh | boolean | Include MeSH terms |
| includeGrants | boolean | Include grant information |
| includeInvestigators | boolean | 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. |
| maxResponseCharacters | integer | 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. |
Fetch full-text articles from PubMed Central as structured sections — the body, then back matter such as acknowledgments, declarations, and appendices — with tables and references. When PMC misses, transparently falls back to Europe PMC `fullTextXML` (structured JATS for records with a PMC counterpart), then to Unpaywall — publisher-hosted or institutional open-access copies as HTML-as-Markdown or PDF-as-text. Name articles by `pmcids` (PMC IDs directly), `pmids` (PubMed IDs, auto-resolved), `dois` (DOIs, auto-resolved to PMC via the ID Converter; preprints and EPMC-only OA fall through to the Europe PMC and Unpaywall layers), or any mix of the three — up to 50 distinct identifiers; the first 10 are fetched per call, the rest returned in `overLimit`; an article several of them name is fetched and returned once. Two independent character controls: `maxCharacters` caps body text per article, `maxResponseCharacters` caps the whole response and defers articles past the ceiling whole, listing them in `deferred.ids` for a follow-up call.
| Argument | Type | Description |
|---|---|---|
| dois | array | DOIs to resolve (e.g. ["10.21203/rs.3.rs-9010375/v1"]), one per element. Combinable with `pmcids` and `pmids` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. Resolved to a PMCID via the PMC ID Converter and returned as structured JATS when the article is in PMC; DOIs with no PMC counterpart (preprints, EPMC-only OA) fall through to Europe PMC, then Unpaywall, when those layers are enabled. |
| pmids | array | PubMed IDs. Combinable with `pmcids` and `dois` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. Articles in PMC are returned as structured JATS; articles not in PMC fall through to Europe PMC (when EPMC has a `fullTextXML`), then to Unpaywall when `UNPAYWALL_EMAIL` is set and a DOI is available. |
Find articles related to a source article — similar content (similar), articles citing this one (cited_by), or articles this one cites (references). Uses NCBI ELink as the primary source; falls back to Europe PMC then OpenAlex when NCBI is unavailable.
| Argument | Type | Description |
|---|---|---|
| pmid* | string | Source PubMed ID |
| offset | integer | Result offset for pagination (0-based); page through results by incrementing by maxResults |
| maxResults | integer | Maximum related articles |
| relationship | string | Relationship type: similar (content-based), cited_by (articles citing this one), references (articles this one cites) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"pmid"
],
"properties": {
"pmid": {
"type": "string",
"pattern": "^\\d+$",
"description": "Source PubMed ID"
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Result offset for pagination (0-based); page through results by incrementing by maxResults"
},
"maxResults": {
"type": "integer",
"default": 10,
"maximum": 50,
"minimum": 1,
"description": "Maximum related articles"
},
"relationship": {
"enum": [
"similar",
"cited_by",
"references"
],
"type": "string",
"default": "similar",
"description": "Relationship type: similar (content-based), cited_by (articles citing this one), references (articles this one cites)"
}
},
"additionalProperties": false
}Get formatted citations for PubMed articles in one or more formats (apa, mla, bibtex, ris, vancouver). Pass a single format as a string or multiple as an array. Retractions, errata, and expressions of concern NCBI links to an article come back in `notices`; Vancouver and APA also cite a retraction in the reference itself.
| Argument | Type | Description |
|---|---|---|
| pmids* | array | PubMed IDs to cite |
| format | any | Citation format(s) to generate — single style as a string or multiple as an array. Allowed values: apa, mla, bibtex, ris, vancouver. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"pmids"
],
"properties": {
"pmids": {
"type": "array",
"items": {
"type": "string",
"pattern": "^\\d+$"
},
"maxItems": 50,
"minItems": 1,
"description": "PubMed IDs to cite"
},
"format": {
"anyOf": [
{
"enum": [
"apa",
"mla",
"bibtex",
"ris",
"vancouver"
],
"type": "string",
"description": "Single citation style. One of: apa, mla, bibtex, ris, vancouver."
},
{
"type": "array",
"items": {
"enum": [
"apa",
"mla",
"bibtex",
"ris",
"vancouver"
],
"type": "string"
},
"minItems": 1,
"description": "Multiple citation styles to generate. Each entry: apa, mla, bibtex, ris, or vancouver."
}
],
"default": "apa",
"description": "Citation format(s) to generate — single style as a string or multiple as an array. Allowed values: apa, mla, bibtex, ris, vancouver."
}
},
"additionalProperties": false
}Look up PubMed IDs from partial bibliographic citations. Useful when you have a reference (journal, year, volume, page, author) and need the PMID — deterministic citation matching, more reliable than free-text search for structured references. Each citation must include at least journal or a four-digit year (ECitMatch primary-keys on journal+volume+page; author-only or volume-only inputs guarantee no match); more fields = better match accuracy.
| Argument | Type | Description |
|---|---|---|
| citations* | array | Citations to look up, 1–25, each matched independently. More fields = better match accuracy. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"citations"
],
"properties": {
"citations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "Arbitrary label to track this citation in results. Auto-assigned if omitted. Echoed back unchanged and never sent to NCBI, so any character is accepted here."
},
"year": {
"type": "string",
"pattern": "^[ \\t]*\\d{4}[ \\t]*$",
"description": "Publication year as four digits (e.g., \"1991\")."
},
"volume": {
"type": "string",
"pattern": "^[^|\\r\\n]*$",
"description": "Volume number. Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
},
"journal": {
"type": "string",
"pattern": "^[^|\\r\\n]*$",
"description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
},
"firstPage": {
"type": "string",
"pattern": "^[^|\\r\\n]*$",
"description": "First page number. Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
},
"authorName": {
"type": "string",
"pattern": "^[^|\\r\\n]*$",
"description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
}
},
"description": "Citation to match against PubMed. Must include at least journal or a four-digit year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match."
},
"maxItems": 25,
"minItems": 1,
"description": "Citations to look up, 1–25, each matched independently. More fields = better match accuracy."
}
},
"additionalProperties": false
}Search and explore the MeSH (Medical Subject Headings) controlled vocabulary. Returns descriptor records with tree numbers, scope notes, and entry terms, plus pagination via offset for paging past the maxResults cap.
| Argument | Type | Description |
|---|---|---|
| query* | string | MeSH descriptor name or free-text term to look up. Must carry a term: a value of only whitespace or invisible characters, such as a zero-width space, is rejected rather than searched. |
| offset | integer | Result offset for pagination (0-based). Pass the `nextOffset` from the previous response to get the following page; the exact-descriptor match is pinned to the first page only. |
| maxResults | integer | Maximum results |
| includeDetails | boolean | Fetch full MeSH records (scope notes, tree numbers, entry terms) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"query"
],
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "MeSH descriptor name or free-text term to look up. Must carry a term: a value of only whitespace or invisible characters, such as a zero-width space, is rejected rather than searched."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Result offset for pagination (0-based). Pass the `nextOffset` from the previous response to get the following page; the exact-descriptor match is pinned to the first page only."
},
"maxResults": {
"type": "integer",
"default": 10,
"maximum": 50,
"minimum": 1,
"description": "Maximum results"
},
"includeDetails": {
"type": "boolean",
"default": true,
"description": "Fetch full MeSH records (scope notes, tree numbers, entry terms)"
}
},
"additionalProperties": false
}Search PubMed with full query syntax, filters, and date ranges. Returns PMIDs and optional brief summaries. Supports field-specific filters (author, journal, MeSH terms), common filters (language, species, free full text), and pagination via offset for paging through large result sets.
| Argument | Type | Description |
|---|---|---|
| sort | string | Sort order: relevance (default), pub_date (newest first), author, or journal |
| query* | string | PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup, bracketed field tags (`[pdat]`), parentheses, and invisible characters such as a zero-width space are disregarded is rejected rather than sent to PubMed. |
| author | string | Filter by author name (e.g. "Smith J"). An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected. |
| offset | integer | Result offset for pagination (0-based). PubMed serves at most the first 9999 records of a result set, so this caps at 9998; narrow the query or add filters to reach anything beyond it. |
| journal | string | Filter by journal name. An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected. |
| species |
Spell-check a PubMed search query against NCBI ESpell and get the corrected query back. Use after a zero-hit or thin `pubmed_search_articles` result, or when a drug, gene, disease, or author name may be misspelled. Corrections are applied in place, so field tags, operators, spacing, and gene symbols stay as written (`alzhiemer diseese treatmnt outcomse` → `alzheimer disease treatment outcomes`). Words in all capitals, with digits or non-ASCII letters, or differing only in case are never changed, nor is a single letter swapped for another (`q fever`, the initial in `smith q`), words after a number or symbol ESpell skips are left as written, and `hasSuggestion` is false when no correction was applied. Re-run the search with `corrected`.
| Argument | Type | Description |
|---|---|---|
| query* | string | PubMed search query to spell-check. Must carry a term: a value of only whitespace or invisible characters, such as a zero-width space, is rejected rather than sent to ESpell. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"query"
],
"properties": {
"query": {
"type": "string",
"minLength": 2,
"description": "PubMed search query to spell-check. Must carry a term: a value of only whitespace or invisible characters, such as a zero-width space, is rejected rather than sent to ESpell."
}
},
"additionalProperties": false
}| sources | array | Filter to specific EPMC sources. Defaults to MED, PMC, PPR when omitted. Pass an explicit array including PAT or AGR to broaden coverage. Allowed values: MED, PMC, PPR, PAT, AGR. |
| pageSize | integer | Results per page. Max 100 per EPMC API. |
| cursorMark | string | Pagination cursor. Use `*` (default) for the first page; pass the previous response's `nextCursorMark` verbatim for subsequent pages. A whitespace-only cursor, invisible characters included, is rejected before the request, and a cursor Europe PMC cannot read fails with `europepmc_invalid_input` once a retry of it fails again while the first page of the same query is served. |
| resultType | string | `core` returns abstract, IDs, dates, license; `lite` is a smaller payload with IDs and titles only. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"query"
],
"properties": {
"sort": {
"type": "string",
"description": "Optional EPMC sort: `<field> asc|desc`, or several comma-separated keys applied in order (`PUB_YEAR desc, CITED desc`). Documented sortable fields: `P_PDATE_D` (publication date), `CITED` (citation count), `AUTH_FIRST` (first author surname), `PUB_YEAR` (publication year), `FIRST_IDATE_D` (date first indexed in Europe PMC). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking; `relevance` or `relevance desc` is sent as no sort. Field and direction match case-insensitively. Each key must be one field plus `asc` or `desc`: a key without a direction, an empty key, or an extra word fails with `europepmc_invalid_input` before the request. A field outside the documented set may be honored, silently ignored, or refused — a sort Europe PMC refuses fails with `europepmc_invalid_input` naming it, and an empty Europe PMC response to a sort with an undocumented field fails the same way, even when Europe PMC honors the field. Note: `P_PDATE_D` is ignored for preprint-only (`sources: [\"PPR\"]`) result sets — preprints have no populated publication date, so use `PUB_YEAR desc` to order preprints by date."
},
"query": {
"type": "string",
"minLength": 1,
"description": "Europe PMC search query. Supports field tokens like `AUTH:\"<name>\"`, `JOURNAL:\"<title>\"`, `TITLE:\"<words>\"`, `PUB_YEAR:[2020 TO 2024]`, `DOI:\"...\"`, `EXT_ID:<pmid> AND SRC:MED`, `PMCID:PMC<digits>`. Identifier tokens may be quoted or unquoted — this tool wraps every query with its `sources` filter, and Europe PMC honors a quoted identifier inside that wrapper. A PubMed-indexed article resolves under `SRC:MED`, not `SRC:PMC`, whichever identifier is used. Free text is matched broadly across abstract/title/keywords. A query with no search term — blank once HTML entities are decoded and markup, parentheses, and invisible characters are disregarded, such as `()` or `<b></b>` — is rejected; any other query is sent as written."
},
"sources": {
"type": "array",
"items": {
"enum": [
"MED",
"PMC",
"PPR",
"PAT",
"AGR"
],
"type": "string"
},
"minItems": 1,
"description": "Filter to specific EPMC sources. Defaults to MED, PMC, PPR when omitted. Pass an explicit array including PAT or AGR to broaden coverage. Allowed values: MED, PMC, PPR, PAT, AGR."
},
"pageSize": {
"type": "integer",
"default": 25,
"maximum": 100,
"minimum": 1,
"description": "Results per page. Max 100 per EPMC API."
},
"cursorMark": {
"type": "string",
"default": "*",
"description": "Pagination cursor. Use `*` (default) for the first page; pass the previous response's `nextCursorMark` verbatim for subsequent pages. A whitespace-only cursor, invisible characters included, is rejected before the request, and a cursor Europe PMC cannot read fails with `europepmc_invalid_input` once a retry of it fails again while the first page of the same query is served."
},
"resultType": {
"enum": [
"core",
"lite"
],
"type": "string",
"default": "core",
"description": "`core` returns abstract, IDs, dates, license; `lite` is a smaller payload with IDs and titles only."
}
},
"additionalProperties": false
}{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"pmids"
],
"properties": {
"pmids": {
"type": "array",
"items": {
"type": "string",
"pattern": "^\\d+$"
},
"maxItems": 200,
"minItems": 1,
"description": "PubMed IDs to fetch"
},
"includeMesh": {
"type": "boolean",
"default": true,
"description": "Include MeSH terms"
},
"includeGrants": {
"type": "boolean",
"default": false,
"description": "Include grant information"
},
"includeInvestigators": {
"type": "boolean",
"default": false,
"description": "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."
},
"maxResponseCharacters": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "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."
}
},
"additionalProperties": false
}| pmcids | array | PMC IDs to fetch (e.g. ["PMC9575052"]). Combinable with `pmids` and `dois` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them. |
| sections | array | Filter to specific sections by title (e.g. ["Introduction", "Methods", "Results", "Discussion"]). A term matches a section or subsection title at any nesting depth, case-insensitively, as a substring — "resul" matches "Results". Back-matter sections match the same way: by their own titles, or by their kind when they carry neither a title nor a label — "appendix" matches a section titled "Appendix A", "footnotes" an untitled footnote group. A label is never matched, so an appendix labelled "Appendix A" under a title of its own matches only on that title. A section whose own title matches is returned whole; one kept only because a nested subsection matched keeps its heading as a breadcrumb, with its own text cleared and only the matching branch beneath it. Tables and assets narrow with the filter: one whose section did not survive, or that names no section, is dropped. Empty strings are skipped, so `[""]` applies no filter; an element of only whitespace or invisible characters, such as a zero-width space, is rejected. Applies to `source=pmc` results only. |
| maxSections | integer | Maximum top-level sections. Back-matter sections count and follow the body, so a cap drops them first. Applies to `source=pmc` results only. |
| overflowMode | string | How to spend `maxCharacters` across an article that exceeds it. truncate: fill sections in document order, so early sections stay whole, the section the budget runs out in is cut, and every section or subsection past that point is dropped (counted in `truncation.omittedSections`). outline: every section and subsection keeps its heading, and the budget is split evenly at every level — across the top-level sections, then inside each one across its own text and each subsection — with what a short part does not need going to the rest. A part whose share would cut it to under 100 characters (or under `maxCharactersPerSection`, when that is lower) returns heading-only instead, its share going to the others, emptying the last such part first; a part that fits its share keeps all its text however short, and a cut excerpt ends at the last word boundary inside its share, so it can run somewhat under that floor. A heading the budget left empty is marked as such in the rendered text. In both modes the body sections spend the budget first and back-matter sections share what the body, tables, and assets leave. Use it to survey what an article contains before requesting specific `sections`. Ignored when no budget is set, and identical for `source=unpaywall` bodies, which have no headings to preserve. |
| includeAssets | boolean | Include the article's figures and supplementary material — `assets[]`, each with its label, caption, enclosing section and deposit pointer. On by default because it is cheaper than tables: a median asset-bearing article grows about 10%, and the body prose already refers to these by label. Set false to omit them, which also removes the `[Figure: …]` / `[Supplementary: …]` markers from the section text, since without the array they point at nothing. Prose-shaped blocks — lists, definition lists, block quotes, boxed text, preformatted blocks, displayed formulae — are section text rather than assets and this switch never affects them. Applies to `source=pmc` results only. |
| includeTables | boolean | Include the article's tables — cells, captions, labels and footnotes. On by default because a dropped table takes its numbers with it. Table-dense articles pay for it: rendered tables typically add 12–17% to an article record and can more than double it. Set false to omit them, or cap the cost with `maxCharacters`, which drops tables it cannot fit whole. Applies to `source=pmc` results only. |
| maxCharacters | integer | Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text — which carries the inline blocks the parser renders in place, such as lists, definition lists, block quotes, boxed text, preformatted blocks and displayed formulae — plus table label, caption, cell and footnote text and asset label, caption and `href` text; or the `source=unpaywall` `content` body. Titles, abstracts, identifiers, and references are never counted or shortened. Shortened text ends at the last word boundary inside its allowance, so it can come back a few characters under it. The counted unit is that text alone — the Markdown grid `content[]` renders around the cells (pipes, padding, the divider row, headings) is scaffolding this budget does not measure, so a table renders longer than it costs here. Body sections are served first, then tables, then assets, then back-matter sections, each spending what is left, in document order. For tables and assets, admission stops at the first entry that does not fit, and every entry from there on is dropped whole rather than cut mid-row or returned with a shortened caption, counted in `truncation.omittedTables` / `truncation.omittedAssets` and named in `truncation.articles[].omittedTableNames` / `omittedAssetNames`. Back matter takes only what remains and is shortened or dropped as body sections are; `sections` still returns it after the body. Applied after `sections`, `maxSections`, `includeReferences`, `includeTables`, and `includeAssets`, so semantic filtering is unaffected. This knob alone bounds only bodies: the response-wide ceiling it implies is this value times the number of articles returned, plus every uncounted field. Use `maxResponseCharacters` for a true whole-response ceiling. Omit for the full body. |
| includeReferences | boolean | Include reference list. Applies to `source=pmc` results only. |
| maxResponseCharacters | integer | Opt-in ceiling for the whole response, in characters — the true response-wide counterpart to the per-article `maxCharacters`. Each article is measured as the JSON record it is returned as, after every filter and the per-article body budget: title, abstract, sections, references, identifiers, license and source metadata — every field it carries. One ledger covers all tiers, so PMC-, Europe PMC-, and Unpaywall-served articles spend the same budget. Articles are kept in the order `articles` lists them 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, `unavailable`, `truncation`, `deferred` itself — are not counted. Omit to return every resolved article. |
| maxCharactersPerSection | integer | Budget for a single top-level section, in characters, counting the section text plus its subsections. Combine with `maxCharacters` to cap both one section and the article; the tighter of the two wins. Applies to `source=pmc` results only. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"dois": {
"type": "array",
"items": {
"type": "string",
"pattern": "^10\\.[^\\s,]+\\/[^\\s,]+$"
},
"maxItems": 50,
"minItems": 1,
"description": "DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]), one per element. Combinable with `pmcids` and `pmids` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. Resolved to a PMCID via the PMC ID Converter and returned as structured JATS when the article is in PMC; DOIs with no PMC counterpart (preprints, EPMC-only OA) fall through to Europe PMC, then Unpaywall, when those layers are enabled."
},
"pmids": {
"type": "array",
"items": {
"type": "string",
"pattern": "^\\d+$"
},
"maxItems": 50,
"minItems": 1,
"description": "PubMed IDs. Combinable with `pmcids` and `dois` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. Articles in PMC are returned as structured JATS; articles not in PMC fall through to Europe PMC (when EPMC has a `fullTextXML`), then to Unpaywall when `UNPAYWALL_EMAIL` is set and a DOI is available."
},
"pmcids": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?:PMC)?\\d+$"
},
"maxItems": 50,
"minItems": 1,
"description": "PMC IDs to fetch (e.g. [\"PMC9575052\"]). Combinable with `pmids` and `dois` — up to 50 distinct identifiers across the three; the first 10 are fetched per call, the rest returned in `overLimit`. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them."
},
"sections": {
"type": "array",
"items": {
"type": "string"
},
"description": "Filter to specific sections by title (e.g. [\"Introduction\", \"Methods\", \"Results\", \"Discussion\"]). A term matches a section or subsection title at any nesting depth, case-insensitively, as a substring — \"resul\" matches \"Results\". Back-matter sections match the same way: by their own titles, or by their kind when they carry neither a title nor a label — \"appendix\" matches a section titled \"Appendix A\", \"footnotes\" an untitled footnote group. A label is never matched, so an appendix labelled \"Appendix A\" under a title of its own matches only on that title. A section whose own title matches is returned whole; one kept only because a nested subsection matched keeps its heading as a breadcrumb, with its own text cleared and only the matching branch beneath it. Tables and assets narrow with the filter: one whose section did not survive, or that names no section, is dropped. Empty strings are skipped, so `[\"\"]` applies no filter; an element of only whitespace or invisible characters, such as a zero-width space, is rejected. Applies to `source=pmc` results only."
},
"maxSections": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Maximum top-level sections. Back-matter sections count and follow the body, so a cap drops them first. Applies to `source=pmc` results only."
},
"overflowMode": {
"enum": [
"truncate",
"outline"
],
"type": "string",
"default": "truncate",
"description": "How to spend `maxCharacters` across an article that exceeds it. truncate: fill sections in document order, so early sections stay whole, the section the budget runs out in is cut, and every section or subsection past that point is dropped (counted in `truncation.omittedSections`). outline: every section and subsection keeps its heading, and the budget is split evenly at every level — across the top-level sections, then inside each one across its own text and each subsection — with what a short part does not need going to the rest. A part whose share would cut it to under 100 characters (or under `maxCharactersPerSection`, when that is lower) returns heading-only instead, its share going to the others, emptying the last such part first; a part that fits its share keeps all its text however short, and a cut excerpt ends at the last word boundary inside its share, so it can run somewhat under that floor. A heading the budget left empty is marked as such in the rendered text. In both modes the body sections spend the budget first and back-matter sections share what the body, tables, and assets leave. Use it to survey what an article contains before requesting specific `sections`. Ignored when no budget is set, and identical for `source=unpaywall` bodies, which have no headings to preserve."
},
"includeAssets": {
"type": "boolean",
"default": true,
"description": "Include the article's figures and supplementary material — `assets[]`, each with its label, caption, enclosing section and deposit pointer. On by default because it is cheaper than tables: a median asset-bearing article grows about 10%, and the body prose already refers to these by label. Set false to omit them, which also removes the `[Figure: …]` / `[Supplementary: …]` markers from the section text, since without the array they point at nothing. Prose-shaped blocks — lists, definition lists, block quotes, boxed text, preformatted blocks, displayed formulae — are section text rather than assets and this switch never affects them. Applies to `source=pmc` results only."
},
"includeTables": {
"type": "boolean",
"default": true,
"description": "Include the article's tables — cells, captions, labels and footnotes. On by default because a dropped table takes its numbers with it. Table-dense articles pay for it: rendered tables typically add 12–17% to an article record and can more than double it. Set false to omit them, or cap the cost with `maxCharacters`, which drops tables it cannot fit whole. Applies to `source=pmc` results only."
},
"maxCharacters": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text — which carries the inline blocks the parser renders in place, such as lists, definition lists, block quotes, boxed text, preformatted blocks and displayed formulae — plus table label, caption, cell and footnote text and asset label, caption and `href` text; or the `source=unpaywall` `content` body. Titles, abstracts, identifiers, and references are never counted or shortened. Shortened text ends at the last word boundary inside its allowance, so it can come back a few characters under it. The counted unit is that text alone — the Markdown grid `content[]` renders around the cells (pipes, padding, the divider row, headings) is scaffolding this budget does not measure, so a table renders longer than it costs here. Body sections are served first, then tables, then assets, then back-matter sections, each spending what is left, in document order. For tables and assets, admission stops at the first entry that does not fit, and every entry from there on is dropped whole rather than cut mid-row or returned with a shortened caption, counted in `truncation.omittedTables` / `truncation.omittedAssets` and named in `truncation.articles[].omittedTableNames` / `omittedAssetNames`. Back matter takes only what remains and is shortened or dropped as body sections are; `sections` still returns it after the body. Applied after `sections`, `maxSections`, `includeReferences`, `includeTables`, and `includeAssets`, so semantic filtering is unaffected. This knob alone bounds only bodies: the response-wide ceiling it implies is this value times the number of articles returned, plus every uncounted field. Use `maxResponseCharacters` for a true whole-response ceiling. Omit for the full body."
},
"includeReferences": {
"type": "boolean",
"default": false,
"description": "Include reference list. Applies to `source=pmc` results only."
},
"maxResponseCharacters": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Opt-in ceiling for the whole response, in characters — the true response-wide counterpart to the per-article `maxCharacters`. Each article is measured as the JSON record it is returned as, after every filter and the per-article body budget: title, abstract, sections, references, identifiers, license and source metadata — every field it carries. One ledger covers all tiers, so PMC-, Europe PMC-, and Unpaywall-served articles spend the same budget. Articles are kept in the order `articles` lists them 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, `unavailable`, `truncation`, `deferred` itself — are not counted. Omit to return every resolved article."
},
"maxCharactersPerSection": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Budget for a single top-level section, in characters, counting the section text plus its subsections. Combine with `maxCharacters` to cap both one section and the article; the tighter of the two wins. Applies to `source=pmc` results only."
}
},
"additionalProperties": false
}| string |
| Filter by species |
| language | string | Filter by language (e.g. "english"). An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected. |
| dateRange | object | Filter by date range. The filter is applied only when both `minDate` and `maxDate` are non-empty; either one empty disables the entire date range. A partial date covers its whole year or month, and a range whose `minDate` falls after its `maxDate` is rejected: `2024/06` to `2024` is valid, `2024/07` to `2024/06/30` is not. |
| meshTerms | array | Filter by MeSH terms. Multiple terms are AND'd — all must match. Empty strings are skipped; an element of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected. |
| maxResults | integer | Maximum results to return. 0 returns only `totalCount`. |
| hasAbstract | boolean | Only include articles with abstracts |
| freeFullText | boolean | Only include free full text articles |
| summaryCount | integer | Fetch brief summaries for top N results (0 = PMIDs only). Above the 50 cap, pass the remaining PMIDs to pubmed_fetch_articles. |
| publicationTypes | array | Filter by publication type (e.g. "Review", "Clinical Trial", "Meta-Analysis"). Multiple values are OR'd — any match qualifies. Empty strings are skipped; an element of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"query"
],
"properties": {
"sort": {
"enum": [
"relevance",
"pub_date",
"author",
"journal"
],
"type": "string",
"default": "relevance",
"description": "Sort order: relevance (default), pub_date (newest first), author, or journal"
},
"query": {
"type": "string",
"minLength": 1,
"description": "PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup, bracketed field tags (`[pdat]`), parentheses, and invisible characters such as a zero-width space are disregarded is rejected rather than sent to PubMed."
},
"author": {
"type": "string",
"description": "Filter by author name (e.g. \"Smith J\"). An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9998,
"minimum": 0,
"description": "Result offset for pagination (0-based). PubMed serves at most the first 9999 records of a result set, so this caps at 9998; narrow the query or add filters to reach anything beyond it."
},
"journal": {
"type": "string",
"description": "Filter by journal name. An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected."
},
"species": {
"enum": [
"humans",
"animals"
],
"type": "string",
"description": "Filter by species"
},
"language": {
"type": "string",
"description": "Filter by language (e.g. \"english\"). An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected."
},
"dateRange": {
"type": "object",
"required": [
"minDate",
"maxDate"
],
"properties": {
"maxDate": {
"type": "string",
"pattern": "^$|^\\d{4}([/\\-.]\\d{1,2}([/\\-.]\\d{1,2})?)?$",
"description": "End date (YYYY/MM/DD, YYYY/MM, or YYYY). Must be a real calendar date — `2023/02/29` is rejected. Leaving it empty drops the whole date range, not just this bound; for no upper bound pass a wide sentinel such as `3000`."
},
"minDate": {
"type": "string",
"pattern": "^$|^\\d{4}([/\\-.]\\d{1,2}([/\\-.]\\d{1,2})?)?$",
"description": "Start date (YYYY/MM/DD, YYYY/MM, or YYYY). Must be a real calendar date — `2023/02/29` is rejected. Leaving it empty drops the whole date range, not just this bound; for no lower bound pass a wide sentinel such as `1000`."
},
"dateType": {
"enum": [
"pdat",
"mdat",
"edat"
],
"type": "string",
"default": "pdat",
"description": "Date type: pdat (publication), mdat (modification), edat (entrez)"
}
},
"description": "Filter by date range. The filter is applied only when both `minDate` and `maxDate` are non-empty; either one empty disables the entire date range. A partial date covers its whole year or month, and a range whose `minDate` falls after its `maxDate` is rejected: `2024/06` to `2024` is valid, `2024/07` to `2024/06/30` is not."
},
"meshTerms": {
"type": "array",
"items": {
"type": "string"
},
"description": "Filter by MeSH terms. Multiple terms are AND'd — all must match. Empty strings are skipped; an element of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected."
},
"maxResults": {
"type": "integer",
"default": 20,
"maximum": 1000,
"minimum": 0,
"description": "Maximum results to return. 0 returns only `totalCount`."
},
"hasAbstract": {
"type": "boolean",
"description": "Only include articles with abstracts"
},
"freeFullText": {
"type": "boolean",
"description": "Only include free full text articles"
},
"summaryCount": {
"type": "integer",
"default": 0,
"maximum": 50,
"minimum": 0,
"description": "Fetch brief summaries for top N results (0 = PMIDs only). Above the 50 cap, pass the remaining PMIDs to pubmed_fetch_articles."
},
"publicationTypes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Filter by publication type (e.g. \"Review\", \"Clinical Trial\", \"Meta-Analysis\"). Multiple values are OR'd — any match qualifies. Empty strings are skipped; an element of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected."
}
},
"additionalProperties": false
}