Skip to main content
Glama
cyanheads

pubmed-mcp-server

by cyanheads

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://pubmed.caseyjhand.com/mcp


Overview

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.

Tools

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

Resources

Resource

Description

pubmed://database/info

PubMed database metadata via EInfo (field list, record count, last update)

Prompts

Prompt

Description

research_plan

Generate a structured four-phase biomedical research plan

Related MCP server: PubMed MCP Server

Capability reference

pubmed_search_articles tool

  • Full PubMed query syntax plus filters (author, journal, MeSH, publication type, language, species, abstract, free full text) and publication/modification/Entrez date ranges

  • 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

  • Reports totalCount, the effectiveQuery PubMed ran, and the appliedFilters

  • Rejects blank filter values, impossible dates, and reversed date ranges before PubMed is called


pubmed_fetch_articles tool

  • Up to 200 PMIDs per call; misses are listed in unavailablePmids

  • 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

  • A retracted, corrected, or questioned article opens with the same notice block as pubmed_format_citations, directly under its title

  • dataBanks 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 one

  • recordType separates journal articles from Bookshelf chapters and books; opt-in maxResponseCharacters defers overflow to deferred.ids


pubmed_fetch_fulltext tool

  • 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

  • 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

  • Europe PMC full text is fetched for records Europe PMC marks open access, preprints included; articles carry the licenses their PMC JATS states

  • Misses carry a typed reason and triedTiers; sections, maxSections, maxCharacters, overflowMode, and maxResponseCharacters bound the response, truncation reports what was cut, and sectionCap names the sections maxSections cut


  • Adds preprints (PPR), patents (PAT), and Agricola (AGR) to MED and PMC; defaults to MED, PMC, PPR

  • Cursor pagination via cursorMark / nextCursorMark, up to 100 per page; abstracts arrive as 400-character snippets

  • Optional sort of comma-separated <field> asc|desc keys; relevance is the default order, and a sort Europe PMC cannot use fails as europepmc_invalid_input

  • Hits carry license and hasFullTextXml

  • Not registered when EUROPEPMC_ENABLED=false


pubmed_europepmc_fetch tool

  • Up to 25 records per call as records: [{ 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.records

  • Not registered when EUROPEPMC_ENABLED=false


pubmed_fetch_citation_metrics tool

  • Up to 1,000 PMIDs per call; the first 200 distinct are fetched and the rest returned in overLimit, PMIDs NIH iCite has no record for in unavailablePmids

  • Relative Citation Ratio (with a provisional flag), NIH percentile, citation count and rates, Approximate Potential to Translate, MeSH category shares, and clinical citations

  • iCite does not flag retractions; check pubmed_fetch_articles for notices

  • Source: NIH iCite


pubmed_format_citations tool

  • Up to 50 PMIDs per call, in any mix of apa, mla, bibtex, ris, and vancouver

  • 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

  • Vancouver writes page ranges in elided Citing Medicine form (1637-1645 → 1637-45); BibTeX issn and RIS SN carry the journal's linking ISSN, and RIS TI drops its trailing period

  • 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


  • similar, cited_by, or references, up to 50 per page with offset pagination

  • Falls back to Europe PMC, then OpenAlex, and names the provider that answered; fails as all_providers_failed if none can


pubmed_spell_check tool

  • Returns original, 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 another

  • Values of indexed field tags (aged[mh], zhang y[au], free full text[sb]) are kept as written; free-text fields ([tiab], [ti], [tw]) are still corrected

  • Run it after a zero-hit or thin search, then retry pubmed_search_articles with corrected


pubmed_lookup_mesh tool

  • Descriptors by name or free-text term, with an exact heading match pinned first (the descriptor, for a drug-class heading); up to 50 per page, continued via nextOffset

  • Records carry their MeSH unique ID (meshId, e.g. D000072657) beside the Entrez UID; includeDetails (default on) adds tree numbers, scope notes, and entry terms


pubmed_lookup_citation tool

  • 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

  • Each is matched independently and comes back matched, not_found, or ambiguous, with recovery detail


pubmed_convert_ids tool

  • Up to 50 ids per call, all of one declared idType (doi, pmid, pmcid); only PMC-indexed articles resolve

  • One success or error row per id, in order, so a partial batch never fails


pubmed://database/info resource

  • Live EInfo call for the pubmed database, returned as application/json

  • count, lastUpdate, and fields[], whose names are the search tags pubmed_search_articles accepts


research_plan prompt

  • Arguments: title, goal, and keywords required; organism and includeAgentPrompts optional

  • Returns a four-phase research plan; includeAgentPrompts: "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_articles

Features

Built 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:

  • NCBI E-utilities (ESearch, EFetch, ESummary, ELink, ESpell, EInfo, ECitMatch) and the PMC ID Converter, with Europe PMC, OpenAlex, and Unpaywall filling the gaps

  • Shared NCBI request queue: paced request starts, capped concurrency, a cooldown that holds every caller after an NCBI 429, and one deadline covering queue wait and retries

  • XML parsing built for PubMed's inconsistent records: structured abstracts, missing fields, varying date formats

  • Forgiving identifiers: a zero-padded PMID resolves as the PMID it spells in every tool that takes one; pubmed_fetch_fulltext also matches DOIs case-insensitively and PMC IDs with or without the PMC prefix

  • Hand-rolled citation formatters (APA, MLA, BibTeX, RIS, Vancouver) with no dependencies

Agent-friendly output:

  • Provenance in responses: source labels, license fields on full-text and Europe PMC results, best-effort warnings on Unpaywall results, and effective-query echo on searches, so agents can judge what to trust

  • Graceful partial failure: batch tools return per-item success/error rows instead of failing the request, with structured status codes and actionable next-step text

  • Discriminated output contracts: source: "pmc" | "unpaywall", typed unavailable reasons, viaSource and triedTiers fields, so callers branch on data, not string parsing

Getting started

Connect 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.

Configuration

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.

Running the server

Local development

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

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.

Project structure

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.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic

  • Use ctx.log for logging, ctx.state for storage

  • Register new tools and resources in the createApp() arrays in src/index.ts

  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Available Tools

12 tools
pubmed_convert_idsPubmed Convert IdsA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
idsYesArticle 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.
idTypeYesThe type of IDs being submitted. Required so the API can unambiguously resolve them.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
recordsNoConversion results, one per input ID
totalConvertedNoNumber of IDs successfully converted
totalSubmittedNoNumber of IDs submitted

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so the safety profile is known. The description adds real behavioral context beyond that: the PMC-only resolution boundary and the single-type/50-ID batch constraint. It stops short of describing output or partial-resolution behavior, but that is largely carried by the output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each earning its place: purpose first, then the batch constraint, then the scope limitation and alternative. No redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be explained, and the description supplies the key operational facts an agent needs: what it converts, batch limits, and the PMC-indexing boundary plus the fallback tool. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the schema already documents the ID formats, the 50-item cap, and single-type requirement in far more detail than the description. The description's parameter mentions ('up to 50 IDs of a single type') merely restate constraints already fully specified, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Convert') and resource ('article identifiers (DOI, PMID, PMCID)'), making the tool's function unambiguous. It also signals scope via the PMC-only note, so an agent can distinguish it from the search/fetch siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names a when-not condition ('Only resolves articles indexed in PubMed Central') and routes the agent to the correct alternative for those cases ('use pubmed_search_articles instead'). It also states the per-request limit of 50 single-type IDs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_europepmc_fetchPubmed Europepmc FetchA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
recordsYesRecords to retrieve, each addressed by the `source` and `epmcId` of a `pubmed_europepmc_search` hit. The whole batch resolves in one Europe PMC request.
maxResponseCharactersNoOpt-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.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoGuidance when one or more requested records could not be resolved, or when `maxResponseCharacters` deferred records — naming how to retrieve them. Absent when every requested record came back.
recordsNoResolved records, in the order Europe PMC returned them. Under a `maxResponseCharacters` budget, only the leading records that fit; `deferred.records` lists the rest.
deferredNoContinuation state for records the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one record.
notFoundNoRequested `source` + `epmcId` pairs Europe PMC returned no record for. Reported in full regardless of where a `maxResponseCharacters` cut lands — these are misses, not deferrals.
truncatedNoTrue when `maxResponseCharacters` withheld at least one resolved record. Absent when the response carries every record that resolved. The continuation state is in `deferred`.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint and openWorldHint already declared, the description adds valuable behavioral context: a 25-record per-call limit and detailed deferral semantics via maxResponseCharacters ('records past the ceiling are deferred whole and listed in deferred.records'). It does not cover error handling or rate limiting, but the added context goes well beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and usage, and each sentence contributes useful information. It is slightly longer than strictly necessary, but every clause (search pairing, source coverage, limits, deferral) earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a simple two-parameter schema, rich annotations, and an existing output schema, the description covers everything an agent needs: purpose, alternatives, special source handling, call limits, and response-bounding behavior. No critical gaps remain.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already fully documents both records and maxResponseCharacters. The description reinforces the array syntax ('an array even for one record') and copying from search hits, but adds no new parameter semantics beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Fetch complete Europe PMC records — including the full, untruncated abstract'. It explicitly differentiates from siblings by naming pubmed_europepmc_search and explaining the retrieval gap for preprint/patent/Agricola records that pubmed_fetch_articles and pubmed_fetch_fulltext cannot address.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly instructs to pair with pubmed_europepmc_search, copy each hit's source and epmcId into records, and use this tool specifically for records lacking PMIDs/DOIs. It explicitly names alternatives that cannot handle them, giving strong when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_fetch_articlesPubmed Fetch ArticlesA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
pmidsYesPubMed IDs to fetch
includeMeshNoInclude MeSH terms
includeGrantsNoInclude grant information
includeInvestigatorsNoInclude `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.
maxResponseCharactersNoOpt-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.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoOptional guidance when no articles were returned — points to discovery tools — or when `maxResponseCharacters` deferred articles, naming how to retrieve them. Absent on successful unbudgeted fetches.
articlesNoParsed articles
deferredNoContinuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.
truncatedNoTrue when `maxResponseCharacters` withheld at least one resolved article. Absent when the response carries every article that resolved. The continuation state is in `deferred`.
totalReturnedNoNumber of articles in this response. Under a `maxResponseCharacters` budget this counts the kept articles only; `deferred.deferredCount` covers the rest.
unavailablePmidsNoPMIDs PubMed returned no record for. That is all this reports: PubMed omits an unknown PMID silently, with no error and no reason, so the absence says nothing about whether the PMID exists. Reported in full regardless of where a `maxResponseCharacters` cutoff lands — these are misses, not deferrals. Use `pubmed_search_articles` to find PMIDs that do resolve.

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnly/openWorld; the description goes well beyond by enumerating what the record contains and, crucially, disclosing the truncation contract: articles are deferred whole, never partially populated, and response envelope fields are not counted. That is exactly the non-obvious behavior an agent needs before setting a ceiling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three front-loaded sentences: purpose first, then return contents, then the sizing/deferral rule. Dense but every clause carries information; no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a batch-fetch tool with an output schema, this covers purpose, return contents, opt-in fields, and the partial-response/deferral contract. An agent can call it correctly and recover from truncation without extra discovery.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents maxResponseCharacters, includeGrants and includeInvestigators in depth, so the description's parameter mentions largely restate it. The 'opt-in' framing and the tie between includeInvestigators and deferral add a little meaning, but not enough to exceed the baseline for a fully documented schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Fetch full article metadata by PubMed IDs', and the 'metadata' framing implicitly separates it from pubmed_fetch_fulltext and the search siblings. It stops short of naming any sibling explicitly, so an agent must infer the boundary from names alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The precondition (you already hold PubMed IDs) and the follow-up path when results are deferred ('deferred.ids for a follow-up call') are both clear. It does not say what to do when you lack IDs, e.g. route to pubmed_search_articles first, which would have made this a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_fetch_citation_metricsPubmed Fetch Citation MetricsA
Read-only
Inspect

Fetch NIH iCite citation metrics for PubMed articles: the Relative Citation Ratio (RCR), NIH percentile, citation count, citations per year, expected and field citation rates, the Approximate Potential to Translate (APT), the human, animal, and molecular/cellular shares of each article's MeSH terms, and how many clinical articles cite it. RCR is a field- and year-normalized citation rate benchmarked to NIH-funded papers, whose median RCR is 1.0; iCite computes none for the most recent papers and marks recent ones provisional. iCite does not flag retractions — a retracted article keeps its metrics — so check pubmed_fetch_articles for retraction notices before relying on a record. Up to 1000 PMIDs per call; the first 200 distinct PMIDs are fetched and the rest returned in overLimit. PMIDs iCite has no record for are listed in unavailablePmids.

NameRequiredDescriptionDefault
pmidsYesPubMed IDs to look up (e.g. ["23456789"]). The first 200 distinct PMIDs are fetched; the rest are returned in `overLimit`. Leading zeros are ignored, so "00023456789" and "23456789" name one article.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoNames the PMIDs iCite had no record for, records not yet scored, records with no RCR or a provisional one, and PMIDs past the per-call limit with how to fetch them. Absent when every record came back final and complete.
recordsNoOne record per fetched PMID iCite knows, in request order.
overLimitNoPMIDs the call named past the per-call fetch limit. Present only when the call named more distinct PMIDs than one call fetches.
unavailablePmidsNoRequested PMIDs within the per-call limit that NIH iCite has no record for, in request order, under the spelling sent. iCite omits a PMID it has no record for without an error, so the absence says nothing about whether PubMed has the article; pubmed_fetch_articles confirms that.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only cover readOnly/openWorld, and the description adds substantial behavior: 200-distinct-PMID truncation with the remainder surfaced in `overLimit`, missing records in `unavailablePmids`, RCR being field/year-normalized with NIH median 1.0, recent papers lacking or having provisional RCR, and the crucial caveat that iCite does not flag retractions. That is exactly the non-obvious operational detail an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the primary purpose and the metric list, then limits and caveats. Dense but each clause carries real information; the long enumeration of metrics is the only mildly padded element.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only lookup with an output schema, the description covers everything an agent needs: scope, truncation semantics, missing-record behavior, normalization of RCR, and the retraction blind spot. Nothing material is left implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the parameter itself is already documented. The description still adds meaning beyond the schema by stating the 200-distinct-PMID dedup/fetch ceiling and the resulting `overLimit` behavior, which the schema does not express.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb (fetch) plus specific resource (NIH iCite citation metrics for PubMed articles), and it enumerates the metrics returned (RCR, NIH percentile, citation count, APT, MeSH shares, citing-clinical count). An agent can distinguish this from pubmed_fetch_articles or pubmed_lookup_citation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear usage context: batch PMID lookup with a 1000-PMID cap and a 200-distinct fetch limit, plus an explicit instruction to check pubmed_fetch_articles for retraction notices before relying on a record. It lacks a positive 'use this instead of sibling X when…' framing for the other metric-free siblings, so it falls just short of 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_fetch_fulltextPubmed Fetch FulltextA
Read-only
Inspect

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 its open-access subset, preprints included). Name articles by pmcids (PMC IDs directly), pmids (PubMed IDs, auto-resolved), dois (DOIs, auto-resolved to PMC via the ID Converter; open-access preprints recover via Europe PMC), 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.

NameRequiredDescriptionDefault
doisNoDOIs 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.
pmidsNoPubMed 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.
pmcidsNoPMC 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.
sectionsNoFilter 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.
maxSectionsNoMaximum top-level sections, counted after `sections`. Back-matter sections count and follow the body, so a cap drops them first. The sections it drops are named per article in `sectionCap`. Applies to `source=pmc` results only.
overflowModeNoHow 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.truncate
includeAssetsNoInclude 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.
includeTablesNoInclude 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.
maxCharactersNoPer-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, licenses, 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; a section or subsection whose allowance cannot hold its first word returns no text rather than part of that word, so it is dropped in `truncate` mode and heading-only in `outline` mode, while text that is one unbroken token, such as a sequence or URL, is cut at the allowance. 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.
includeReferencesNoInclude reference list. Applies to `source=pmc` results only.
maxResponseCharactersNoOpt-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`, `sectionCap`, `deferred` itself — are not counted. Omit to return every resolved article.
maxCharactersPerSectionNoBudget 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.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoOptional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A `maxSections` cap that dropped sections names, per article, how many it kept of how many and the titles it left out, and points at `sections` or a larger cap. A response-wide budget that deferred articles names the ids to re-request. Identifiers past the per-call limit are named with the field to resend each under. Absent when none of those applies.
articlesNoFull-text articles in routing order — `pmcids`, then `pmids`, then `dois`, request order within each — each at the position of the id it would be deferred under, whichever tier served it. An article several ids name appears once.
deferredNoContinuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.
overLimitNoIdentifiers the call named past the per-call fetch limit, returned unfetched for a follow-up call. Present only when the call named more than `limit` distinct identifiers. Separate from `deferred`, which lists articles that were fetched but withheld by `maxResponseCharacters`.
truncatedNoTrue when a character budget shortened at least one returned body or withheld a whole article, when `maxSections` dropped top-level sections from a returned article, or when the call named more distinct identifiers than one call fetches. Absent when every requested identifier was fetched and every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the sections the cap dropped are in `sectionCap`; the withheld ids are in `deferred`; the unfetched ids are in `overLimit`.
sectionCapNoThe top-level sections `maxSections` left out of `source: pmc` articles, PMC- and Europe PMC-served alike. Present only when the cap dropped at least one section from a returned article. Separate from `truncation`, which accounts for characters a budget removed from the sections the cap kept: no section appears in both. Re-call with `sections` set to the titles you need, or with a larger `maxSections`, to receive them.
truncationNoCharacter accounting for full text the budget shortened. Present only when a budget actually removed characters — its absence means every returned article carries its full post-filter body.
unavailableNoPer-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` names the field each id was sent in. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both. Ids past the per-call limit are listed in `overLimit`, never here.
totalReturnedNoNumber of articles in this response. Under a `maxResponseCharacters` budget this counts the kept articles only; `deferred.deferredCount` covers the rest.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnly and openWorld; the description adds substantive behavior — the 10-per-call fetch cap with the remainder in `overLimit`, de-duplication of articles named by several identifiers, the automatic fallback chain, and the two independent character budgets with whole-article deferral into `deferred.ids`. Two gaps: it never mentions the Unpaywall layer that the schema says can serve bodies, and it says articles come 'with ... references' although `includeReferences` defaults to false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the operation and output shape, and the fallback and batching rules follow in priority order. The second sentence is extremely long and packs identifier semantics, limits, and two character controls into one run-on, so parsing costs more than it should for a 12-parameter tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema, return values need no explanation, yet the description usefully names the envelope fields an agent must react to (`overLimit`, `deferred.ids`). It is nearly complete for this complexity; the omissions are the Unpaywall tier and the off-by-default references, both of which could mislead an agent about what a response will contain.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, and most of what the description says about identifiers and the two character knobs is restated in the per-parameter descriptions. It does add cross-parameter meaning — that the three identifier arrays can be mixed to 50 distinct IDs with only the first 10 fetched per call — which the schema states per-field but the description makes salient up front.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first clause names a specific verb and resource (fetch full-text articles from PubMed Central) and states the output shape (structured sections with tables and references). It also discloses the Europe PMC fallback, which separates it from abstract-level siblings like pubmed_fetch_articles and from pubmed_europepmc_fetch. An agent can pick it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives real routing guidance: PMC first, Europe PMC `fullTextXML` transparently when PMC misses, and a note that `outline` mode is for surveying an article before requesting specific `sections`. What it lacks is an explicit contrast with sibling tools (when to use pubmed_fetch_articles or pubmed_europepmc_fetch instead), so the choice is implied rather than spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_format_citationsPubmed Format CitationsA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
pmidsYesPubMed IDs to cite
formatNoCitation format(s) to generate — single style as a string or multiple as an array. Allowed values: apa, mla, bibtex, ris, vancouver.apa

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
noticeNoOptional guidance when no citations were produced — points to discovery tools. Absent when at least one citation was produced.
citationsNoCitations per article
totalFormattedNoNumber of PMIDs successfully formatted
totalSubmittedNoNumber of PMIDs submitted for citation formatting
unavailablePmidsNoPMIDs PubMed returned no record for, so nothing could be cited for them. That is all this reports: PubMed omits a PMID it does not recognize silently, with no error and no reason, so the absence says nothing about whether the PMID exists. Use `pubmed_search_articles` to find PMIDs that do resolve.

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, and the description adds behavior not in structured fields: NCBI-linked retractions, errata, and expressions of concern surface in a `notices` field, and Vancouver/APA embed retraction notices in the reference itself. This is real behavioral context beyond the annotations, though permission/rate-limit or failure behavior is not covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no filler; the capability and the format-passing mechanics are front-loaded, and the notices caveat follows in a compact clause. Slight redundancy between the parenthetical style list and the array/string sentence keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't document return values, yet it volunteers the one non-obvious output field (`notices`) and how retractions appear in references. For a two-parameter read-only tool this covers what an agent needs to call it correctly, leaving only sibling-routing guidance unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the schema already documents both parameters, including the exact enum values and the string-or-array anyOf. The description restates that alternation and the style list without adding syntax, defaulting, or interaction details beyond what the schema carries, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Get formatted citations') and resource ('PubMed articles') and enumerates the supported styles (apa, mla, bibtex, ris, vancouver). It does not, however, distinguish itself from the sibling pubmed_lookup_citation, which an agent could plausibly confuse with this tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explains how to invoke the format parameter (single string vs. array) but never says when this tool is preferred over pubmed_lookup_citation or pubmed_fetch_articles. Usage is implied by the purpose rather than stated, with no exclusions or alternative-routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_lookup_citationPubmed Lookup CitationA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
citationsYesCitations to look up, 1–25, each matched independently. More fields = better match accuracy.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
resultsNoMatch results, one per input citation
totalMatchedNoNumber of citations with PMID matches
totalWarningsNoNumber of matched citations that carry at least one warning
totalSubmittedNoNumber of citations submitted

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only cover readOnlyHint and openWorldHint, so the description carries most of the behavioral load and does so well: it discloses the ECitMatch primary-key rule (journal+volume+page), that author-only or volume-only inputs guarantee no match, and that richer input improves match accuracy. It does not mention the 1–25 batch cap (left to the schema) or any rate/limit behavior, but the added matching semantics are substantial.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the purpose before the constraints, and each sentence carries information. Minor redundancy with the schema's own item description ('more fields = better match accuracy' appears in both), which keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and annotations cover the safety profile. Combined with the required-field constraint, the no-match failure mode, and the accuracy heuristic, an agent has everything needed to build a valid call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3; the description earns above that by adding the accuracy heuristic ('more fields = better match accuracy') and the failure-mode rule that journal or a four-digit year is mandatory. Some of this overlaps the nested schema description, but the match-accuracy guidance is genuinely additional.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Look up PubMed IDs from partial bibliographic citations') and pins down the exact input shape (journal, year, volume, page, author). It implicitly separates itself from free-text search siblings by calling the matching 'deterministic' and contrasting it with 'free-text search for structured references'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear trigger: use it when you already hold a structured reference and need the PMID, and explicitly says it beats free-text search for structured references. It does not name the alternative sibling tool (e.g., pubmed_search_articles) or state when not to reach for this one, which keeps it short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_lookup_meshPubmed Lookup MeshA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
queryYesMeSH 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.
offsetNoResult 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.
maxResultsNoMaximum results
includeDetailsNoFetch full MeSH records (scope notes, tree numbers, entry terms)

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
queryNoOriginal search query
noticeNoOptional guidance when no descriptors matched or the offset overshot the result set — suggests spell-check, free-text search, or resetting the offset. Absent on successful result pages.
offsetNoResult offset this page was read from
resultsNoMatching MeSH records
nextOffsetNoOffset to request for the next page. Omitted when this is the last page, so its absence is the end-of-results signal.
totalCountNoTotal MeSH descriptors matching the query upstream, before the maxResults cap

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, so safety is covered. The description adds genuine behavioral context beyond that: the shape of the returned descriptor records (tree numbers, scope notes, entry terms) and the pagination model for paging past the maxResults cap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler, with the purpose front-loaded ahead of the return/pagination detail. Nothing is repeated or padding, though the second sentence is a dense clause stack rather than cleanly separated points.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only lookup tool with a fully documented schema and an output schema covering return values, the description supplies enough to call it correctly. It needn't explain the record fields in detail since the output schema exists, though it does so anyway.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all four parameters, including offset's nextOffset chaining and the whitespace-rejection rule for query. The description only adds a brief mention of offset-based paging, which is already in the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb pair (search/explore) and a precise resource (the MeSH controlled vocabulary), which is inherently distinguishable from the article-oriented siblings like pubmed_search_articles and pubmed_find_related. It does not explicitly name an alternative, but the resource itself separates it cleanly from every sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: an agent can infer it should call this when it needs MeSH descriptor data, but the description never says when to prefer it over pubmed_search_articles or pubmed_spell_check, nor any prerequisite for looking up a descriptor. Adequate but with a clear gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_search_articlesPubmed Search ArticlesB
Read-only
Inspect

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.

NameRequiredDescriptionDefault
sortNoSort order: relevance (default), pub_date (newest first), author, or journalrelevance
queryYesPubMed search query (supports full NCBI syntax), sent as written apart from inline formatting tags such as `<i>`, which are removed. 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.
authorNoFilter 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.
offsetNoResult 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.
journalNoFilter by journal name. An empty string applies no filter; a value of only whitespace, invisible characters, markup, parentheses, brackets, or double quotes is rejected.
speciesNoFilter by species: `humans` adds `humans[MeSH Terms]`, `animals` adds `"animals"[mh:noexp]` (animals other than humans; a record indexed for both stays in). Both match only records with MEDLINE species indexing, which preprints and ahead-of-print citations lack.
languageNoFilter 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.
dateRangeNoFilter 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.
meshTermsNoFilter 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.
maxResultsNoMaximum results to return. 0 returns only `totalCount`.
hasAbstractNoOnly include articles with abstracts
freeFullTextNoOnly include free full text articles
summaryCountNoFetch brief summaries for top N results (0 = PMIDs only). Above the 50 cap, pass the remaining PMIDs to pubmed_fetch_articles.
publicationTypesNoFilter 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.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
pmidsNoPubMed IDs
queryNoOriginal query
noticeNoOptional guidance when the result set does not reflect what was asked for — a field tag PubMed ignored, a phrase it matched nothing for, a dateRange dropped for having one bound, no matches at all, or paging past the end. Absent when nothing applies.
offsetNoResult offset used
searchUrlNoPubMed search URL
summariesNoBrief summaries (empty array when summaryCount is 0)
totalCountNoTotal matching articles
appliedFiltersNoFilter values applied to the PubMed query, as sent: each text value as written, formatting tags removed, and dates in YYYY/MM/DD form
effectiveQueryNoThe term sent to PubMed: the query as written, with a clause appended for each active filter. Only inline formatting tags with no attributes (`<i>`, `<b>`, `<em>`, `<strong>`, `<u>` and their closers) are removed; `&`, `<`, and `>` are sent as written.

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the safety profile is covered by structured data. The description adds a useful redefinition of the return shape (PMIDs, with summaries only when requested) but omits operational realities documented elsewhere, such as the 9,999-record retrieval ceiling and where summaries stop being available.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the primary action and return shape, then filter categories, then pagination. Every clause carries a distinct payload; the only mild redundancy is repeating filter families the schema already lists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 14-parameter search tool with a full output schema and annotations, the description is adequate but thin on routing: it never says that full article details come from pubmed_fetch_articles, nor when this tool beats pubmed_europepmc_search. It covers the query/return/pagination essentials but leaves the cross-tool workflow to be inferred.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents enumeration, validation, defaults, and edge cases in detail. The description adds only a grouped overview of filter categories (author, journal, MeSH, language, species, free full text), which orients the reader but contributes no semantics beyond the schema. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Search PubMed') plus the capability envelope: full query syntax, filters, date ranges. An agent can identify the core action immediately. It does not, however, distinguish itself from sibling search tools such as pubmed_europepmc_search, so the boundary between them is left implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than prescribed: the mention of returned PMIDs and optional brief summaries hints at a two-step search-then-fetch workflow with pubmed_fetch_articles, and 'pagination via offset for paging through large result sets' signals when offset matters. There is no explicit when-not-to-use or named alternative, which matters here given the Europe PMC and fetch siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pubmed_spell_checkPubmed Spell CheckA
Read-only
Inspect

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). The value of an indexed field — MeSH, publication type, journal, author, language, subset, and the like (aged[mh], free full text[sb], zhang y[au]) — is kept as written, misspelled or not, since the ESpell dictionary is not the index that field searches; free-text fields such as [tiab], [ti], and [tw] are corrected. 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.

NameRequiredDescriptionDefault
queryYesPubMed 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.

Output Schema

NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
originalNoOriginal query
correctedNoThe query with ESpell's accepted corrections applied in place; field tags, the values of indexed fields such as `aged[mh]` and `zhang y[au]`, operators, spacing, words in capitals or with digits, and a single letter ESpell would swap for another are kept as written. Same as `original` when `hasSuggestion` is false.
hasSuggestionNoWhether at least one correction was applied to `corrected`

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint and openWorldHint; the description goes far beyond them by disclosing what is and is not corrected — indexed field values ([mh], [sb], [au]) are preserved, free-text fields ([tiab], [ti], [tw]) are fixed, all-caps/digit/non-ASCII tokens are untouched, and `hasSuggestion` is false when nothing changed. These are non-obvious rules that would cause real misuse if omitted, and nothing contradicts the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and usage are front-loaded in the first two sentences, which is correct. The remaining prose is a long, clause-heavy block of edge cases; most of it earns its place because the preservation rules are genuinely counterintuitive, but the all-caps/digit/non-ASCII enumeration is denser than an agent needs and could be compressed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so the description needn't enumerate return fields — yet it still explains the one semantically loaded flag (`hasSuggestion`). Combined with annotations covering the safety and open-world profile, an agent has everything needed to call this correctly and act on the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter exists and schema description coverage is 100%, so the schema already documents the minLength and the whitespace-only rejection. The description adds no syntax, format, or example guidance specific to `query` beyond the illustrated typo rewrite, which is illustrative rather than semantic. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Spell-check a PubMed search query against NCBI ESpell') and names the exact output ('get the corrected query back'). The follow-up sentence ties it to the sibling `pubmed_search_articles` as its complement rather than a competitor, so the agent can place it in the workflow without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit triggers are given: 'after a zero-hit or thin `pubmed_search_articles` result, or when a drug, gene, disease, or author name may be misspelled.' It also closes the loop by saying to re-run the search with `corrected`, which is the alternative action an agent would otherwise have to guess.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv2.11.1
    • Changedpubmed_europepmc_fetch3 fields changed
      • changedOutput schema / properties / records / items / properties / hasFullTextXml / description
        Previous value: -"Whether Europe PMC publishes a fullTextXML for this record. Derived from `inPMC` — only records with a PMC counterpart have JATS via Europe PMC."New value: +"Whether Europe PMC publishes a fullTextXML for this record: true when Europe PMC marks it open access and lists a full-text id for it, open-access preprints (`PPR`) included; false otherwise. Absent when Europe PMC reports no open-access status."
      • changedOutput schema / properties / records / items / properties / journal / description
        Previous value: -"Journal title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."New value: +"Journal name as Europe PMC reports it — its MEDLINE abbreviation where it records one (e.g. `Otolaryngol Pol`), else the full journal title — the same name `pubmed_europepmc_search` returns for the record. Display-ready plain text — JATS/HTML markup stripped and HTML entities decoded. Absent when Europe PMC carries no journal, as for preprints."
      • addedOutput schema / properties / records / items / properties / license
        Added value: +{
        +  "description": "License Europe PMC records for this record, verbatim (e.g. `cc by`, `cc by-nc-nd`, `cc0`). Relayed as Europe PMC states it, never interpreted. Absent when Europe PMC supplies none.",
        +  "type": "string"
        +}
    • Changedpubmed_europepmc_search6 fields changed
      • changedInput schema / properties / cursorMark / description
        Previous value: -"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."New value: +"Pagination cursor. Use `*` (default) for the first page; pass the previous response's `nextCursorMark` verbatim for subsequent pages. Under relevance ranking (no `sort`), a record can reappear on the next page, so de-duplicate hits by `epmcId` across 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."
      • changedInput schema / properties / resultType / description
        Previous value: -"`core` returns abstract, IDs, dates, license; `lite` is a smaller payload with IDs and titles only."New value: +"`core` returns abstract, IDs, dates, license; `lite` is a smaller payload with the same IDs, title, authors, journal, and dates, but no abstract or license."
      • changedOutput schema / properties / hits / items / properties / epmcId / description
        Previous value: -"Europe PMC's internal record id. Pass it with this hit's `source` to `pubmed_europepmc_fetch` as `records: [{ source, epmcId }]` for the complete record. Europe PMC's `fullTextXML` is keyed on `pmcId`, not on this id, so records without a PMC counterpart have no full text to fetch."New value: +"Europe PMC's internal record id. Pass it with this hit's `source` to `pubmed_europepmc_fetch` as `records: [{ source, epmcId }]` for the complete record. `pubmed_fetch_fulltext` takes this hit's `pmcId`, `pmid`, or `doi`, not this id."
      • changedOutput schema / properties / hits / items / properties / hasFullTextXml / description
        Previous value: -"Whether Europe PMC publishes a fullTextXML for this record. Derived from `inPMC` — only records with a PMC counterpart have JATS via EPMC; preprints (`PPR`) and MED-only records return false."New value: +"Whether Europe PMC publishes a fullTextXML for this record: true when Europe PMC marks it open access and lists a full-text id for it, open-access preprints (`PPR`) included; false otherwise. Absent when Europe PMC reports no open-access status."
      • changedOutput schema / properties / hits / items / properties / journal / description
        Previous value: -"Journal title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."New value: +"Journal name as Europe PMC reports it — its MEDLINE abbreviation where it records one (e.g. `Otolaryngol Pol`), else the full journal title — the same for `resultType` `core` and `lite`. Display-ready plain text — JATS/HTML markup stripped and HTML entities decoded. Absent when Europe PMC carries no journal, as for preprints."
      • addedOutput schema / properties / hits / items / properties / license
        Added value: +{
        +  "description": "License Europe PMC records for this record, verbatim (e.g. `cc by`, `cc by-nc-nd`, `cc0`). Relayed as Europe PMC states it, never interpreted. Absent when Europe PMC supplies none, and on every `resultType: \"lite\"` result.",
        +  "type": "string"
        +}
    • Changedpubmed_fetch_articles1 field changed
      • addedOutput schema / properties / articles / items / properties / dataBanks
        Added value: +{
        +  "description": "Trial registrations and data deposits the record lists, from NCBI's `DataBankList`, in NCBI's order and uncapped — a ClinicalTrials.gov NCT ID, GenBank and PDB accessions, Dryad and figshare DOIs. To find other PubMed records citing an accession, search `pubmed_search_articles` for `<accession>[si]` (e.g. `NCT03951753[si]`). Counted with the rest of the record under `maxResponseCharacters`. Absent when the record lists none, and never set on `book-chapter` or `book` records.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "One databank or registry the record links to, with its accessions there",
        +    "properties": {
        +      "accessionNumbers": {
        +        "description": "The record's accessions in this bank, from `AccessionNumberList`, in NCBI's order and uncapped. Absent when NCBI lists the bank without one.",
        +        "items": {
        +          "description": "One accession as NCBI writes it — free text, an all-digit OMIM number or a DOI included (e.g. \"NCT03951753\", \"300460\", \"10.5061/dryad.8gtht7726\")",
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "name": {
        +        "description": "Databank or registry name, verbatim from NCBI `DataBankName` (e.g. \"ClinicalTrials.gov\", \"GENBANK\", \"PDB\", \"Dryad\", \"OMIM\").",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "name"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Addedpubmed_fetch_citation_metrics
    • Changedpubmed_fetch_fulltext8 fields changed
      • changedInput schema / properties / maxCharacters / description
        Previous value: -"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."New value: +"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, licenses, 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; a section or subsection whose allowance cannot hold its first word returns no text rather than part of that word, so it is dropped in `truncate` mode and heading-only in `outline` mode, while text that is one unbroken token, such as a sequence or URL, is cut at the allowance. 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."
      • changedInput schema / properties / maxResponseCharacters / description
        Previous value: -"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."New value: +"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`, `sectionCap`, `deferred` itself — are not counted. Omit to return every resolved article."
      • changedInput schema / properties / maxSections / description
        Previous value: -"Maximum top-level sections. Back-matter sections count and follow the body, so a cap drops them first. Applies to `source=pmc` results only."New value: +"Maximum top-level sections, counted after `sections`. Back-matter sections count and follow the body, so a cap drops them first. The sections it drops are named per article in `sectionCap`. Applies to `source=pmc` results only."
      • changedInput schema / properties / overflowMode / description
        Previous value: -"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: split the budget evenly so every section and subsection keeps its heading, and an excerpt as far as the budget reaches — 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."New value: +"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."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "assets": {
        -        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from a section, body or back matter, leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        -          "properties": {
        -            "assetType": {
        -              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        -              "enum": [
        -                "figure",
        -                "supplementary-material"
        -              ],
        -              "type": "string"
        -            },
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "href": {
        -              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Display label as printed, e.g. `Fig. 1`, less one trailing ` |`, `.` or `:` (`Figure 1.` reads `Figure 1`). Some supplements carry a bare number (`1`), kept as deposited here",
        -              "type": "string"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost titled section enclosing the asset — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. Absent for an asset inside no titled section, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "assetType"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            },
        -            "suffix": {
        -              "description": "Generational name suffix verbatim from the JATS `<suffix>` (e.g. \"Jr\", \"IV\"), never normalized. Absent when the deposit gives none.",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "elocationId": {
        -            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        -            "type": "string"
        -          },
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article sections in source order: the body, then the back matter — acknowledgments, notes and declarations, footnotes, glossary, appendices. A back-matter element with no heading of its own is titled by its kind, such as `Footnotes` or `Appendix`.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article section, from the body or from the back matter after it",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "tables": {
        -        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One table from the article, with its cells, caption, and owning section",
        -          "properties": {
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "footnotes": {
        -              "description": "`<table-wrap-foot>` text, one line per footnote or paragraph, joined by `\\n`. A footnote marker deposited as a `<label>` leads its line, followed by a space (`* No patient …`).",
        -              "type": "string"
        -            },
        -            "headerRowCount": {
        -              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        -              "type": "number"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Table label as printed, e.g. `TABLE 1`, less one trailing ` |`, `.` or `:` (`Table 1.` reads `Table 1`)",
        -              "type": "string"
        -            },
        -            "rows": {
        -              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. A line break in a cell — a `<break/>`, a paragraph, or a list item in the source — is kept as `\\n`, so `1.08\\n2.44` is two values; superscripts and other inline markup are never spaced (`0.108a`). Empty when `unextractableReason` is set.",
        -              "items": {
        -                "description": "One row, as cell text by grid column",
        -                "items": {
        -                  "type": "string"
        -                },
        -                "type": "array"
        -              },
        -              "type": "array"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost titled section enclosing the table — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. A `sections` filter keeps the table with that section. In back matter the section name is the only positional cue there is. Absent for a table inside no titled section, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            },
        -            "unextractableReason": {
        -              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        -              "enum": [
        -                "cals-tgroup",
        -                "graphic-only",
        -                "no-rows"
        -              ],
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "headerRowCount",
        -            "rows"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "journalName": {
        -        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present when a `pmcids` entry named it, absent when it was requested under a PMID or DOI. When several identifiers name one article, it is requested under the first to reach it: `pmcids`, then `pmids`, then `dois`. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID this article was requested under — present when it was requested under a `pmids` entry, absent when under a PMC ID or DOI (see `pmcId` for which identifier an article several of them name is requested under)",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      },
        -      "year": {
        -        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "assets": {
        +        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from a section, body or back matter, leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        +          "properties": {
        +            "assetType": {
        +              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        +              "enum": [
        +                "figure",
        +                "supplementary-material"
        +              ],
        +              "type": "string"
        +            },
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "href": {
        +              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Display label as printed, e.g. `Fig. 1`, less one trailing ` |`, `.` or `:` (`Figure 1.` reads `Figure 1`). Some supplements carry a bare number (`1`), kept as deposited here",
        +              "type": "string"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost titled section enclosing the asset — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. Absent for an asset inside no titled section, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "assetType"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group or consortium name, as the group's own name only: members a deposit nests inside it are not listed here or in `authors`. `pubmed_fetch_articles` with `includeInvestigators` returns a collective author's members from PubMed.",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            },
        +            "suffix": {
        +              "description": "Generational name suffix verbatim from the JATS `<suffix>` (e.g. \"Jr\", \"IV\"), never normalized. Absent when the deposit gives none.",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "elocationId": {
        +            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        +            "type": "string"
        +          },
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "licenses": {
        +        "description": "The reuse statements the article's JATS deposits, from NCBI PMC and Europe PMC alike: one entry per `<license>` in its article-meta `<permissions>`, in document order. Figure and table permissions are not included. Relayed as deposited, never interpreted. Absent when the article-meta carries no `<license>`.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One license statement the article deposits",
        +          "properties": {
        +            "text": {
        +              "description": "The license's `<license-p>` paragraphs as plain text, a blank line between paragraphs. Absent when the license deposits only a URL.",
        +              "type": "string"
        +            },
        +            "urls": {
        +              "description": "The license's distinct `xlink:href`, then `ali:license_ref` values, as deposited. Absent when the license deposits only text.",
        +              "items": {
        +                "type": "string"
        +              },
        +              "type": "array"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article sections in source order: the body, then the back matter — acknowledgments, notes and declarations, footnotes, glossary, appendices. A back-matter element with no heading of its own is titled by its kind, such as `Footnotes` or `Appendix`.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article section, from the body or from the back matter after it",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "tables": {
        +        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One table from the article, with its cells, caption, and owning section",
        +          "properties": {
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "footnotes": {
        +              "description": "`<table-wrap-foot>` text, one line per footnote or paragraph, joined by `\\n`. A footnote marker deposited as a `<label>` leads its line, followed by a space (`* No patient …`).",
        +              "type": "string"
        +            },
        +            "headerRowCount": {
        +              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        +              "type": "number"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Table label as printed, e.g. `TABLE 1`, less one trailing ` |`, `.` or `:` (`Table 1.` reads `Table 1`)",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. A line break in a cell — a `<break/>`, a paragraph, or a list item in the source — is kept as `\\n`, so `1.08\\n2.44` is two values; superscripts and other inline markup are never spaced (`0.108a`). Empty when `unextractableReason` is set.",
        +              "items": {
        +                "description": "One row, as cell text by grid column",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "type": "array"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost titled section enclosing the table — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. A `sections` filter keeps the table with that section. In back matter the section name is the only positional cue there is. Absent for a table inside no titled section, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            },
        +            "unextractableReason": {
        +              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        +              "enum": [
        +                "cals-tgroup",
        +                "graphic-only",
        +                "no-rows"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "headerRowCount",
        +            "rows"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "journalName": {
        +        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present when a `pmcids` entry named it, absent when it was requested under a PMID or DOI. When several identifiers name one article, it is requested under the first to reach it: `pmcids`, then `pmids`, then `dois`. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID this article was requested under — present when it was requested under a `pmids` entry, absent when under a PMC ID or DOI (see `pmcId` for which identifier an article several of them name is requested under)",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      },
        +      "year": {
        +        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Identifiers past the per-call limit are named with the field to resend each under. Absent when none of those applies."New value: +"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A `maxSections` cap that dropped sections names, per article, how many it kept of how many and the titles it left out, and points at `sections` or a larger cap. A response-wide budget that deferred articles names the ids to re-request. Identifiers past the per-call limit are named with the field to resend each under. Absent when none of those applies."
      • addedOutput schema / properties / sectionCap
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "The top-level sections `maxSections` left out of `source: pmc` articles, PMC- and Europe PMC-served alike. Present only when the cap dropped at least one section from a returned article. Separate from `truncation`, which accounts for characters a budget removed from the sections the cap kept: no section appears in both. Re-call with `sections` set to the titles you need, or with a larger `maxSections`, to receive them.",
        +  "properties": {
        +    "articles": {
        +      "description": "Per-article lists, in the order `articles` lists the articles, covering only the returned articles the cap cut",
        +      "items": {
        +        "additionalProperties": false,
        +        "description": "The top-level sections one article lost to `maxSections`",
        +        "properties": {
        +          "id": {
        +            "description": "Identifier for the article — PMCID, PMID, DOI, or Europe PMC id, whichever the article carries first; the id `truncation.articles[].id` uses",
        +            "type": "string"
        +          },
        +          "omittedSections": {
        +            "description": "The top-level sections past the cap, in document order, back matter included — counted after the `sections` filter, so a section the filter excluded is not here. The article returned the first `maxSections` top-level sections and had `maxSections` plus this many.",
        +            "items": {
        +              "additionalProperties": false,
        +              "description": "One top-level section the cap left out",
        +              "properties": {
        +                "label": {
        +                  "description": "Section label as printed (e.g. `2`), when the section carries one",
        +                  "type": "string"
        +                },
        +                "title": {
        +                  "description": "Section heading, when the section carries one",
        +                  "type": "string"
        +                }
        +              },
        +              "type": "object"
        +            },
        +            "type": "array"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "omittedSections"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "maxSections": {
        +      "description": "The `maxSections` cap applied",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "maxSections",
        +    "articles"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / truncated / description
        Previous value: -"True when a character budget shortened at least one returned body or withheld a whole article, or when the call named more distinct identifiers than one call fetches. Absent when every requested identifier was fetched and every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the withheld ids are in `deferred`; the unfetched ids are in `overLimit`."New value: +"True when a character budget shortened at least one returned body or withheld a whole article, when `maxSections` dropped top-level sections from a returned article, or when the call named more distinct identifiers than one call fetches. Absent when every requested identifier was fetched and every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the sections the cap dropped are in `sectionCap`; the withheld ids are in `deferred`; the unfetched ids are in `overLimit`."
    • Changedpubmed_format_citations1 field changed
      • changedOutput schema / properties / citations / items / properties / citations / description
        Previous value: -"Citations keyed by style"New value: +"Citations keyed by style. APA and MLA are Markdown — the journal, volume and book title in *italics*, every other character of the record escaped so it renders as written; BibTeX, RIS and Vancouver are plain text."
    • Changedpubmed_search_articles5 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"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."New value: +"PubMed search query (supports full NCBI syntax), sent as written apart from inline formatting tags such as `<i>`, which are removed. 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."
      • changedInput schema / properties / species / description
        Previous value: -"Filter by species"New value: +"Filter by species: `humans` adds `humans[MeSH Terms]`, `animals` adds `\"animals\"[mh:noexp]` (animals other than humans; a record indexed for both stays in). Both match only records with MEDLINE species indexing, which preprints and ahead-of-print citations lack."
      • changedOutput schema / properties / appliedFilters / description
        Previous value: -"Normalized filter values that were applied to the PubMed query"New value: +"Filter values applied to the PubMed query, as sent: each text value as written, formatting tags removed, and dates in YYYY/MM/DD form"
      • changedOutput schema / properties / appliedFilters / properties / species / description
        Previous value: -"Species filter applied to the search"New value: +"Species filter applied to the search: `humans` as `humans[MeSH Terms]`, `animals` as `\"animals\"[mh:noexp]`"
      • changedOutput schema / properties / effectiveQuery / description
        Previous value: -"Sanitized query sent to PubMed after applying all active filters"New value: +"The term sent to PubMed: the query as written, with a clause appended for each active filter. Only inline formatting tags with no attributes (`<i>`, `<b>`, `<em>`, `<strong>`, `<u>` and their closers) are removed; `&`, `<`, and `>` are sent as written."
    • Changedpubmed_spell_check1 field changed
      • changedOutput schema / properties / corrected / description
        Previous value: -"The query with ESpell's accepted corrections applied in place; field tags, operators, spacing, and words in capitals or with digits are kept as written. Same as `original` when `hasSuggestion` is false."New value: +"The query with ESpell's accepted corrections applied in place; field tags, the values of indexed fields such as `aged[mh]` and `zhang y[au]`, operators, spacing, words in capitals or with digits, and a single letter ESpell would swap for another are kept as written. Same as `original` when `hasSuggestion` is false."
  2. 7 tool updatesv2.10.22
    • Changedpubmed_europepmc_fetch3 fields changed
      • changedInput schema / properties / maxResponseCharacters / maximum
        Previous value: -1000000New value: +9007199254740991
      • changedOutput schema / properties / deferred / properties / nextDeferredCharacters / description
        Previous value: -"Serialized size of the next deferred record — the first entry in `records`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller record further down `records` cannot be reached until this one fits."New value: +"Serialized size of the next deferred record — the first entry in `records`, where the response stopped — and the continuation ceiling: re-calling with the deferred pairs needs `maxResponseCharacters` ≥ this to return anything, and a smaller record further down `records` cannot be reached until this one fits. Repeating this same call needs `maxResponseCharacters` ≥ `returnedCharacters` + this to return this record as well."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results; a sort it cannot use (one whose keys are not each a field plus asc/desc, rejected before any request; an empty response to a sort with an undocumented field; or a second HTTP 503 to a sorted search when the same query without the sort is served); or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
    • Changedpubmed_europepmc_search4 fields changed
      • changedInput schema / properties / sort / description
        Previous value: -"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). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking. Field and direction match case-insensitively. A field outside the documented set may be honored, silently ignored, or rejected, and a sort using one — or a key without `asc`/`desc` — can fail with `europepmc_invalid_input` naming it, 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` to order preprints by date."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. `blank_cursor`: The `cursorMark` holds only whitespace or invisible characters such as a zero-width space. Europe PMC cannot read it and answers with the HTTP 503 it also uses for an outage. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results; a sort it cannot use (one whose keys are not each a field plus asc/desc, rejected before any request; an empty response to a sort with an undocumented field; or a second HTTP 503 to a sorted search when the same query without the sort is served); or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. `blank_cursor`: The `cursorMark` holds only whitespace or invisible characters such as a zero-width space. Europe PMC cannot read it and answers with the HTTP 503 it also uses for an outage. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / hits / items / properties / abstractTruncated / description
        Previous value: -"Whether `abstractSnippet` was cut short of the full abstract. Retrieve the complete text with `pubmed_europepmc_fetch` using this record’s `source` and `epmcId`. Present whenever `abstractSnippet` is; omitted when Europe PMC carries no abstract."New value: +"Whether `abstractSnippet` was cut short of the full abstract. Retrieve the complete text by passing this record to `pubmed_europepmc_fetch` as `records: [{ source, epmcId }]`. Present whenever `abstractSnippet` is; omitted when Europe PMC carries no abstract."
      • changedOutput schema / properties / hits / items / properties / epmcId / description
        Previous value: -"Europe PMC's internal record id. Pass it with this hit's `source` to `pubmed_europepmc_fetch` for the complete record. Europe PMC's `fullTextXML` is keyed on `pmcId`, not on this id, so records without a PMC counterpart have no full text to fetch."New value: +"Europe PMC's internal record id. Pass it with this hit's `source` to `pubmed_europepmc_fetch` as `records: [{ source, epmcId }]` for the complete record. Europe PMC's `fullTextXML` is keyed on `pmcId`, not on this id, so records without a PMC counterpart have no full text to fetch."
    • Changedpubmed_fetch_articles8 fields changed
      • addedInput schema / properties / includeInvestigators
        Added value: +{
        +  "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.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / maxResponseCharacters / maximum
        Previous value: -1000000New value: +9007199254740991
      • changedOutput schema / properties / articles / items / properties / affiliations / description
        Previous value: -"Deduplicated author affiliations"New value: +"Deduplicated affiliations, referenced by `affiliationIndices`: the authors' entries first, then any that only `investigators` carry"
      • changedOutput schema / properties / articles / items / properties / authors / items / properties / collectiveName / description
        Previous value: -"Group/collective author name"New value: +"Group/collective author name. NCBI may list the members of the group separately; set `includeInvestigators` to return them as `investigators`."
      • addedOutput schema / properties / articles / items / properties / authors / items / properties / suffix
        Added value: +{
        +  "description": "Generational name suffix verbatim from NCBI `Suffix` (e.g. \"Jr\", \"2nd\", \"3rd\"), never normalized. Absent when NCBI gives none.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / book / properties / editors / items / properties / suffix
        Added value: +{
        +  "description": "Editor generational name suffix verbatim from NCBI `Suffix` (e.g. \"Jr\"). Absent when NCBI supplies none, or on a group editor.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / investigators
        Added value: +{
        +  "description": "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.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "One member of a collective author, from NCBI's `InvestigatorList`",
        +    "properties": {
        +      "affiliationIndices": {
        +        "description": "Indices into the top-level affiliations array, where an investigator's affiliations follow the authors'",
        +        "items": {
        +          "type": "number"
        +        },
        +        "type": "array"
        +      },
        +      "firstName": {
        +        "description": "First/given name",
        +        "type": "string"
        +      },
        +      "initials": {
        +        "description": "Initials",
        +        "type": "string"
        +      },
        +      "lastName": {
        +        "description": "Last name",
        +        "type": "string"
        +      },
        +      "orcid": {
        +        "description": "ORCID identifier",
        +        "type": "string"
        +      },
        +      "suffix": {
        +        "description": "Generational name suffix verbatim from NCBI `Suffix` (e.g. \"Jr\", \"2nd\"). Absent when NCBI gives none.",
        +        "type": "string"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / deferred / properties / nextDeferredCharacters / description
        Previous value: -"Serialized size of the next deferred article — the first entry in `ids`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller article further down `ids` cannot be reached until this one fits."New value: +"Serialized size of the next deferred article — the first entry in `ids`, where the response stopped — and the continuation ceiling: re-calling with the deferred PMIDs needs `maxResponseCharacters` ≥ this to return anything, and a smaller article further down `ids` cannot be reached until this one fits. Repeating this same call needs `maxResponseCharacters` ≥ `returnedCharacters` + this to return this article as well."
    • Changedpubmed_fetch_fulltext32 fields changed
      • changedInput schema / properties / dois / description
        Previous value: -"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]), one per element. Combinable with `pmcids` and `pmids` — at most 10 distinct identifiers across the three. 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."New value: +"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."
      • changedInput schema / properties / dois / maxItems
        Previous value: -10New value: +50
      • changedInput schema / properties / maxCharacters / description
        Previous value: -"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. Sections are served first, then tables, then assets, each spending what is left, in document order — 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`. 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."New value: +"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."
      • changedInput schema / properties / maxCharacters / maximum
        Previous value: -1000000New value: +9007199254740991
      • changedInput schema / properties / maxCharactersPerSection / description
        Previous value: -"Budget for a single top-level body 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."New value: +"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."
      • changedInput schema / properties / maxCharactersPerSection / maximum
        Previous value: -1000000New value: +9007199254740991
      • changedInput schema / properties / maxResponseCharacters / description
        Previous value: -"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, body 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 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, `unavailable`, `truncation`, `deferred` itself — are not counted. Omit to return every resolved article."New value: +"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."
      • changedInput schema / properties / maxResponseCharacters / maximum
        Previous value: -1000000New value: +9007199254740991
      • changedInput schema / properties / maxSections / description
        Previous value: -"Maximum top-level body sections. Applies to `source=pmc` results only."New value: +"Maximum top-level sections. Back-matter sections count and follow the body, so a cap drops them first. Applies to `source=pmc` results only."
      • changedInput schema / properties / maxSections / maximum
        Previous value: -50New value: +9007199254740991
      • changedInput schema / properties / overflowMode / description
        Previous value: -"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: split the budget evenly so every section and subsection keeps its heading, and an excerpt as far as the budget reaches — a heading the budget left empty is marked as such in the rendered text. 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."New value: +"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: split the budget evenly so every section and subsection keeps its heading, and an excerpt as far as the budget reaches — 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."
      • changedInput schema / properties / pmcids / description
        Previous value: -"PMC IDs to fetch (e.g. [\"PMC9575052\"]). Combinable with `pmids` and `dois` — at most 10 distinct identifiers across the three. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them."New value: +"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."
      • changedInput schema / properties / pmcids / maxItems
        Previous value: -10New value: +50
      • changedInput schema / properties / pmids / description
        Previous value: -"PubMed IDs. Combinable with `pmcids` and `dois` — at most 10 distinct identifiers across the three. 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."New value: +"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."
      • changedInput schema / properties / pmids / maxItems
        Previous value: -10New value: +50
      • changedInput schema / properties / sections / description
        Previous value: -"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\". 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."New value: +"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."
      • changedOutput schema / properties / articles / description
        Previous value: -"Full-text articles"New value: +"Full-text articles in routing order — `pmcids`, then `pmids`, then `dois`, request order within each — each at the position of the id it would be deferred under, whichever tier served it. An article several ids name appears once."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "assets": {
        -        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        -          "properties": {
        -            "assetType": {
        -              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        -              "enum": [
        -                "figure",
        -                "supplementary-material"
        -              ],
        -              "type": "string"
        -            },
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "href": {
        -              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Display label as printed, e.g. `Fig. 1`",
        -              "type": "string"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "assetType"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "elocationId": {
        -            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        -            "type": "string"
        -          },
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "tables": {
        -        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One table from the article, with its cells, caption, and owning section",
        -          "properties": {
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "footnotes": {
        -              "description": "`<table-wrap-foot>` text, one line per footnote or paragraph, joined by `\\n`. A footnote marker deposited as a `<label>` leads its line, followed by a space (`* No patient …`).",
        -              "type": "string"
        -            },
        -            "headerRowCount": {
        -              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        -              "type": "number"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Table label as printed, e.g. `TABLE 1`",
        -              "type": "string"
        -            },
        -            "rows": {
        -              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. A line break in a cell — a `<break/>`, a paragraph, or a list item in the source — is kept as `\\n`, so `1.08\\n2.44` is two values; superscripts and other inline markup are never spaced (`0.108a`). Empty when `unextractableReason` is set.",
        -              "items": {
        -                "description": "One row, as cell text by grid column",
        -                "items": {
        -                  "type": "string"
        -                },
        -                "type": "array"
        -              },
        -              "type": "array"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            },
        -            "unextractableReason": {
        -              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        -              "enum": [
        -                "cals-tgroup",
        -                "graphic-only",
        -                "no-rows"
        -              ],
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "headerRowCount",
        -            "rows"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "journalName": {
        -        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present when a `pmcids` entry named it, absent when it was requested under a PMID or DOI. When several identifiers name one article, it is requested under the first to reach it: `pmcids`, then `pmids`, then `dois`. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID this article was requested under — present when it was requested under a `pmids` entry, absent when under a PMC ID or DOI (see `pmcId` for which identifier an article several of them name is requested under)",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      },
        -      "year": {
        -        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "assets": {
        +        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from a section, body or back matter, leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        +          "properties": {
        +            "assetType": {
        +              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        +              "enum": [
        +                "figure",
        +                "supplementary-material"
        +              ],
        +              "type": "string"
        +            },
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "href": {
        +              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Display label as printed, e.g. `Fig. 1`, less one trailing ` |`, `.` or `:` (`Figure 1.` reads `Figure 1`). Some supplements carry a bare number (`1`), kept as deposited here",
        +              "type": "string"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost titled section enclosing the asset — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. Absent for an asset inside no titled section, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "assetType"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            },
        +            "suffix": {
        +              "description": "Generational name suffix verbatim from the JATS `<suffix>` (e.g. \"Jr\", \"IV\"), never normalized. Absent when the deposit gives none.",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "elocationId": {
        +            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        +            "type": "string"
        +          },
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article sections in source order: the body, then the back matter — acknowledgments, notes and declarations, footnotes, glossary, appendices. A back-matter element with no heading of its own is titled by its kind, such as `Footnotes` or `Appendix`.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article section, from the body or from the back matter after it",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "tables": {
        +        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One table from the article, with its cells, caption, and owning section",
        +          "properties": {
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "footnotes": {
        +              "description": "`<table-wrap-foot>` text, one line per footnote or paragraph, joined by `\\n`. A footnote marker deposited as a `<label>` leads its line, followed by a space (`* No patient …`).",
        +              "type": "string"
        +            },
        +            "headerRowCount": {
        +              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        +              "type": "number"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Table label as printed, e.g. `TABLE 1`, less one trailing ` |`, `.` or `:` (`Table 1.` reads `Table 1`)",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. A line break in a cell — a `<break/>`, a paragraph, or a list item in the source — is kept as `\\n`, so `1.08\\n2.44` is two values; superscripts and other inline markup are never spaced (`0.108a`). Empty when `unextractableReason` is set.",
        +              "items": {
        +                "description": "One row, as cell text by grid column",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "type": "array"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost titled section enclosing the table — a body section or a back-matter one such as an appendix — as that section is titled in `sections`, or, for a heading nested deeper than `sections` carries, as its heading line inside the text of the section that contains it. A `sections` filter keeps the table with that section. In back matter the section name is the only positional cue there is. Absent for a table inside no titled section, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            },
        +            "unextractableReason": {
        +              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        +              "enum": [
        +                "cals-tgroup",
        +                "graphic-only",
        +                "no-rows"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "headerRowCount",
        +            "rows"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "journalName": {
        +        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present when a `pmcids` entry named it, absent when it was requested under a PMID or DOI. When several identifiers name one article, it is requested under the first to reach it: `pmcids`, then `pmids`, then `dois`. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID this article was requested under — present when it was requested under a `pmids` entry, absent when under a PMC ID or DOI (see `pmcId` for which identifier an article several of them name is requested under)",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      },
        +      "year": {
        +        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / properties / deferred / properties / dois / description
        Previous value: -"The deferred ids sent as DOIs, in response order — re-submit them as `dois`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a DOI."New value: +"The deferred ids sent as DOIs, in routing order — re-submit them as `dois`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a DOI."
      • changedOutput schema / properties / deferred / properties / ids / description
        Previous value: -"Identifiers of the deferred articles, in response order, keyed as they were requested (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` field — or, when `idType` is absent, with the `pmcids` / `pmids` / `dois` lists below — and the same other inputs. Never contains an id from `unavailable`."New value: +"Identifiers of the deferred articles, in routing order — `pmcids`, then `pmids`, then `dois`, request order within each — keyed as they were requested (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` field — or, when `idType` is absent, with the `pmcids` / `pmids` / `dois` lists below — and the same other inputs. Never contains an id from `unavailable`."
      • changedOutput schema / properties / deferred / properties / nextDeferredCharacters / description
        Previous value: -"Serialized size of the next deferred article — the first entry in `ids`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller article further down `ids` cannot be reached until this one fits."New value: +"Serialized size of the next deferred article — the first entry in `ids`, where the response stopped — and the continuation ceiling: re-calling with the deferred ids needs `maxResponseCharacters` ≥ this to return anything, and a smaller article further down `ids` cannot be reached until this one fits. Repeating this same call needs `maxResponseCharacters` ≥ `returnedCharacters` + this to return this article as well."
      • changedOutput schema / properties / deferred / properties / pmcids / description
        Previous value: -"The deferred ids sent as PMC IDs, in response order — re-submit them as `pmcids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMC ID."New value: +"The deferred ids sent as PMC IDs, in routing order — re-submit them as `pmcids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMC ID."
      • changedOutput schema / properties / deferred / properties / pmids / description
        Previous value: -"The deferred ids sent as PMIDs, in response order — re-submit them as `pmids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMID."New value: +"The deferred ids sent as PMIDs, in routing order — re-submit them as `pmids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMID."
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Absent when none of those applies."New value: +"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Identifiers past the per-call limit are named with the field to resend each under. Absent when none of those applies."
      • addedOutput schema / properties / overLimit
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Identifiers the call named past the per-call fetch limit, returned unfetched for a follow-up call. Present only when the call named more than `limit` distinct identifiers. Separate from `deferred`, which lists articles that were fetched but withheld by `maxResponseCharacters`.",
        +  "properties": {
        +    "dois": {
        +      "description": "The over-limit ids sent as DOIs, in routing order — re-submit them as `dois`. Present only when the over-limit ids came from more than one field (so `idType` is absent) and one of them was a DOI.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "idType": {
        +      "description": "The input field every over-limit id was sent in — re-submit them as `pmids`, `pmcids`, or `dois` respectively. Absent when the over-limit ids came from more than one field; `pmcids` / `pmids` / `dois` then group them.",
        +      "enum": [
        +        "pmid",
        +        "pmcid",
        +        "doi"
        +      ],
        +      "type": "string"
        +    },
        +    "ids": {
        +      "description": "Identifiers past the per-call limit, none of them fetched, in routing order — each listed once, under the first spelling sent (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` field — or, when `idType` is absent, with the `pmcids` / `pmids` / `dois` lists below — and the same other inputs, at most `limit` per call. Never contains an id from `unavailable` or `deferred`.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "limit": {
        +      "description": "Distinct identifiers one call fetches. The first this many, in routing order — `pmcids`, then `pmids`, then `dois`, request order within each — were fetched; the ids below were not.",
        +      "type": "number"
        +    },
        +    "pmcids": {
        +      "description": "The over-limit ids sent as PMC IDs, in routing order — re-submit them as `pmcids`. Present only when the over-limit ids came from more than one field (so `idType` is absent) and one of them was a PMC ID.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "pmids": {
        +      "description": "The over-limit ids sent as PMIDs, in routing order — re-submit them as `pmids`. Present only when the over-limit ids came from more than one field (so `idType` is absent) and one of them was a PMID.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "limit",
        +    "ids"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / truncated / description
        Previous value: -"True when a character budget shortened at least one returned body, or withheld a whole article. Absent when every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the withheld ids are in `deferred`."New value: +"True when a character budget shortened at least one returned body or withheld a whole article, or when the call named more distinct identifiers than one call fetches. Absent when every requested identifier was fetched and every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the withheld ids are in `deferred`; the unfetched ids are in `overLimit`."
      • changedOutput schema / properties / truncation / properties / articles / items / properties / omittedAssetNames / description
        Previous value: -"The dropped assets by name, in document order — each asset's label, else its `id`, else `asset <n>` for its position in the article. Contiguous for the same reason `omittedTableNames` is: admission stops at the first asset that did not fit rather than skipping ahead to a smaller one. Absent when none were dropped."New value: +"The dropped assets by name, in document order — each asset's label (a bare-number label led by its type, `Supplementary material 1`), else its `id`, else `asset <n>` for its position in the article. Contiguous for the same reason `omittedTableNames` is: admission stops at the first asset that did not fit rather than skipping ahead to a smaller one. Absent when none were dropped."
      • changedOutput schema / properties / truncation / properties / articles / items / properties / omittedAssets / description
        Previous value: -"Figures and supplementary items this article dropped whole because the budget left no room once sections and tables were served. An asset is never returned with a truncated caption, so it is either returned complete or counted here. Absent when none were dropped."New value: +"Figures and supplementary items this article dropped whole because the budget left no room once body sections and tables were served. An asset is never returned with a truncated caption, so it is either returned complete or counted here. Absent when none were dropped."
      • changedOutput schema / properties / truncation / properties / articles / items / properties / sections / items / description
        Previous value: -"Character accounting for one body section of a budgeted article"New value: +"Character accounting for one section of a budgeted article"
      • changedOutput schema / properties / truncation / properties / articles / items / properties / source / description
        Previous value: -"Which output shape was budgeted: `pmc` budgets body sections and subsections, `unpaywall` budgets the single `content` body"New value: +"Which output shape was budgeted: `pmc` budgets sections and subsections, `unpaywall` budgets the single `content` body"
      • changedOutput schema / properties / truncation / properties / omittedSections / description
        Previous value: -"Body sections and subsections dropped entirely because an article budget was exhausted before reaching them. A dropped section counts once, together with its subsections. Always 0 in `outline` mode, which keeps every heading."New value: +"Sections and subsections, back matter included, dropped entirely because an article budget was exhausted before reaching them. Back matter is reached last, after tables and assets. A dropped section counts once, together with its subsections. Always 0 in `outline` mode, which keeps every heading."
      • changedOutput schema / properties / unavailable / description
        Previous value: -"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` names the field each id was sent in. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both."New value: +"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` names the field each id was sent in. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both. Ids past the per-call limit are listed in `overLimit`, never here."
    • Changedpubmed_format_citations1 field changed
      • addedOutput schema / properties / citations / items / properties / notices
        Added value: +{
        +  "description": "Retraction, erratum, and expression-of-concern notices NCBI links to this article, in NCBI's order. Vancouver notes each `RetractionIn` and `ErratumIn` after the reference and APA each `RetractionIn`; the other types, and MLA, BibTeX, and RIS, carry none, so check this before citing. Absent when NCBI links none.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "A linked notice that qualifies the cited article",
        +    "properties": {
        +      "note": {
        +        "description": "NCBI's note on the link, e.g. the correction an erratum makes. Absent when NCBI gives none.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PMID of the notice — pass it to `pubmed_fetch_articles` to read it. Absent when the notice has no PMID, as with many errata.",
        +        "type": "string"
        +      },
        +      "refSource": {
        +        "description": "Citation of the notice as NCBI writes it (e.g. \"Lancet. 2010 Feb 6;375(9713):445. doi: 10.1016/S0140-6736(10)60175-4.\").",
        +        "type": "string"
        +      },
        +      "refType": {
        +        "description": "NCBI's `RefType` for the link: RetractionIn, RetractedandRepublishedIn, ExpressionOfConcernIn, ErratumIn, or CorrectedandRepublishedIn.",
        +        "enum": [
        +          "RetractionIn",
        +          "RetractedandRepublishedIn",
        +          "ExpressionOfConcernIn",
        +          "ErratumIn",
        +          "CorrectedandRepublishedIn"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "refType",
        +      "refSource"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Changedpubmed_lookup_mesh3 fields changed
      • changedOutput schema / properties / results / items / properties / meshId / description
        Previous value: -"Canonical MeSH DescriptorUI (e.g. \"D003924\") — resolves at the MeSH Browser and NLM linked data. Falls back to the raw Entrez UID when a record is not decodable."New value: +"Canonical MeSH unique identifier (e.g. \"D003924\", \"C000626284\", \"Q000009\") — resolves at the MeSH Browser and NLM linked data. A pharmacological-action record carries its descriptor's UI. Omitted only when eSummary returned no record for this UID."
      • changedOutput schema / properties / results / items / properties / treeNumbers / description
        Previous value: -"Navigable MeSH tree numbers (e.g. \"D02.078.370.141.450\"). Omitted for supplementary concept records (SCRs), which map to a heading rather than occupying a tree position."New value: +"Navigable MeSH tree numbers (e.g. \"D02.078.370.141.450\"). Omitted for supplementary concept records (SCRs), which map to a heading rather than occupying a tree position, and for pharmacological-action records, whose descriptor carries the tree positions."
      • changedOutput schema / properties / results / items / required
        Previous value: -[
        -  "meshId",
        -  "entrezUid",
        -  "name"
        -]New value: +[
        +  "entrezUid",
        +  "name"
        +]
    • Changedpubmed_spell_check2 fields changed
      • changedOutput schema / properties / corrected / description
        Previous value: -"Corrected query (same as original if no suggestion)"New value: +"The query with ESpell's accepted corrections applied in place; field tags, operators, spacing, and words in capitals or with digits are kept as written. Same as `original` when `hasSuggestion` is false."
      • changedOutput schema / properties / hasSuggestion / description
        Previous value: -"Whether NCBI suggested a correction"New value: +"Whether at least one correction was applied to `corrected`"
  3. 6 tool updatesv2.10.20
    • Changedpubmed_europepmc_fetch6 fields changed
      • addedInput schema / properties / maxResponseCharacters
        Added value: +{
        +  "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.",
        +  "maximum": 1000000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / deferred
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Continuation state for records the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one record.",
        +  "properties": {
        +    "deferredCount": {
        +      "description": "Records that resolved but were withheld to stay under the ceiling",
        +      "type": "number"
        +    },
        +    "maxResponseCharacters": {
        +      "description": "The `maxResponseCharacters` ceiling this response was budgeted against",
        +      "type": "number"
        +    },
        +    "nextDeferredCharacters": {
        +      "description": "Serialized size of the next deferred record — the first entry in `records`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller record further down `records` cannot be reached until this one fits.",
        +      "type": "number"
        +    },
        +    "records": {
        +      "description": "The deferred records, in response order, each as the resolved record's own `source` + `epmcId` — a `PMC` request answered by its `MED` record is listed as that `MED` pair. Pass them back as `records` to retrieve them. Never contains a pair from `notFound`.",
        +      "items": {
        +        "additionalProperties": false,
        +        "description": "One deferred record, addressed by its own `source` + `epmcId`",
        +        "properties": {
        +          "epmcId": {
        +            "description": "Europe PMC's internal record id",
        +            "type": "string"
        +          },
        +          "source": {
        +            "description": "Europe PMC source the record was resolved from",
        +            "enum": [
        +              "MED",
        +              "PMC",
        +              "PPR",
        +              "PAT",
        +              "AGR"
        +            ],
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "source",
        +          "epmcId"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "returnedCharacters": {
        +      "description": "Serialized characters the returned records account for",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "maxResponseCharacters",
        +    "returnedCharacters",
        +    "deferredCount",
        +    "records",
        +    "nextDeferredCharacters"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / notFound / description
        Previous value: -"Requested `source` + `epmcId` pairs Europe PMC returned no record for"New value: +"Requested `source` + `epmcId` pairs Europe PMC returned no record for. Reported in full regardless of where a `maxResponseCharacters` cut lands — these are misses, not deferrals."
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance when one or more requested records could not be resolved. Absent when every record came back."New value: +"Guidance when one or more requested records could not be resolved, or when `maxResponseCharacters` deferred records — naming how to retrieve them. Absent when every requested record came back."
      • changedOutput schema / properties / records / description
        Previous value: -"Resolved records, in the order Europe PMC returned them"New value: +"Resolved records, in the order Europe PMC returned them. Under a `maxResponseCharacters` budget, only the leading records that fit; `deferred.records` lists the rest."
      • addedOutput schema / properties / truncated
        Added value: +{
        +  "description": "True when `maxResponseCharacters` withheld at least one resolved record. Absent when the response carries every record that resolved. The continuation state is in `deferred`.",
        +  "type": "boolean"
        +}
    • Changedpubmed_fetch_articles2 fields changed
      • changedOutput schema / properties / articles / items / properties / articleDates / description
        Previous value: -"Article dates"New value: +"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."
      • changedOutput schema / properties / articles / items / properties / grantList / items / properties / acronym / description
        Previous value: -"Grant acronym"New value: +"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`."
    • Changedpubmed_fetch_fulltext15 fields changed
      • changedInput schema / properties / dois / description
        Previous value: -"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]), one per element. Provide exactly one of `pmcids`, `pmids`, or `dois`. 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."New value: +"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]), one per element. Combinable with `pmcids` and `pmids` — at most 10 distinct identifiers across the three. 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."
      • changedInput schema / properties / pmcids / description
        Previous value: -"PMC IDs to fetch (e.g. [\"PMC9575052\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them."New value: +"PMC IDs to fetch (e.g. [\"PMC9575052\"]). Combinable with `pmids` and `dois` — at most 10 distinct identifiers across the three. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them."
      • changedInput schema / properties / pmids / description
        Previous value: -"PubMed IDs. Provide exactly one of `pmcids`, `pmids`, or `dois`. 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."New value: +"PubMed IDs. Combinable with `pmcids` and `dois` — at most 10 distinct identifiers across the three. 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."
      • changedInput schema / properties / sections / description
        Previous value: -"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\". 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. Applies to `source=pmc` results only."New value: +"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\". 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."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "assets": {
        -        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        -          "properties": {
        -            "assetType": {
        -              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        -              "enum": [
        -                "figure",
        -                "supplementary-material"
        -              ],
        -              "type": "string"
        -            },
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "href": {
        -              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Display label as printed, e.g. `Fig. 1`",
        -              "type": "string"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "assetType"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "elocationId": {
        -            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        -            "type": "string"
        -          },
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "tables": {
        -        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One table from the article, with its cells, caption, and owning section",
        -          "properties": {
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "footnotes": {
        -              "description": "`<table-wrap-foot>` text, flattened to one string",
        -              "type": "string"
        -            },
        -            "headerRowCount": {
        -              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        -              "type": "number"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Table label as printed, e.g. `TABLE 1`",
        -              "type": "string"
        -            },
        -            "rows": {
        -              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. Empty when `unextractableReason` is set.",
        -              "items": {
        -                "description": "One row, as cell text by grid column",
        -                "items": {
        -                  "type": "string"
        -                },
        -                "type": "array"
        -              },
        -              "type": "array"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            },
        -            "unextractableReason": {
        -              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        -              "enum": [
        -                "cals-tgroup",
        -                "graphic-only",
        -                "no-rows"
        -              ],
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "headerRowCount",
        -            "rows"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "journalName": {
        -        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      },
        -      "year": {
        -        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "assets": {
        +        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        +          "properties": {
        +            "assetType": {
        +              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        +              "enum": [
        +                "figure",
        +                "supplementary-material"
        +              ],
        +              "type": "string"
        +            },
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "href": {
        +              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Display label as printed, e.g. `Fig. 1`",
        +              "type": "string"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "assetType"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "elocationId": {
        +            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        +            "type": "string"
        +          },
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article body sections",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article body section",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "tables": {
        +        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One table from the article, with its cells, caption, and owning section",
        +          "properties": {
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "footnotes": {
        +              "description": "`<table-wrap-foot>` text, one line per footnote or paragraph, joined by `\\n`. A footnote marker deposited as a `<label>` leads its line, followed by a space (`* No patient …`).",
        +              "type": "string"
        +            },
        +            "headerRowCount": {
        +              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        +              "type": "number"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Table label as printed, e.g. `TABLE 1`",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. A line break in a cell — a `<break/>`, a paragraph, or a list item in the source — is kept as `\\n`, so `1.08\\n2.44` is two values; superscripts and other inline markup are never spaced (`0.108a`). Empty when `unextractableReason` is set.",
        +              "items": {
        +                "description": "One row, as cell text by grid column",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "type": "array"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            },
        +            "unextractableReason": {
        +              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        +              "enum": [
        +                "cals-tgroup",
        +                "graphic-only",
        +                "no-rows"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "headerRowCount",
        +            "rows"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "journalName": {
        +        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present when a `pmcids` entry named it, absent when it was requested under a PMID or DOI. When several identifiers name one article, it is requested under the first to reach it: `pmcids`, then `pmids`, then `dois`. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID this article was requested under — present when it was requested under a `pmids` entry, absent when under a PMC ID or DOI (see `pmcId` for which identifier an article several of them name is requested under)",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      },
        +      "year": {
        +        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedOutput schema / properties / deferred / properties / dois
        Added value: +{
        +  "description": "The deferred ids sent as DOIs, in response order — re-submit them as `dois`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a DOI.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / deferred / properties / idType / description
        Previous value: -"Which input branch the deferred ids belong to — re-submit them as `pmids`, `pmcids`, or `dois` respectively. Matches the `idType` on `unavailable` entries."New value: +"The input field every deferred id was sent in — re-submit them as `pmids`, `pmcids`, or `dois` respectively. Matches the `idType` on `unavailable` entries. Absent when the deferred ids came from more than one field; `pmcids` / `pmids` / `dois` then group them."
      • changedOutput schema / properties / deferred / properties / ids / description
        Previous value: -"Identifiers of the deferred articles, in response order, keyed as they were requested (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` branch and the same other inputs. Never contains an id from `unavailable`."New value: +"Identifiers of the deferred articles, in response order, keyed as they were requested (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` field — or, when `idType` is absent, with the `pmcids` / `pmids` / `dois` lists below — and the same other inputs. Never contains an id from `unavailable`."
      • addedOutput schema / properties / deferred / properties / pmcids
        Added value: +{
        +  "description": "The deferred ids sent as PMC IDs, in response order — re-submit them as `pmcids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMC ID.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / deferred / properties / pmids
        Added value: +{
        +  "description": "The deferred ids sent as PMIDs, in response order — re-submit them as `pmids`. Present only when the deferred ids came from more than one field (so `idType` is absent) and one of them was a PMID.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / deferred / required
        Previous value: -[
        -  "maxResponseCharacters",
        -  "returnedCharacters",
        -  "deferredCount",
        -  "idType",
        -  "ids",
        -  "nextDeferredCharacters"
        -]New value: +[
        +  "maxResponseCharacters",
        +  "returnedCharacters",
        +  "deferredCount",
        +  "ids",
        +  "nextDeferredCharacters"
        +]
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_filter`: A `sections` element holds no visible character — only whitespace, or invisible characters such as a zero-width space — so it names no heading. An exactly-empty element is not blank here; it is skipped. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_rate_limited",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_filter"
        +]
      • changedOutput schema / properties / unavailable / description
        Previous value: -"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` discriminates which branch the id came from. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both."New value: +"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` names the field each id was sent in. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both."
      • changedOutput schema / properties / unavailable / items / properties / idType / description
        Previous value: -"Which input branch the id came from"New value: +"Which input field the id was sent in — `pmids`, `pmcids`, or `dois` respectively"
    • Changedpubmed_find_related1 field changed
      • changedOutput schema / properties / articles / items / properties / pubDate / description
        Previous value: -"Publication date"New value: +"Publication date as YYYY-MM-DD; a month- or year-only date reads as the first day of that month or year. On an NCBI Bookshelf chapter (`docType` \"chapter\") it is the chapter's own date — its last revision, otherwise its contribution — not the year its book began."
    • Changedpubmed_lookup_citation7 fields changed
      • changedInput schema / properties / citations / items / description
        Previous value: -"Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match."New value: +"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."
      • changedInput schema / properties / citations / items / properties / authorName / description
        Previous value: -"Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break."New value: +"Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
      • changedInput schema / properties / citations / items / properties / firstPage / description
        Previous value: -"First page number. Cannot contain a pipe (\"|\") or a line break."New value: +"First page number. Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
      • changedInput schema / properties / citations / items / properties / journal / description
        Previous value: -"Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break."New value: +"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."
      • changedInput schema / properties / citations / items / properties / volume / description
        Previous value: -"Volume number. Cannot contain a pipe (\"|\") or a line break."New value: +"Volume number. Must contain a visible character, and cannot contain a pipe (\"|\") or a line break."
      • changedInput schema / properties / citations / items / properties / year / description
        Previous value: -"Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break."New value: +"Publication year as four digits (e.g., \"1991\")."
      • changedInput schema / properties / citations / items / properties / year / pattern
        Previous value: -"^[^|\\r\\n]*$"New value: +"^[ \\t]*\\d{4}[ \\t]*$"
    • Changedpubmed_search_articles5 fields changed
      • changedInput schema / properties / dateRange / properties / maxDate / description
        Previous value: -"End date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound. Must be a real calendar date — `2023/02/29` is rejected."New value: +"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`."
      • changedInput schema / properties / dateRange / properties / minDate / description
        Previous value: -"Start date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound. Must be a real calendar date — `2023/02/29` is rejected."New value: +"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`."
      • changedInput schema / properties / maxResults / description
        Previous value: -"Maximum results to return"New value: +"Maximum results to return. 0 returns only `totalCount`."
      • changedInput schema / properties / maxResults / minimum
        Previous value: -1New value: +0
      • changedOutput schema / properties / summaries / items / properties / pubDate / description
        Previous value: -"Publication date"New value: +"Publication date as YYYY-MM-DD; a month- or year-only date reads as the first day of that month or year. On an NCBI Bookshelf chapter (`docType` \"chapter\") it is the chapter's own date — its last revision, otherwise its contribution — not the year its book began."
  4. 10 tool updatesv2.10.19
    • Changedpubmed_convert_ids2 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `malformed_id`: An `ids` element does not match the declared `idType` — most often several identifiers packed into one element, which the comma-delimited upstream batch would split into extra records. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `malformed_id`: An `ids` element does not match the declared `idType` — most often several identifiers packed into one element, which the comma-delimited upstream batch would split into extra records. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "malformed_id"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "malformed_id"
        +]
    • Changedpubmed_europepmc_fetch1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
    • Changedpubmed_europepmc_search4 fields changed
      • changedInput schema / properties / cursorMark / description
        Previous value: -"Pagination cursor. Use `*` (default) for the first page; pass the previous response's `nextCursorMark` for subsequent pages."New value: +"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."
      • changedInput schema / properties / query / description
        Previous value: -"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."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. `blank_cursor`: The `cursorMark` holds only whitespace or invisible characters such as a zero-width space. Europe PMC cannot read it and answers with the HTTP 503 it also uses for an outage. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "europepmc_unreachable",
        -  "europepmc_invalid_response",
        -  "europepmc_invalid_input",
        -  "europepmc_disabled"
        -]New value: +[
        +  "europepmc_unreachable",
        +  "europepmc_invalid_response",
        +  "europepmc_invalid_input",
        +  "blank_query",
        +  "blank_cursor",
        +  "europepmc_disabled"
        +]
    • Changedpubmed_fetch_articles3 fields changed
      • addedOutput schema / properties / articles / items / properties / commentsCorrections
        Added value: +{
        +  "description": "Records NCBI links to this article — for example retraction notices, errata, expressions of concern, comments, and updates — from `CommentsCorrectionsList`, in NCBI's order and uncapped. `Cites` entries are excluded: they list a bibliography, which `pubmed_find_related` covers with its `references` relationship. Read this alongside `publicationTypes`, not in place of it: that field describes the record itself, and a corrected or questioned article often carries no matching type. Absent when NCBI links nothing other than `Cites` entries, and never set on `book-chapter` or `book` records.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "One record NCBI links to this article and published separately from it — for example a retraction notice, erratum, expression of concern, comment, update, or republication.",
        +    "properties": {
        +      "note": {
        +        "description": "NCBI's note on the link, e.g. what an erratum corrected (\"Fuβer, Fabian [corrected to Fußer, Fabian]\"). Absent unless NCBI supplies one.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PMID of the linked record — pass it to `pubmed_fetch_articles` to read that record. Absent when the linked record has no PMID, as with many errata.",
        +        "type": "string"
        +      },
        +      "refSource": {
        +        "description": "Citation of the linked record as NCBI writes it (e.g. \"Lancet. 2010 Feb 6;375(9713):445. doi: 10.1016/S0140-6736(10)60175-4.\").",
        +        "type": "string"
        +      },
        +      "refType": {
        +        "description": "Link type, verbatim from NCBI's `RefType` — e.g. \"RetractionIn\", \"RetractionOf\", \"ErratumIn\", \"ErratumFor\", \"ExpressionOfConcernIn\", \"CommentIn\", \"CommentOn\", \"UpdateIn\". The set is open: treat an unfamiliar value as opaque.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "refType",
        +      "refSource"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `invalid_efetch_response`: NCBI EFetch returned a payload missing the PubmedArticleSet wrapper. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `invalid_efetch_response`: NCBI EFetch returned a payload missing the PubmedArticleSet wrapper. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "invalid_efetch_response"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "invalid_efetch_response"
        +]
    • Changedpubmed_fetch_fulltext2 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found"
        +]
    • Changedpubmed_format_citations2 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found"
        +]
    • Changedpubmed_lookup_citation8 fields changed
      • removedInput schema / properties / citations / anyOf
        Removed value: -[
        -  {
        -    "description": "Up to 25 citations, each matched independently.",
        -    "items": {
        -      "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        -      "properties": {
        -        "authorName": {
        -          "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        -          "pattern": "^[^|\\r\\n]*$",
        -          "type": "string"
        -        },
        -        "firstPage": {
        -          "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        -          "pattern": "^[^|\\r\\n]*$",
        -          "type": "string"
        -        },
        -        "journal": {
        -          "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        -          "pattern": "^[^|\\r\\n]*$",
        -          "type": "string"
        -        },
        -        "key": {
        -          "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.",
        -          "type": "string"
        -        },
        -        "volume": {
        -          "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        -          "pattern": "^[^|\\r\\n]*$",
        -          "type": "string"
        -        },
        -        "year": {
        -          "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        -          "pattern": "^[^|\\r\\n]*$",
        -          "type": "string"
        -        }
        -      },
        -      "type": "object"
        -    },
        -    "maxItems": 25,
        -    "minItems": 1,
        -    "type": "array"
        -  },
        -  {
        -    "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        -    "properties": {
        -      "authorName": {
        -        "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        -        "pattern": "^[^|\\r\\n]*$",
        -        "type": "string"
        -      },
        -      "firstPage": {
        -        "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        -        "pattern": "^[^|\\r\\n]*$",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        -        "pattern": "^[^|\\r\\n]*$",
        -        "type": "string"
        -      },
        -      "key": {
        -        "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.",
        -        "type": "string"
        -      },
        -      "volume": {
        -        "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        -        "pattern": "^[^|\\r\\n]*$",
        -        "type": "string"
        -      },
        -      "year": {
        -        "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        -        "pattern": "^[^|\\r\\n]*$",
        -        "type": "string"
        -      }
        -    },
        -    "type": "object"
        -  }
        -]
      • changedInput schema / properties / citations / description
        Previous value: -"Citations to look up — an array of up to 25, or a single citation object. More fields = better match accuracy."New value: +"Citations to look up, 1–25, each matched independently. More fields = better match accuracy."
      • addedInput schema / properties / citations / items
        Added value: +{
        +  "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        +  "properties": {
        +    "authorName": {
        +      "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        +      "pattern": "^[^|\\r\\n]*$",
        +      "type": "string"
        +    },
        +    "firstPage": {
        +      "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        +      "pattern": "^[^|\\r\\n]*$",
        +      "type": "string"
        +    },
        +    "journal": {
        +      "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        +      "pattern": "^[^|\\r\\n]*$",
        +      "type": "string"
        +    },
        +    "key": {
        +      "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.",
        +      "type": "string"
        +    },
        +    "volume": {
        +      "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        +      "pattern": "^[^|\\r\\n]*$",
        +      "type": "string"
        +    },
        +    "year": {
        +      "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        +      "pattern": "^[^|\\r\\n]*$",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / citations / maxItems
        Added value: +25
      • addedInput schema / properties / citations / minItems
        Added value: +1
      • addedInput schema / properties / citations / type
        Added value: +"array"
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found"
        +]
    • Changedpubmed_lookup_mesh3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"MeSH descriptor name or free-text term to look up. Must carry a term: a blank or whitespace-only value is rejected rather than searched."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "blank_query"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query"
        +]
    • Changedpubmed_search_articles11 fields changed
      • changedInput schema / properties / author / description
        Previous value: -"Filter by author name (e.g. \"Smith J\")"New value: +"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."
      • changedInput schema / properties / dateRange / description
        Previous value: -"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."New value: +"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."
      • changedInput schema / properties / dateRange / properties / maxDate / description
        Previous value: -"End date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound"New value: +"End date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound. Must be a real calendar date — `2023/02/29` is rejected."
      • changedInput schema / properties / dateRange / properties / minDate / description
        Previous value: -"Start date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound"New value: +"Start date (YYYY/MM/DD, YYYY/MM, or YYYY); empty string disables this bound. Must be a real calendar date — `2023/02/29` is rejected."
      • changedInput schema / properties / journal / description
        Previous value: -"Filter by journal name"New value: +"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."
      • changedInput schema / properties / language / description
        Previous value: -"Filter by language (e.g. \"english\")"New value: +"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."
      • changedInput schema / properties / meshTerms / description
        Previous value: -"Filter by MeSH terms. Multiple terms are AND'd — all must match."New value: +"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."
      • changedInput schema / properties / publicationTypes / description
        Previous value: -"Filter by publication type (e.g. \"Review\", \"Clinical Trial\", \"Meta-Analysis\"). Multiple values are OR'd — any match qualifies."New value: +"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."
      • changedInput schema / properties / query / description
        Previous value: -"PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup, bracketed field tags (`[pdat]`), and parentheses are removed is rejected rather than sent to PubMed as an empty term."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. `invalid_date_range`: A `dateRange` bound is not a real calendar date — a month outside 01–12, day 00, or a day past the end of its month such as `2023/02/29` — or `minDate` falls after `maxDate` once PubMed expands each partial date: `minDate` from the start of its year or month, `maxDate` to the end. `blank_filter`: An `author`, `journal`, or `language` value, or a `publicationTypes` or `meshTerms` element, holds no term once markup is removed and HTML entities are decoded — only whitespace, invisible characters such as a zero-width space, parentheses, brackets, or double quotes are left, as in `()` or `\"\"` — so its field clause would carry no term. An exactly-empty string is not blank here; it sets no filter. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "blank_query"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query",
        +  "invalid_date_range",
        +  "blank_filter"
        +]
    • Changedpubmed_spell_check3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"PubMed search query to spell-check. Must carry a term: a blank or whitespace-only value is rejected rather than sent to ESpell."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline. `ncbi_rate_limited`: NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap. `ncbi_deadline_exceeded`: The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and `&nbsp;` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "blank_query"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_rate_limited",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query"
        +]
  5. 11 tool updatesv2.10.18
    • Changedpubmed_convert_ids1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `malformed_id`: An `ids` element does not match the declared `idType` — most often several identifiers packed into one element, which the comma-delimited upstream batch would split into extra records. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `malformed_id`: An `ids` element does not match the declared `idType` — most often several identifiers packed into one element, which the comma-delimited upstream batch would split into extra records. Other values are possible when a failure originates below the handler."
    • Changedpubmed_europepmc_fetch1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
    • Changedpubmed_europepmc_search4 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"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 combined with `AND SRC:` must be unquoted — the quoted form matches nothing. Free text is matched broadly across abstract/title/keywords."New value: +"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."
      • changedInput schema / properties / sort / description
        Previous value: -"Optional EPMC sort: `<field> asc|desc`. Documented sortable fields: `P_PDATE_D` (publication date), `CITED` (citation count), `AUTH_FIRST` (first author surname), `PUB_YEAR` (publication year). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking. Fields outside the documented set are rejected by EPMC. Note: `P_PDATE_D` is ignored for preprint-only (`sources: [\"PPR\"]`) result sets — preprints have no populated publication date, so use `PUB_YEAR` to order preprints by date."New value: +"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). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking. Field and direction match case-insensitively. A field outside the documented set may be honored, silently ignored, or rejected, and a sort using one — or a key without `asc`/`desc` — can fail with `europepmc_invalid_input` naming it, 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` to order preprints by date."
      • changedOutput schema / anyOf
        Previous value: -[
        -  {
        -    "not": {
        -      "required": [
        -        "error"
        -      ]
        -    },
        -    "required": [
        -      "hits",
        -      "cursorMark",
        -      "searchUrl",
        -      "query",
        -      "totalCount",
        -      "appliedSources"
        -    ]
        -  },
        -  {
        -    "required": [
        -      "error"
        -    ]
        -  }
        -]New value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "hits",
        +      "cursorMark",
        +      "searchUrl",
        +      "totalCount",
        +      "query",
        +      "appliedSources"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt. `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler."
    • Changedpubmed_fetch_articles1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `invalid_efetch_response`: NCBI EFetch returned a payload missing the PubmedArticleSet wrapper. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `invalid_efetch_response`: NCBI EFetch returned a payload missing the PubmedArticleSet wrapper. Other values are possible when a failure originates below the handler."
    • Changedpubmed_fetch_fulltext10 fields changed
      • changedInput schema / properties / maxCharacters / description
        Previous value: -"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. 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. Sections are served first, then tables, then assets, each spending what is left, in document order — 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`. 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."New value: +"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. Sections are served first, then tables, then assets, each spending what is left, in document order — 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`. 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."
      • changedInput schema / properties / overflowMode / description
        Previous value: -"How to spend `maxCharacters` across an article that exceeds it. truncate: fill sections in document order, so early sections stay whole and sections past the budget are dropped (counted in `truncation.omittedSections`). outline: split the budget evenly so every section keeps its heading, and an excerpt as far as the budget reaches — 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."New value: +"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: split the budget evenly so every section and subsection keeps its heading, and an excerpt as far as the budget reaches — a heading the budget left empty is marked as such in the rendered text. 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."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "assets": {
        -        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        -          "properties": {
        -            "assetType": {
        -              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        -              "enum": [
        -                "figure",
        -                "supplementary-material"
        -              ],
        -              "type": "string"
        -            },
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "href": {
        -              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Display label as printed, e.g. `Fig. 1`",
        -              "type": "string"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "assetType"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "elocationId": {
        -            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        -            "type": "string"
        -          },
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "tables": {
        -        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "One table from the article, with its cells, caption, and owning section",
        -          "properties": {
        -            "caption": {
        -              "description": "Caption text, with the label excluded",
        -              "type": "string"
        -            },
        -            "footnotes": {
        -              "description": "`<table-wrap-foot>` text, flattened to one string",
        -              "type": "string"
        -            },
        -            "headerRowCount": {
        -              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        -              "type": "number"
        -            },
        -            "id": {
        -              "description": "JATS `id` attribute — the target body-text cross-references point at",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Table label as printed, e.g. `TABLE 1`",
        -              "type": "string"
        -            },
        -            "rows": {
        -              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. Empty when `unextractableReason` is set.",
        -              "items": {
        -                "description": "One row, as cell text by grid column",
        -                "items": {
        -                  "type": "string"
        -                },
        -                "type": "array"
        -              },
        -              "type": "array"
        -            },
        -            "sectionTitle": {
        -              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        -              "type": "string"
        -            },
        -            "unextractableReason": {
        -              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        -              "enum": [
        -                "cals-tgroup",
        -                "graphic-only",
        -                "no-rows"
        -              ],
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "headerRowCount",
        -            "rows"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Detected article title when present",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "assets": {
        +        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        +          "properties": {
        +            "assetType": {
        +              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        +              "enum": [
        +                "figure",
        +                "supplementary-material"
        +              ],
        +              "type": "string"
        +            },
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "href": {
        +              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Display label as printed, e.g. `Fig. 1`",
        +              "type": "string"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "assetType"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "elocationId": {
        +            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        +            "type": "string"
        +          },
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article body sections",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article body section",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "tables": {
        +        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One table from the article, with its cells, caption, and owning section",
        +          "properties": {
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "footnotes": {
        +              "description": "`<table-wrap-foot>` text, flattened to one string",
        +              "type": "string"
        +            },
        +            "headerRowCount": {
        +              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        +              "type": "number"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Table label as printed, e.g. `TABLE 1`",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. Empty when `unextractableReason` is set.",
        +              "items": {
        +                "description": "One row, as cell text by grid column",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "type": "array"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            },
        +            "unextractableReason": {
        +              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        +              "enum": [
        +                "cals-tgroup",
        +                "graphic-only",
        +                "no-rows"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "headerRowCount",
        +            "rows"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "journalName": {
        +        "description": "Journal or repository name from Unpaywall's record for the DOI (e.g. `medRxiv` for a medRxiv preprint). Absent when Unpaywall has none.",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title, from the first source that carries one: Unpaywall's record for the DOI, then the Europe PMC record when the chain searched Europe PMC for this id, then — for `html-markdown` content only — the title detected on the page. Absent when none of them has a title.",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      },
        +      "year": {
        +        "description": "Publication year from Unpaywall's record for the DOI. Absent when Unpaywall has none.",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `unpaywall_unreachable`: Unpaywall was unreachable when resolving a DOI or fetching content. `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "unpaywall_unreachable",
        -  "europepmc_unreachable",
        -  "europepmc_invalid_response",
        -  "europepmc_invalid_input"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found"
        +]
      • changedOutput schema / properties / truncation / properties / articles / items / properties / sections / description
        Previous value: -"Per-section accounting for `source: pmc` articles, in document order, including sections dropped for budget. Absent for `source: unpaywall`, whose body has no section structure."New value: +"Per-section accounting for `source: pmc` articles, in document order, including sections dropped for budget; a shortened section lists its subsections. Absent for `source: unpaywall`, whose body has no section structure."
      • addedOutput schema / properties / truncation / properties / articles / items / properties / sections / items / properties / label
        Added value: +{
        +  "description": "Section label as printed (e.g. `2`), when the section carries one",
        +  "type": "string"
        +}
      • changedOutput schema / properties / truncation / properties / articles / items / properties / sections / items / properties / returnedCharacters / description
        Previous value: -"Body characters this section carries in the response. Zero means the section was dropped in `truncate` mode, or kept as a heading-only entry in `outline` mode."New value: +"Body characters this section carries in the response. Zero means the section was dropped in `truncate` mode, or kept as a heading-only entry in `outline` mode, marked as such in the rendered text."
      • addedOutput schema / properties / truncation / properties / articles / items / properties / sections / items / properties / subsections
        Added value: +{
        +  "description": "Per-subsection accounting for a shortened section, in document order, including subsections dropped for budget — where inside the section the cut landed. Absent when the section was returned whole or carries no subsections.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "Character accounting for one subsection of a shortened section",
        +    "properties": {
        +      "label": {
        +        "description": "Subsection label as printed (e.g. `2.1`), when the subsection carries one",
        +        "type": "string"
        +      },
        +      "originalCharacters": {
        +        "description": "Body characters this subsection carried before the budget pass",
        +        "type": "number"
        +      },
        +      "returnedCharacters": {
        +        "description": "Body characters this subsection carries in the response. Zero means it was dropped in `truncate` mode and counted in `omittedSections`, or kept as a heading-only entry in `outline` mode, marked as such in the rendered text.",
        +        "type": "number"
        +      },
        +      "title": {
        +        "description": "Subsection heading, when the subsection carries one",
        +        "type": "string"
        +      },
        +      "truncated": {
        +        "description": "True when the subsection returned fewer characters than it originally carried",
        +        "type": "boolean"
        +      }
        +    },
        +    "required": [
        +      "originalCharacters",
        +      "returnedCharacters",
        +      "truncated"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / truncation / properties / omittedSections / description
        Previous value: -"Body sections dropped entirely because an article budget was exhausted before reaching them. Always 0 in `outline` mode, which keeps every heading."New value: +"Body sections and subsections dropped entirely because an article budget was exhausted before reaching them. A dropped section counts once, together with its subsections. Always 0 in `outline` mode, which keeps every heading."
    • Changedpubmed_find_related2 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `openalex_unreachable`: OpenAlex was unreachable after all retry attempts. `openalex_invalid_response`: OpenAlex returned a body that could not be parsed (invalid JSON). `all_providers_failed`: Every provider eligible for the requested relationship failed; none answered. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `all_providers_failed`: Every provider eligible for the requested relationship failed; none answered. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "europepmc_unreachable",
        -  "europepmc_invalid_response",
        -  "europepmc_invalid_input",
        -  "openalex_unreachable",
        -  "openalex_invalid_response",
        -  "all_providers_failed"
        -]New value: +[
        +  "all_providers_failed"
        +]
    • Changedpubmed_format_citations1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
    • Changedpubmed_lookup_citation7 fields changed
      • addedInput schema / properties / citations / anyOf
        Added value: +[
        +  {
        +    "description": "Up to 25 citations, each matched independently.",
        +    "items": {
        +      "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        +      "properties": {
        +        "authorName": {
        +          "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        +          "pattern": "^[^|\\r\\n]*$",
        +          "type": "string"
        +        },
        +        "firstPage": {
        +          "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        +          "pattern": "^[^|\\r\\n]*$",
        +          "type": "string"
        +        },
        +        "journal": {
        +          "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        +          "pattern": "^[^|\\r\\n]*$",
        +          "type": "string"
        +        },
        +        "key": {
        +          "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.",
        +          "type": "string"
        +        },
        +        "volume": {
        +          "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        +          "pattern": "^[^|\\r\\n]*$",
        +          "type": "string"
        +        },
        +        "year": {
        +          "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        +          "pattern": "^[^|\\r\\n]*$",
        +          "type": "string"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "maxItems": 25,
        +    "minItems": 1,
        +    "type": "array"
        +  },
        +  {
        +    "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        +    "properties": {
        +      "authorName": {
        +        "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        +        "pattern": "^[^|\\r\\n]*$",
        +        "type": "string"
        +      },
        +      "firstPage": {
        +        "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        +        "pattern": "^[^|\\r\\n]*$",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        +        "pattern": "^[^|\\r\\n]*$",
        +        "type": "string"
        +      },
        +      "key": {
        +        "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.",
        +        "type": "string"
        +      },
        +      "volume": {
        +        "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        +        "pattern": "^[^|\\r\\n]*$",
        +        "type": "string"
        +      },
        +      "year": {
        +        "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        +        "pattern": "^[^|\\r\\n]*$",
        +        "type": "string"
        +      }
        +    },
        +    "type": "object"
        +  }
        +]
      • changedInput schema / properties / citations / description
        Previous value: -"Citations to look up. More fields = better match accuracy."New value: +"Citations to look up — an array of up to 25, or a single citation object. More fields = better match accuracy."
      • removedInput schema / properties / citations / items
        Removed value: -{
        -  "description": "Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match.",
        -  "properties": {
        -    "authorName": {
        -      "description": "Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break.",
        -      "pattern": "^[^|\\r\\n]*$",
        -      "type": "string"
        -    },
        -    "firstPage": {
        -      "description": "First page number. Cannot contain a pipe (\"|\") or a line break.",
        -      "pattern": "^[^|\\r\\n]*$",
        -      "type": "string"
        -    },
        -    "journal": {
        -      "description": "Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break.",
        -      "pattern": "^[^|\\r\\n]*$",
        -      "type": "string"
        -    },
        -    "key": {
        -      "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.",
        -      "type": "string"
        -    },
        -    "volume": {
        -      "description": "Volume number. Cannot contain a pipe (\"|\") or a line break.",
        -      "pattern": "^[^|\\r\\n]*$",
        -      "type": "string"
        -    },
        -    "year": {
        -      "description": "Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break.",
        -      "pattern": "^[^|\\r\\n]*$",
        -      "type": "string"
        -    }
        -  },
        -  "type": "object"
        -}
      • removedInput schema / properties / citations / maxItems
        Removed value: -25
      • removedInput schema / properties / citations / minItems
        Removed value: -1
      • removedInput schema / properties / citations / type
        Removed value: -"array"
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."
    • Changedpubmed_lookup_mesh1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."
    • Changedpubmed_search_articles3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup is stripped is rejected rather than sent to PubMed as an empty term."New value: +"PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup, bracketed field tags (`[pdat]`), and parentheses are removed is rejected rather than sent to PubMed as an empty term."
      • changedOutput schema / anyOf
        Previous value: -[
        -  {
        -    "not": {
        -      "required": [
        -        "error"
        -      ]
        -    },
        -    "required": [
        -      "query",
        -      "offset",
        -      "pmids",
        -      "summaries",
        -      "searchUrl",
        -      "effectiveQuery",
        -      "totalCount",
        -      "appliedFilters"
        -    ]
        -  },
        -  {
        -    "required": [
        -      "error"
        -    ]
        -  }
        -]New value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "query",
        +      "offset",
        +      "pmids",
        +      "summaries",
        +      "searchUrl",
        +      "totalCount",
        +      "effectiveQuery",
        +      "appliedFilters"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."
    • Changedpubmed_spell_check1 field changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429). `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and anything the tool strips before searching, are removed — so NCBI would receive a blank term. pubmed_search_articles strips markup, bracketed field tags, and parentheses, so a bare field tag such as `[pdat]` or empty parentheses `()` count as blank there. Other values are possible when a failure originates below the handler."
  6. 9 tool updatesv2.10.12
    • Changedpubmed_convert_ids3 fields changed
      • changedInput schema / properties / ids / description
        Previous value: -"Article identifiers to convert. All IDs must be the same type. DOIs: \"10.1093/nar/gks1195\", PMIDs: \"23193287\", PMCIDs: \"PMC3531190\" (the \"PMC\" prefix is optional — bare digits like \"3531190\" are also accepted)."New value: +"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."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `malformed_id`: An `ids` element does not match the declared `idType` — most often several identifiers packed into one element, which the comma-delimited upstream batch would split into extra records. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "malformed_id"
        +]
    • Changedpubmed_fetch_articles9 fields changed
      • changedOutput schema / properties / articles / items / properties / authors / description
        Previous value: -"Author list"New value: +"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."
      • addedOutput schema / properties / articles / items / properties / book
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "The containing book of a `book-chapter`, or the book itself on a `book` record. Present only on those two record types, and never a stand-in for `journalInfo`.",
        +  "properties": {
        +    "accession": {
        +      "description": "NCBI Bookshelf accession from `ArticleIdList` (`bookaccession`), e.g. \"NBK1247\". The record is readable at `https://www.ncbi.nlm.nih.gov/books/<accession>/`.",
        +      "type": "string"
        +    },
        +    "beginningDate": {
        +      "description": "First year of a continuously-updated book, from `Book/BeginningDate` (GeneReviews runs from 1993). Absent on a book published once.",
        +      "type": "string"
        +    },
        +    "collectionTitle": {
        +      "description": "Series the book belongs to, from `Book/CollectionTitle` (e.g. \"ADA Clinical Compendia Series\"). Absent for a book outside a series.",
        +      "type": "string"
        +    },
        +    "doi": {
        +      "description": "The book's own DOI, from `Book/ELocationID` with `EIdType=\"doi\"`. Distinct from the record-level `doi`, which is the chapter's: a chapter does not inherit this one.",
        +      "type": "string"
        +    },
        +    "edition": {
        +      "description": "Edition statement from `Book/Edition`. Rare on Bookshelf titles — absent unless NCBI supplies one.",
        +      "type": "string"
        +    },
        +    "editors": {
        +      "description": "Editors of the containing book, from `Book/AuthorList` marked `Type=\"editors\"`. Kept out of `authors`, which carries the chapter's own writers. Absent when the book credits no editors.",
        +      "items": {
        +        "additionalProperties": false,
        +        "description": "One editor of the containing book. Name parts only — editors are a citation credit, not a contributor record, so no affiliations or ORCID are reported for them.",
        +        "properties": {
        +          "collectiveName": {
        +            "description": "Group or committee credited as editor, when the entry names an organization rather than a person. Mutually exclusive with the name-part fields.",
        +            "type": "string"
        +          },
        +          "firstName": {
        +            "description": "Editor given name as NCBI supplies it (`ForeName`, often \"Margaret P\"). Absent when NCBI carries initials only, or on a group editor.",
        +            "type": "string"
        +          },
        +          "initials": {
        +            "description": "Editor initials with no separators (e.g. \"MP\"). Absent when NCBI supplies none, or on a group editor.",
        +            "type": "string"
        +          },
        +          "lastName": {
        +            "description": "Editor surname, from the book's `Book/AuthorList Type=\"editors\"` entry. Absent on a group editor, which carries `collectiveName` instead.",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "endingDate": {
        +      "description": "Last year of a closed date range, from `Book/EndingDate`. Absent while a book is still being updated, which leaves the range open-ended.",
        +      "type": "string"
        +    },
        +    "isbns": {
        +      "description": "Every `Book/Isbn` on the record. A book commonly carries a print and an electronic ISBN, so this is a list. Absent for a Bookshelf title with no ISBN, which is most of them.",
        +      "items": {
        +        "description": "One ISBN, verbatim as NCBI reports it — leading zeros intact",
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "medium": {
        +      "description": "Medium the book is published in, from `Book/Medium` — \"Internet\" wherever NCBI supplies it. Absent when NCBI supplies none; it is never defaulted.",
        +      "type": "string"
        +    },
        +    "pubDate": {
        +      "description": "Publication year from `Book/PubDate`. Year only — NCBI's month and day are not reported, since no citation style uses them for a book.",
        +      "type": "string"
        +    },
        +    "publisher": {
        +      "description": "Publisher of the book, from `Book/Publisher/PublisherName`.",
        +      "type": "string"
        +    },
        +    "publisherLocation": {
        +      "description": "Place of publication, from `Book/Publisher/PublisherLocation` (e.g. \"Seattle (WA)\"). Absent when NCBI supplies no place.",
        +      "type": "string"
        +    },
        +    "title": {
        +      "description": "Title of the containing book, from `Book/BookTitle` (e.g. \"GeneReviews®\"). On a `book` record this is the same value as the record's own `title`.",
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
      • changedOutput schema / properties / articles / items / properties / journalInfo / description
        Previous value: -"Journal information"New value: +"Journal information. Present on `journal-article` records only — absent on `book-chapter` and `book` records, because a Bookshelf record has no journal and its book title is never reported as one; read `book` for those. (#114)"
      • addedOutput schema / properties / articles / items / properties / journalInfo / properties / elocationId
        Added value: +{
        +  "description": "Electronic article locator from NCBI `ELocationID` — the publisher-assigned article number (e.g. \"2400512\"). Journals that assign article numbers instead of pages often omit pagination entirely, leaving this the only locator. Never a substitute for `pages`, and never the DOI: a DOI-typed `ELocationID` is reported in `doi` instead. Absent when the only locator NCBI supplies is marked invalid.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / journalInfo / properties / elocationIdType
        Added value: +{
        +  "description": "Type of `elocationId`, from NCBI's `EIdType` attribute — \"pii\" in practice. Free-form: NCBI does not close the set, so treat an unfamiliar value as opaque.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / recordType
        Added value: +{
        +  "description": "Which kind of PubMed record this is, set from the XML element it arrived in: `journal-article` for an ordinary article, `book-chapter` for an NCBI Bookshelf chapter, `book` for a whole Bookshelf book. Read this to tell the three apart — `publicationTypes` cannot, because PubMed labels a Bookshelf record \"Review\" or \"Study Guide\". `journalInfo` is present only on `journal-article`; `book` only on the other two.",
        +  "enum": [
        +    "journal-article",
        +    "book-chapter",
        +    "book"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / articles / items / properties / title / description
        Previous value: -"Article title"New value: +"Article title — the chapter title on a `book-chapter`, and the book title on a `book` record, where it repeats `book.title`."
      • addedOutput schema / properties / articles / items / required
        Added value: +[
        +  "recordType"
        +]
      • changedOutput schema / properties / unavailablePmids / description
        Previous value: -"PMIDs that returned no article data. Reported in full regardless of where a `maxResponseCharacters` cutoff lands — these are misses, not deferrals, and re-requesting them returns nothing."New value: +"PMIDs PubMed returned no record for. That is all this reports: PubMed omits an unknown PMID silently, with no error and no reason, so the absence says nothing about whether the PMID exists. Reported in full regardless of where a `maxResponseCharacters` cutoff lands — these are misses, not deferrals. Use `pubmed_search_articles` to find PMIDs that do resolve."
    • Changedpubmed_fetch_fulltext19 fields changed
      • changedInput schema / properties / dois / description
        Previous value: -"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`. 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."New value: +"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]), one per element. Provide exactly one of `pmcids`, `pmids`, or `dois`. 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."
      • removedInput schema / properties / dois / items / minLength
        Removed value: -3
      • addedInput schema / properties / dois / items / pattern
        Added value: +"^10\\.[^\\s,]+\\/[^\\s,]+$"
      • addedInput schema / properties / includeAssets
        Added value: +{
        +  "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.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / includeTables
        Added value: +{
        +  "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.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / maxCharacters / description
        Previous value: -"Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text, or the `source=unpaywall` `content` body; titles, abstracts, identifiers, and references are never counted or shortened. Applied after `sections`, `maxSections`, and `includeReferences`, 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."New value: +"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. 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. Sections are served first, then tables, then assets, each spending what is left, in document order — 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`. 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."
      • changedInput schema / properties / sections / description
        Previous value: -"Filter to specific sections by title, case-insensitive (e.g. [\"Introduction\", \"Methods\", \"Results\", \"Discussion\"]). Applies to `source=pmc` results only."New value: +"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\". 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. Applies to `source=pmc` results only."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Detected article title when present",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "assets": {
        +        "description": "Every `<fig>` and `<supplementary-material>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Each one lifted from the body leaves a `[Figure: <label>]` or `[Supplementary: <label>]` marker at its position in the section text, so reading order survives the lift. Absent when the article deposits none, when `includeAssets` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One figure or supplementary-material item, with its caption, pointer, and section",
        +          "properties": {
        +            "assetType": {
        +              "description": "Which captioned element this came from — `figure` for a `<fig>`, `supplementary-material` for a `<supplementary-material>` deposit",
        +              "enum": [
        +                "figure",
        +                "supplementary-material"
        +              ],
        +              "type": "string"
        +            },
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "href": {
        +              "description": "The `<graphic>`/`<media>` `@xlink:href` exactly as deposited — a pointer into the PMC deposit (`MOL2-20-1253-g001.jpg`), not a fetchable URL. No absolute form of it resolves; read the rendered article at `pmcUrl` instead. Absent when the deposit names no file.",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Display label as printed, e.g. `Fig. 1`",
        +              "type": "string"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the asset, wherever that section sits — body, `<back>` matter, or an appendix all count. Absent for an asset inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "assetType"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "elocationId": {
        +            "description": "Electronic article locator from JATS `<elocation-id>` — the publisher-assigned article number (e.g. \"e20542\"). Journals that assign article numbers deposit no `<fpage>`, so this is the only locator on roughly half of PMC records. Never a substitute for `pages`; JATS carries no type attribute, so there is no counterpart to the `elocationIdType` that `pubmed_fetch_articles` reports.",
        +            "type": "string"
        +          },
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article body sections",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article body section",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "tables": {
        +        "description": "Every `<table-wrap>` the article carries, in document order — from the body and from `<floats-group>`, `<back>` and appendices alike. Absent when the article deposits none, when `includeTables` is false, or when a `sections` filter left none standing.",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "One table from the article, with its cells, caption, and owning section",
        +          "properties": {
        +            "caption": {
        +              "description": "Caption text, with the label excluded",
        +              "type": "string"
        +            },
        +            "footnotes": {
        +              "description": "`<table-wrap-foot>` text, flattened to one string",
        +              "type": "string"
        +            },
        +            "headerRowCount": {
        +              "description": "How many leading `rows` entries are header rows — a `<thead>` block, or leading rows made entirely of `<th>`. 0 when the table declares none. Several header rows stack: read one column top to bottom for its full header path.",
        +              "type": "number"
        +            },
        +            "id": {
        +              "description": "JATS `id` attribute — the target body-text cross-references point at",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Table label as printed, e.g. `TABLE 1`",
        +              "type": "string"
        +            },
        +            "rows": {
        +              "description": "Cell text by row, in document order, one entry per grid column. `colspan` and `rowspan` are expanded, so a cell covering several columns or rows repeats its text across each cell it covers and a well-formed table is rectangular — align on position from the left, and read a repeated value as one spanning cell rather than several measurements. Empty when `unextractableReason` is set.",
        +              "items": {
        +                "description": "One row, as cell text by grid column",
        +                "items": {
        +                  "type": "string"
        +                },
        +                "type": "array"
        +              },
        +              "type": "array"
        +            },
        +            "sectionTitle": {
        +              "description": "Title of the innermost section enclosing the table, wherever that section sits — body, `<back>` matter, or an appendix all count, and in back matter the section name is the only positional cue there is. Absent only for a table inside no section at all, such as a `<floats-group>` deposit.",
        +              "type": "string"
        +            },
        +            "unextractableReason": {
        +              "description": "Why `rows` is empty — set only then. graphic-only: the table was deposited as an image with no underlying markup. cals-tgroup: the table uses the CALS `<tgroup>` model, which this server does not extract (0 of 283 tables in an open-access survey used it). no-rows: the markup carried no rows. The label and caption are still returned, so a table that could not be read is visible rather than silently missing.",
        +              "enum": [
        +                "cals-tgroup",
        +                "graphic-only",
        +                "no-rows"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "headerRowCount",
        +            "rows"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Detected article title when present",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Absent when none of those applies."New value: +"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A table returned with no cell values names the affected table(s), the article each came from, and why the cells cannot be recovered. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Absent when none of those applies."
      • addedOutput schema / properties / truncation / properties / articles / items / properties / omittedAssetNames
        Added value: +{
        +  "description": "The dropped assets by name, in document order — each asset's label, else its `id`, else `asset <n>` for its position in the article. Contiguous for the same reason `omittedTableNames` is: admission stops at the first asset that did not fit rather than skipping ahead to a smaller one. Absent when none were dropped.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / truncation / properties / articles / items / properties / omittedAssets
        Added value: +{
        +  "description": "Figures and supplementary items this article dropped whole because the budget left no room once sections and tables were served. An asset is never returned with a truncated caption, so it is either returned complete or counted here. Absent when none were dropped.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / truncation / properties / articles / items / properties / omittedTableNames
        Added value: +{
        +  "description": "The dropped tables by name, in document order — each table's label, else its `id`, else `table <n>` for its position in the article. Names the tables a bare count only hints at, the way `deferred.ids` names deferred articles. Every table from the first that did not fit onward is here: admission stops at that table rather than skipping ahead to a smaller one, so these are contiguous. Absent when none were dropped.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / truncation / properties / articles / items / properties / omittedTables
        Added value: +{
        +  "description": "Tables this article dropped whole because the budget left no room for them. A table is never cut mid-row, so it is either returned complete or counted here. Absent when none were dropped.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / truncation / properties / omittedAssets
        Added value: +{
        +  "description": "Figures and supplementary items dropped whole across every budgeted article, because the budget left no room once body sections and tables were served. Absent when none were dropped. Re-request the affected articles with a higher `maxCharacters`, or with `sections` narrowed, to receive them.",
        +  "type": "number"
        +}
      • addedOutput schema / properties / truncation / properties / omittedTables
        Added value: +{
        +  "description": "Tables dropped whole across every budgeted article, because the budget left no room once body sections were served. Absent when none were dropped. Re-request the affected articles with a higher `maxCharacters`, or with `sections` narrowed, to receive them.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / unavailable / items / properties / reason / description
        Previous value: -"Why no full text was returned — the most specific signal any tier that answered reported. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the record was retrieved but carries front matter and abstract only, with no body sections — use `pubmed_fetch_articles` for the metadata. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data). A reason never means the chain ran to completion — read `unqueriedTiers` for that."New value: +"Why no full text was returned — the most specific signal any tier that answered reported. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the record was retrieved but carries front matter and abstract only, with no body sections — use `pubmed_fetch_articles` for the metadata. no-doi: the DOI lookup ran and this record has none, so Unpaywall could not be queried. doi-lookup-failed: the DOI lookup itself errored, so whether a DOI exists is unknown and Unpaywall was never reached — retry the request; unlike no-doi this is a transient failure, not a settled answer. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data). A reason never means the chain ran to completion — read `unqueriedTiers` for that."
      • changedOutput schema / properties / unavailable / items / properties / reason / enum
        Previous value: -[
        -  "not-found",
        -  "no-pmc-fallback-disabled",
        -  "no-epmc-fulltext",
        -  "no-body",
        -  "no-doi",
        -  "no-oa",
        -  "fetch-failed",
        -  "parse-failed",
        -  "service-error"
        -]New value: +[
        +  "not-found",
        +  "no-pmc-fallback-disabled",
        +  "no-epmc-fulltext",
        +  "no-body",
        +  "no-doi",
        +  "doi-lookup-failed",
        +  "no-oa",
        +  "fetch-failed",
        +  "parse-failed",
        +  "service-error"
        +]
      • changedOutput schema / properties / unavailable / items / properties / triedTiers / items / properties / outcome / description
        Previous value: -"Per-tier outcome. not-attempted: tier was skipped. miss: tier returned no record. no-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the tier returned a record with front matter and abstract but no body sections, so the chain continued. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall reports no open-access copy. fetch-failed: OA copy download failed. parse-failed: extraction produced empty content. service-error: tier service threw."New value: +"Per-tier outcome. not-attempted: tier was skipped. miss: tier returned no record. no-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the tier returned a record with front matter and abstract but no body sections, so the chain continued. no-doi: the DOI lookup ran and this record has none, so Unpaywall could not be queried. doi-lookup-failed: the DOI lookup itself errored, so whether a DOI exists is unknown and Unpaywall was never reached — retry the request. no-oa: Unpaywall reports no open-access copy. fetch-failed: OA copy download failed. parse-failed: extraction produced empty content. service-error: tier service threw."
      • changedOutput schema / properties / unavailable / items / properties / triedTiers / items / properties / outcome / enum
        Previous value: -[
        -  "not-attempted",
        -  "miss",
        -  "no-fulltext",
        -  "no-body",
        -  "no-doi",
        -  "no-oa",
        -  "fetch-failed",
        -  "parse-failed",
        -  "service-error"
        -]New value: +[
        +  "not-attempted",
        +  "miss",
        +  "no-fulltext",
        +  "no-body",
        +  "no-doi",
        +  "doi-lookup-failed",
        +  "no-oa",
        +  "fetch-failed",
        +  "parse-failed",
        +  "service-error"
        +]
    • Changedpubmed_find_related9 fields changed
      • changedOutput schema / properties / articles / items / properties / authors / description
        Previous value: -"Author string"New value: +"Author string — the first three of the record's own authors, then \"et al.\". On an NCBI Bookshelf chapter these are the chapter's authors; the book's editors are in `editors`."
      • addedOutput schema / properties / articles / items / properties / bookTitle
        Added value: +{
        +  "description": "Title of the book an NCBI Bookshelf record belongs to. Present instead of `source` on a book record; absent on a journal article.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / docType
        Added value: +{
        +  "description": "What PubMed classifies this record as: \"chapter\" or \"book\" for an NCBI Bookshelf record, \"citation\" for an ordinary journal article. Absent when PubMed supplies none.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / articles / items / properties / editors
        Added value: +{
        +  "description": "Editors of the containing book, kept out of `authors` so they cannot displace the record's own authors. Absent on a journal article and on a book that credits no editors.",
        +  "items": {
        +    "description": "One editor, \"Surname Initials\" as ESummary renders it",
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / articles / items / properties / publisherName
        Added value: +{
        +  "description": "Publisher of the book an NCBI Bookshelf record belongs to. Present only on a book record; absent on a journal article.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / articles / items / properties / source / description
        Previous value: -"Journal source"New value: +"Journal the article appeared in. Absent on an NCBI Bookshelf record, which has no journal — its venue is in `bookTitle` and `publisherName` instead, and `docType` says which kind of record it is."
      • addedOutput schema / properties / coverageFailures
        Added value: +{
        +  "description": "Reference-coverage fallbacks that failed instead of answering, so the reference set is unverified rather than confirmed absent. Absent when every provider consulted answered.",
        +  "items": {
        +    "additionalProperties": false,
        +    "description": "One coverage provider that could not be checked",
        +    "properties": {
        +      "provider": {
        +        "description": "Reference-coverage provider that failed",
        +        "enum": [
        +          "europepmc",
        +          "openalex"
        +        ],
        +        "type": "string"
        +      },
        +      "reason": {
        +        "description": "Declared failure reason, e.g. europepmc_unreachable or provider_disabled",
        +        "type": "string"
        +      },
        +      "retryable": {
        +        "description": "Whether a retry can reach this provider",
        +        "type": "boolean"
        +      }
        +    },
        +    "required": [
        +      "provider",
        +      "reason",
        +      "retryable"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance when results are empty, a fallback provider answered, offset overshot, or Europe PMC rows were excluded for carrying no PubMed PMID. Absent on a clean NCBI result page."New value: +"Guidance when results are empty, a fallback provider answered, offset overshot, a fallback provider could not be reached, or upstream rows were excluded for carrying no PubMed PMID. Absent on a clean NCBI result page."
      • changedOutput schema / properties / totalCount / description
        Previous value: -"Total related articles found before windowing. A Europe PMC total may shrink to the PubMed-addressable count once a request window covers the whole upstream set, since rows without a PubMed PMID cannot be returned."New value: +"Total related articles found before windowing. A Europe PMC or OpenAlex total may shrink to the PubMed-addressable count once a request window covers the whole upstream set, since rows without a PubMed PMID cannot be returned."
    • Changedpubmed_format_citations1 field changed
      • changedOutput schema / properties / unavailablePmids / description
        Previous value: -"Requested PMIDs that did not return article metadata"New value: +"PMIDs PubMed returned no record for, so nothing could be cited for them. That is all this reports: PubMed omits a PMID it does not recognize silently, with no error and no reason, so the absence says nothing about whether the PMID exists. Use `pubmed_search_articles` to find PMIDs that do resolve."
    • Changedpubmed_lookup_citation11 fields changed
      • changedInput schema / properties / citations / items / properties / authorName / description
        Previous value: -"Author name, typically \"lastname initials\" (e.g., \"mann bj\")"New value: +"Author name, typically \"lastname initials\" (e.g., \"mann bj\"). Cannot contain a pipe (\"|\") or a line break."
      • addedInput schema / properties / citations / items / properties / authorName / pattern
        Added value: +"^[^|\\r\\n]*$"
      • changedInput schema / properties / citations / items / properties / firstPage / description
        Previous value: -"First page number"New value: +"First page number. Cannot contain a pipe (\"|\") or a line break."
      • addedInput schema / properties / citations / items / properties / firstPage / pattern
        Added value: +"^[^|\\r\\n]*$"
      • changedInput schema / properties / citations / items / properties / journal / description
        Previous value: -"Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\")"New value: +"Journal title or ISO abbreviation (e.g., \"proc natl acad sci u s a\"). Cannot contain a pipe (\"|\") or a line break."
      • addedInput schema / properties / citations / items / properties / journal / pattern
        Added value: +"^[^|\\r\\n]*$"
      • changedInput schema / properties / citations / items / properties / key / description
        Previous value: -"Arbitrary label to track this citation in results. Auto-assigned if omitted."New value: +"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."
      • changedInput schema / properties / citations / items / properties / volume / description
        Previous value: -"Volume number"New value: +"Volume number. Cannot contain a pipe (\"|\") or a line break."
      • addedInput schema / properties / citations / items / properties / volume / pattern
        Added value: +"^[^|\\r\\n]*$"
      • changedInput schema / properties / citations / items / properties / year / description
        Previous value: -"Publication year (e.g., \"1991\")"New value: +"Publication year (e.g., \"1991\"). Cannot contain a pipe (\"|\") or a line break."
      • addedInput schema / properties / citations / items / properties / year / pattern
        Added value: +"^[^|\\r\\n]*$"
    • Changedpubmed_lookup_mesh3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"MeSH descriptor name or free-text term to look up"New value: +"MeSH descriptor name or free-text term to look up. Must carry a term: a blank or whitespace-only value is rejected rather than searched."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query"
        +]
    • Changedpubmed_search_articles9 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"PubMed search query (supports full NCBI syntax)"New value: +"PubMed search query (supports full NCBI syntax). Must carry a search term: a value that is blank once markup is stripped is rejected rather than sent to PubMed as an empty term."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query"
        +]
      • changedOutput schema / properties / summaries / items / properties / authors / description
        Previous value: -"Formatted author string"New value: +"Formatted author string — the first three of the record's own authors, then \"et al.\". On an NCBI Bookshelf chapter these are the chapter's authors; the book's editors are reported separately in `editors`."
      • addedOutput schema / properties / summaries / items / properties / bookTitle
        Added value: +{
        +  "description": "Title of the book an NCBI Bookshelf record belongs to, e.g. \"GeneReviews(®)\". Present instead of `source` on a book record; absent on a journal article.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / summaries / items / properties / docType
        Added value: +{
        +  "description": "What PubMed classifies this record as: \"chapter\" or \"book\" for an NCBI Bookshelf record, \"citation\" for an ordinary journal article. Absent when PubMed supplies none.",
        +  "type": "string"
        +}
      • addedOutput schema / properties / summaries / items / properties / editors
        Added value: +{
        +  "description": "Editors of the containing book, kept out of `authors` so they cannot displace the record's own authors. Absent on a journal article and on a book that credits no editors.",
        +  "items": {
        +    "description": "One editor, \"Surname Initials\" as ESummary renders it",
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / summaries / items / properties / publisherName
        Added value: +{
        +  "description": "Publisher of the book an NCBI Bookshelf record belongs to. Present only on a book record; absent on a journal article.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / summaries / items / properties / source / description
        Previous value: -"Journal source"New value: +"Journal the article appeared in. Absent on an NCBI Bookshelf record, which has no journal — its venue is in `bookTitle` and `publisherName` instead, and `docType` says which kind of record it is."
    • Changedpubmed_spell_check3 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"PubMed search query to spell-check"New value: +"PubMed search query to spell-check. Must carry a term: a blank or whitespace-only value is rejected rather than sent to ESpell."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `blank_query`: The query holds no search term once whitespace, and any markup the tool strips first, are removed — so NCBI would receive a blank term. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "blank_query"
        +]
  7. 5 tool updatesv2.10.8
    • Changedpubmed_europepmc_fetch3 fields changed
      • changedOutput schema / properties / records / items / properties / authors / description
        Previous value: -"Formatted author string"New value: +"Formatted author string as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
      • changedOutput schema / properties / records / items / properties / journal / description
        Previous value: -"Journal title"New value: +"Journal title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
      • changedOutput schema / properties / records / items / properties / title / description
        Previous value: -"Record title"New value: +"Record title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
    • Changedpubmed_europepmc_search3 fields changed
      • changedOutput schema / properties / hits / items / properties / authors / description
        Previous value: -"Formatted author string"New value: +"Formatted author string as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
      • changedOutput schema / properties / hits / items / properties / journal / description
        Previous value: -"Journal title"New value: +"Journal title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
      • changedOutput schema / properties / hits / items / properties / title / description
        Previous value: -"Article title"New value: +"Article title as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded."
    • Changedpubmed_fetch_articles6 fields changed
      • addedInput schema / properties / maxResponseCharacters
        Added value: +{
        +  "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.",
        +  "maximum": 1000000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / deferred
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Continuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.",
        +  "properties": {
        +    "deferredCount": {
        +      "description": "Articles that resolved but were withheld to stay under the ceiling",
        +      "type": "number"
        +    },
        +    "ids": {
        +      "description": "PMIDs of the deferred articles, in response order. Re-call `pubmed_fetch_articles` with these as `pmids` and the same other inputs to retrieve them. Never contains a PMID from `unavailablePmids`.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "maxResponseCharacters": {
        +      "description": "The `maxResponseCharacters` ceiling this response was budgeted against",
        +      "type": "number"
        +    },
        +    "nextDeferredCharacters": {
        +      "description": "Serialized size of the next deferred article — the first entry in `ids`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller article further down `ids` cannot be reached until this one fits.",
        +      "type": "number"
        +    },
        +    "returnedCharacters": {
        +      "description": "Serialized characters the returned article records account for",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "maxResponseCharacters",
        +    "returnedCharacters",
        +    "deferredCount",
        +    "ids",
        +    "nextDeferredCharacters"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance when no articles were returned — points to discovery tools. Absent on successful fetches."New value: +"Optional guidance when no articles were returned — points to discovery tools — or when `maxResponseCharacters` deferred articles, naming how to retrieve them. Absent on successful unbudgeted fetches."
      • changedOutput schema / properties / totalReturned / description
        Previous value: -"Number of articles returned"New value: +"Number of articles in this response. Under a `maxResponseCharacters` budget this counts the kept articles only; `deferred.deferredCount` covers the rest."
      • addedOutput schema / properties / truncated
        Added value: +{
        +  "description": "True when `maxResponseCharacters` withheld at least one resolved article. Absent when the response carries every article that resolved. The continuation state is in `deferred`.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / unavailablePmids / description
        Previous value: -"PMIDs that returned no article data"New value: +"PMIDs that returned no article data. Reported in full regardless of where a `maxResponseCharacters` cutoff lands — these are misses, not deferrals, and re-requesting them returns nothing."
    • Changedpubmed_fetch_fulltext10 fields changed
      • changedInput schema / properties / maxCharacters / description
        Previous value: -"Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text, or the `source=unpaywall` `content` body; titles, abstracts, identifiers, and references are never counted or shortened. Applied after `sections`, `maxSections`, and `includeReferences`, so semantic filtering is unaffected. The response-wide ceiling is this value times the number of articles returned. Omit for the full body."New value: +"Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text, or the `source=unpaywall` `content` body; titles, abstracts, identifiers, and references are never counted or shortened. Applied after `sections`, `maxSections`, and `includeReferences`, 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."
      • addedInput schema / properties / maxResponseCharacters
        Added value: +{
        +  "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, body 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 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, `unavailable`, `truncation`, `deferred` itself — are not counted. Omit to return every resolved article.",
        +  "maximum": 1000000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Detected article title when present",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article body sections",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article body section",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text. Sections nested deeper than this level are folded in here in document order, each heading rendered on its own line above its text.",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Detected article title when present",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • addedOutput schema / properties / deferred
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Continuation state for articles the whole-response budget withheld. Present only when `maxResponseCharacters` deferred at least one article.",
        +  "properties": {
        +    "deferredCount": {
        +      "description": "Articles the chain resolved but withheld to stay under the ceiling",
        +      "type": "number"
        +    },
        +    "idType": {
        +      "description": "Which input branch the deferred ids belong to — re-submit them as `pmids`, `pmcids`, or `dois` respectively. Matches the `idType` on `unavailable` entries.",
        +      "enum": [
        +        "pmid",
        +        "pmcid",
        +        "doi"
        +      ],
        +      "type": "string"
        +    },
        +    "ids": {
        +      "description": "Identifiers of the deferred articles, in response order, keyed as they were requested (PMC IDs in `PMC<digits>` form). Re-call `pubmed_fetch_fulltext` with these under the `idType` branch and the same other inputs. Never contains an id from `unavailable`.",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "maxResponseCharacters": {
        +      "description": "The `maxResponseCharacters` ceiling this response was budgeted against",
        +      "type": "number"
        +    },
        +    "nextDeferredCharacters": {
        +      "description": "Serialized size of the next deferred article — the first entry in `ids`, where the response stopped. Raise `maxResponseCharacters` to at least this to make progress; a smaller article further down `ids` cannot be reached until this one fits.",
        +      "type": "number"
        +    },
        +    "returnedCharacters": {
        +      "description": "Serialized characters the returned article records account for",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "maxResponseCharacters",
        +    "returnedCharacters",
        +    "deferredCount",
        +    "idType",
        +    "ids",
        +    "nextDeferredCharacters"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A budgeted response names the characters returned versus carried and points at `truncation`. Absent when none of those applies."New value: +"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A budgeted response names the characters returned versus carried and points at `truncation`. A response-wide budget that deferred articles names the ids to re-request. Absent when none of those applies."
      • changedOutput schema / properties / totalReturned / description
        Previous value: -"Number of articles returned"New value: +"Number of articles in this response. Under a `maxResponseCharacters` budget this counts the kept articles only; `deferred.deferredCount` covers the rest."
      • changedOutput schema / properties / truncated / description
        Previous value: -"True when a character budget shortened at least one returned body. Absent when every returned article carries its full post-filter body. The per-article accounting is in `truncation`."New value: +"True when a character budget shortened at least one returned body, or withheld a whole article. Absent when every resolved article is present with its full post-filter body. The per-article body accounting is in `truncation`; the withheld ids are in `deferred`."
      • changedOutput schema / properties / unavailable / description
        Previous value: -"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` discriminates which branch the id came from."New value: +"Per-identifier explanations for any requested PMIDs, PMCIDs, or DOIs with no returnable full text. `idType` discriminates which branch the id came from. Distinct from `deferred`: nothing here is retrievable by re-calling, and an id never appears in both."
      • changedOutput schema / properties / unavailable / items / properties / reason / description
        Previous value: -"Why no full text was returned. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the record was retrieved but carries front matter and abstract only, with no body sections — use `pubmed_fetch_articles` for the metadata. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data)."New value: +"Why no full text was returned — the most specific signal any tier that answered reported. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the record was retrieved but carries front matter and abstract only, with no body sections — use `pubmed_fetch_articles` for the metadata. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data). A reason never means the chain ran to completion — read `unqueriedTiers` for that."
      • addedOutput schema / properties / unavailable / items / properties / unqueriedTiers
        Added value: +{
        +  "description": "Tiers the chain skipped because this deployment has not configured them, and that could have served this id — the search was incomplete, and a deployment with these tiers configured may still resolve the id. `triedTiers` carries which environment variable each one is waiting on. Absent when every tier that could have served the id was actually queried; a tier skipped because it was inapplicable to this id (no DOI for Unpaywall) is never listed.",
        +  "items": {
        +    "description": "A fallback tier this deployment has not configured",
        +    "enum": [
        +      "europepmc",
        +      "unpaywall"
        +    ],
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
    • Changedpubmed_find_related4 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `openalex_unreachable`: OpenAlex was unreachable after all retry attempts. `openalex_invalid_response`: OpenAlex returned a body that could not be parsed (invalid JSON). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `openalex_unreachable`: OpenAlex was unreachable after all retry attempts. `openalex_invalid_response`: OpenAlex returned a body that could not be parsed (invalid JSON). `all_providers_failed`: Every provider eligible for the requested relationship failed; none answered. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "queue_full",
        -  "ncbi_unreachable",
        -  "ncbi_deadline_exceeded",
        -  "ncbi_invalid_response",
        -  "ncbi_resource_not_found",
        -  "europepmc_unreachable",
        -  "europepmc_invalid_response",
        -  "europepmc_invalid_input",
        -  "openalex_unreachable",
        -  "openalex_invalid_response"
        -]New value: +[
        +  "queue_full",
        +  "ncbi_unreachable",
        +  "ncbi_deadline_exceeded",
        +  "ncbi_invalid_response",
        +  "ncbi_resource_not_found",
        +  "europepmc_unreachable",
        +  "europepmc_invalid_response",
        +  "europepmc_invalid_input",
        +  "openalex_unreachable",
        +  "openalex_invalid_response",
        +  "all_providers_failed"
        +]
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance when results are empty, a fallback provider answered, or offset overshot. Absent on a clean NCBI result page."New value: +"Guidance when results are empty, a fallback provider answered, offset overshot, or Europe PMC rows were excluded for carrying no PubMed PMID. Absent on a clean NCBI result page."
      • changedOutput schema / properties / totalCount / description
        Previous value: -"Total related articles found before windowing"New value: +"Total related articles found before windowing. A Europe PMC total may shrink to the PubMed-addressable count once a request window covers the whole upstream set, since rows without a PubMed PMID cannot be returned."
  8. 11 tool updatesv2.10.4
    • Changedpubmed_convert_ids6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "records",
        +      "totalConverted",
        +      "totalSubmitted"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "records",
        -  "totalConverted",
        -  "totalSubmitted"
        -]
    • Changedpubmed_europepmc_fetch6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "records"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "europepmc_unreachable",
        +            "europepmc_invalid_response",
        +            "europepmc_invalid_input",
        +            "europepmc_disabled"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "records"
        -]
    • Changedpubmed_europepmc_search6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "hits",
        +      "cursorMark",
        +      "searchUrl",
        +      "query",
        +      "totalCount",
        +      "appliedSources"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `europepmc_disabled`: Europe PMC service is disabled via EUROPEPMC_ENABLED=false. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "europepmc_unreachable",
        +            "europepmc_invalid_response",
        +            "europepmc_invalid_input",
        +            "europepmc_disabled"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "hits",
        -  "cursorMark",
        -  "searchUrl",
        -  "query",
        -  "totalCount",
        -  "appliedSources"
        -]
    • Changedpubmed_fetch_articles6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "articles",
        +      "totalReturned"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `invalid_efetch_response`: NCBI EFetch returned a payload missing the PubmedArticleSet wrapper. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found",
        +            "invalid_efetch_response"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "articles",
        -  "totalReturned"
        -]
    • Changedpubmed_fetch_fulltext7 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "articles",
        +      "totalReturned"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `unpaywall_unreachable`: Unpaywall was unreachable when resolving a DOI or fetching content. `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found",
        +            "unpaywall_unreachable",
        +            "europepmc_unreachable",
        +            "europepmc_invalid_response",
        +            "europepmc_invalid_input"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • addedOutput schema / properties / truncated
        Added value: +{
        +  "description": "True when a character budget shortened at least one returned body. Absent when every returned article carries its full post-filter body. The per-article accounting is in `truncation`.",
        +  "type": "boolean"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "articles",
        -  "totalReturned"
        -]
    • Changedpubmed_find_related6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "sourcePmid",
        +      "relationship",
        +      "offset",
        +      "articles",
        +      "totalCount",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). `europepmc_unreachable`: Europe PMC was unreachable after all retry attempts. `europepmc_invalid_response`: Europe PMC returned a body that could not be parsed (invalid JSON or XML). `europepmc_invalid_input`: Europe PMC rejected the request input (empty query, unknown sort field, malformed parameter). `openalex_unreachable`: OpenAlex was unreachable after all retry attempts. `openalex_invalid_response`: OpenAlex returned a body that could not be parsed (invalid JSON). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found",
        +            "europepmc_unreachable",
        +            "europepmc_invalid_response",
        +            "europepmc_invalid_input",
        +            "openalex_unreachable",
        +            "openalex_invalid_response"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "sourcePmid",
        -  "relationship",
        -  "offset",
        -  "articles",
        -  "totalCount",
        -  "source"
        -]
    • Changedpubmed_format_citations6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "citations",
        +      "totalSubmitted",
        +      "totalFormatted"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "citations",
        -  "totalSubmitted",
        -  "totalFormatted"
        -]
    • Changedpubmed_lookup_citation6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "results",
        +      "totalMatched",
        +      "totalSubmitted",
        +      "totalWarnings"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "results",
        -  "totalMatched",
        -  "totalSubmitted",
        -  "totalWarnings"
        -]
    • Changedpubmed_lookup_mesh6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "query",
        +      "offset",
        +      "results",
        +      "totalCount"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "query",
        -  "offset",
        -  "results",
        -  "totalCount"
        -]
    • Changedpubmed_search_articles6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "query",
        +      "offset",
        +      "pmids",
        +      "summaries",
        +      "searchUrl",
        +      "effectiveQuery",
        +      "totalCount",
        +      "appliedFilters"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "query",
        -  "offset",
        -  "pmids",
        -  "summaries",
        -  "searchUrl",
        -  "effectiveQuery",
        -  "totalCount",
        -  "appliedFilters"
        -]
    • Changedpubmed_spell_check6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "original",
        +      "corrected",
        +      "hasSuggestion"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `queue_full`: Local NCBI request queue is at capacity. `ncbi_unreachable`: NCBI E-utilities is unreachable after all retry attempts. `ncbi_deadline_exceeded`: Total request deadline expired before NCBI returned a response. `ncbi_invalid_response`: NCBI returned a body that could not be parsed (invalid XML/JSON). `ncbi_resource_not_found`: NCBI returned a structured \"not found\" error for the requested ID(s). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "queue_full",
        +            "ncbi_unreachable",
        +            "ncbi_deadline_exceeded",
        +            "ncbi_invalid_response",
        +            "ncbi_resource_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "original",
        -  "corrected",
        -  "hasSuggestion"
        -]
  9. 7 tool updatesv2.10.2
    • Changedpubmed_convert_ids1 field changed
      • changedOutput schema / properties / records / items / properties / doi / description
        Previous value: -"Digital Object Identifier; absent if no DOI is on record"New value: +"Digital Object Identifier, cased as the PMC ID Converter reports it; absent if no DOI is on record. DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ from a Europe PMC-sourced `doi` — compare the two case-insensitively."
    • Addedpubmed_europepmc_fetch
    • Changedpubmed_europepmc_search5 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"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`. Free text is matched broadly across abstract/title/keywords."New value: +"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 combined with `AND SRC:` must be unquoted — the quoted form matches nothing. Free text is matched broadly across abstract/title/keywords."
      • changedOutput schema / properties / hits / items / properties / abstractSnippet / description
        Previous value: -"First few hundred characters of the abstract as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded — when `resultType: \"core\"` is requested"New value: +"First 400 characters of the abstract as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded — when `resultType: \"core\"` is requested, with a trailing … appended when the abstract was cut. Check `abstractTruncated` before treating it as the whole abstract."
      • addedOutput schema / properties / hits / items / properties / abstractTruncated
        Added value: +{
        +  "description": "Whether `abstractSnippet` was cut short of the full abstract. Retrieve the complete text with `pubmed_europepmc_fetch` using this record’s `source` and `epmcId`. Present whenever `abstractSnippet` is; omitted when Europe PMC carries no abstract.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / hits / items / properties / doi / description
        Previous value: -"DOI when present"New value: +"DOI when present, cased as Europe PMC reports it. DOIs are case-insensitive by spec and no case normalization is applied here, so the same DOI can arrive in a different case from `pubmed_fetch_articles` (Europe PMC `10.1056/nejmoa2212948`, NCBI `10.1056/NEJMoa2212948`) — a byte-for-byte comparison across the two reports a false mismatch."
      • changedOutput schema / properties / hits / items / properties / epmcId / description
        Previous value: -"Europe PMC's internal record id; key for `fullTextXML` lookup"New value: +"Europe PMC's internal record id. Pass it with this hit's `source` to `pubmed_europepmc_fetch` for the complete record. Europe PMC's `fullTextXML` is keyed on `pmcId`, not on this id, so records without a PMC counterpart have no full text to fetch."
    • Changedpubmed_fetch_articles1 field changed
      • changedOutput schema / properties / articles / items / properties / doi / description
        Previous value: -"DOI"New value: +"DOI, cased as NCBI reports it (usually the publisher's mixed case). DOIs are case-insensitive by spec and no case normalization is applied here, so the same DOI can arrive in a different case from `pubmed_europepmc_search` and `pubmed_europepmc_fetch` (NCBI `10.1056/NEJMoa2212948`, Europe PMC `10.1056/nejmoa2212948`) — a byte-for-byte comparison across the two reports a false mismatch."
    • Changedpubmed_fetch_fulltext11 fields changed
      • addedInput schema / properties / maxCharacters
        Added value: +{
        +  "description": "Per-article budget for body text, in characters. Counts `source=pmc` section and subsection text, or the `source=unpaywall` `content` body; titles, abstracts, identifiers, and references are never counted or shortened. Applied after `sections`, `maxSections`, and `includeReferences`, so semantic filtering is unaffected. The response-wide ceiling is this value times the number of articles returned. Omit for the full body.",
        +  "maximum": 1000000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedInput schema / properties / maxCharactersPerSection
        Added value: +{
        +  "description": "Budget for a single top-level body 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.",
        +  "maximum": 1000000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedInput schema / properties / overflowMode
        Added value: +{
        +  "default": "truncate",
        +  "description": "How to spend `maxCharacters` across an article that exceeds it. truncate: fill sections in document order, so early sections stay whole and sections past the budget are dropped (counted in `truncation.omittedSections`). outline: split the budget evenly so every section keeps its heading, and an excerpt as far as the budget reaches — 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.",
        +  "enum": [
        +    "truncate",
        +    "outline"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / pmcids / description
        Previous value: -"PMC IDs to fetch (e.g. [\"PMC9575052\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`."New value: +"PMC IDs to fetch (e.g. [\"PMC9575052\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`. PMC IDs with no retrievable full text fall through to Europe PMC, then to Unpaywall on the DOI the chain resolves for them."
      • changedOutput schema / properties / articles / items / oneOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        -    "properties": {
        -      "abstract": {
        -        "description": "Abstract",
        -        "type": "string"
        -      },
        -      "affiliations": {
        -        "description": "Author affiliations",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "articleType": {
        -        "description": "Article type",
        -        "type": "string"
        -      },
        -      "authors": {
        -        "description": "Authors",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Author entry",
        -          "properties": {
        -            "collectiveName": {
        -              "description": "Group name",
        -              "type": "string"
        -            },
        -            "givenNames": {
        -              "description": "Given names",
        -              "type": "string"
        -            },
        -            "lastName": {
        -              "description": "Last name",
        -              "type": "string"
        -            }
        -          },
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "doi": {
        -        "description": "DOI",
        -        "type": "string"
        -      },
        -      "epmcId": {
        -        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        -        "type": "string"
        -      },
        -      "epmcSource": {
        -        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        -        "type": "string"
        -      },
        -      "journal": {
        -        "additionalProperties": false,
        -        "description": "Journal information",
        -        "properties": {
        -          "issn": {
        -            "description": "ISSN",
        -            "type": "string"
        -          },
        -          "issue": {
        -            "description": "Issue number",
        -            "type": "string"
        -          },
        -          "pages": {
        -            "description": "Page range",
        -            "type": "string"
        -          },
        -          "title": {
        -            "description": "Journal title",
        -            "type": "string"
        -          },
        -          "volume": {
        -            "description": "Volume number",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "keywords": {
        -        "description": "Keywords",
        -        "items": {
        -          "type": "string"
        -        },
        -        "type": "array"
        -      },
        -      "pmcId": {
        -        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        -        "type": "string"
        -      },
        -      "pmcUrl": {
        -        "description": "PMC URL — derived from `pmcId` when present",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID",
        -        "type": "string"
        -      },
        -      "publicationDate": {
        -        "additionalProperties": false,
        -        "description": "Publication date",
        -        "properties": {
        -          "day": {
        -            "description": "Publication day",
        -            "type": "string"
        -          },
        -          "month": {
        -            "description": "Publication month",
        -            "type": "string"
        -          },
        -          "year": {
        -            "description": "Publication year",
        -            "type": "string"
        -          }
        -        },
        -        "type": "object"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL",
        -        "type": "string"
        -      },
        -      "references": {
        -        "description": "Reference list",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Reference entry",
        -          "properties": {
        -            "citation": {
        -              "description": "Citation text",
        -              "type": "string"
        -            },
        -            "id": {
        -              "description": "Reference ID",
        -              "type": "string"
        -            },
        -            "label": {
        -              "description": "Reference label",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "citation"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "sections": {
        -        "description": "Article body sections",
        -        "items": {
        -          "additionalProperties": false,
        -          "description": "Article body section",
        -          "properties": {
        -            "label": {
        -              "description": "Section label",
        -              "type": "string"
        -            },
        -            "subsections": {
        -              "description": "Nested subsections",
        -              "items": {
        -                "additionalProperties": false,
        -                "description": "Article subsection",
        -                "properties": {
        -                  "label": {
        -                    "description": "Subsection label",
        -                    "type": "string"
        -                  },
        -                  "text": {
        -                    "description": "Subsection body text",
        -                    "type": "string"
        -                  },
        -                  "title": {
        -                    "description": "Subsection heading",
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "text"
        -                ],
        -                "type": "object"
        -              },
        -              "type": "array"
        -            },
        -            "text": {
        -              "description": "Section body text",
        -              "type": "string"
        -            },
        -            "title": {
        -              "description": "Section heading",
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "text"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "source": {
        -        "const": "pmc",
        -        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Article title",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        -        "enum": [
        -          "pmc",
        -          "europepmc"
        -        ],
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "sections"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "additionalProperties": false,
        -    "description": "Best-effort full text from an open-access copy",
        -    "properties": {
        -      "content": {
        -        "description": "Full article text — Markdown or plain text per `contentFormat`",
        -        "type": "string"
        -      },
        -      "contentFormat": {
        -        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        -        "enum": [
        -          "html-markdown",
        -          "pdf-text"
        -        ],
        -        "type": "string"
        -      },
        -      "doi": {
        -        "description": "DOI used to locate the open-access copy",
        -        "type": "string"
        -      },
        -      "hostType": {
        -        "description": "`publisher` or `repository` — where the OA copy is hosted",
        -        "type": "string"
        -      },
        -      "license": {
        -        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        -        "type": "string"
        -      },
        -      "pmid": {
        -        "description": "PubMed ID when input was `pmids`; absent for `dois` input",
        -        "type": "string"
        -      },
        -      "pubmedUrl": {
        -        "description": "PubMed URL — present when `pmid` is set",
        -        "type": "string"
        -      },
        -      "source": {
        -        "const": "unpaywall",
        -        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        -        "type": "string"
        -      },
        -      "sourceUrl": {
        -        "description": "URL the content was fetched from",
        -        "type": "string"
        -      },
        -      "title": {
        -        "description": "Detected article title when present",
        -        "type": "string"
        -      },
        -      "totalPages": {
        -        "description": "Page count reported by the PDF extractor; absent for HTML",
        -        "type": "number"
        -      },
        -      "version": {
        -        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        -        "type": "string"
        -      },
        -      "viaSource": {
        -        "const": "unpaywall",
        -        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        -        "type": "string"
        -      },
        -      "wordCount": {
        -        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "source",
        -      "viaSource",
        -      "contentFormat",
        -      "doi",
        -      "sourceUrl",
        -      "content"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "description": "Structured JATS full-text article. `viaSource` records whether the JATS came from NCBI PMC or Europe PMC.",
        +    "properties": {
        +      "abstract": {
        +        "description": "Abstract",
        +        "type": "string"
        +      },
        +      "affiliations": {
        +        "description": "Author affiliations",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "articleType": {
        +        "description": "Article type",
        +        "type": "string"
        +      },
        +      "authors": {
        +        "description": "Authors",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Author entry",
        +          "properties": {
        +            "collectiveName": {
        +              "description": "Group name",
        +              "type": "string"
        +            },
        +            "givenNames": {
        +              "description": "Given names",
        +              "type": "string"
        +            },
        +            "lastName": {
        +              "description": "Last name",
        +              "type": "string"
        +            }
        +          },
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "doi": {
        +        "description": "DOI, cased as the tier that served this record reports it (NCBI PMC, Europe PMC, or Unpaywall). DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ between tiers and from other tools — compare case-insensitively.",
        +        "type": "string"
        +      },
        +      "epmcId": {
        +        "description": "Europe PMC record id — present when `viaSource` is `europepmc`",
        +        "type": "string"
        +      },
        +      "epmcSource": {
        +        "description": "Europe PMC source code when `viaSource` is `europepmc`. Common values: `MED` (PubMed-derived), `PMC` (PMC counterpart), `PPR` (preprint), `PAT` (patent), `AGR` (Agricola), plus less common codes (`CTX`, `CBA`, `ETH`, `HIR`). Treat as opaque — EPMC may introduce new codes.",
        +        "type": "string"
        +      },
        +      "journal": {
        +        "additionalProperties": false,
        +        "description": "Journal information",
        +        "properties": {
        +          "issn": {
        +            "description": "ISSN",
        +            "type": "string"
        +          },
        +          "issue": {
        +            "description": "Issue number",
        +            "type": "string"
        +          },
        +          "pages": {
        +            "description": "Page range",
        +            "type": "string"
        +          },
        +          "title": {
        +            "description": "Journal title",
        +            "type": "string"
        +          },
        +          "volume": {
        +            "description": "Volume number",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "keywords": {
        +        "description": "Keywords",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID — present for NCBI PMC records and Europe PMC entries that have a PMC counterpart. Absent for EPMC-only records like preprints; use `epmcId` in that case.",
        +        "type": "string"
        +      },
        +      "pmcUrl": {
        +        "description": "PMC URL — derived from `pmcId` when present",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID",
        +        "type": "string"
        +      },
        +      "publicationDate": {
        +        "additionalProperties": false,
        +        "description": "Publication date",
        +        "properties": {
        +          "day": {
        +            "description": "Publication day",
        +            "type": "string"
        +          },
        +          "month": {
        +            "description": "Publication month",
        +            "type": "string"
        +          },
        +          "year": {
        +            "description": "Publication year",
        +            "type": "string"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL",
        +        "type": "string"
        +      },
        +      "references": {
        +        "description": "Reference list",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Reference entry",
        +          "properties": {
        +            "citation": {
        +              "description": "Citation text",
        +              "type": "string"
        +            },
        +            "id": {
        +              "description": "Reference ID",
        +              "type": "string"
        +            },
        +            "label": {
        +              "description": "Reference label",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "citation"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "sections": {
        +        "description": "Article body sections",
        +        "items": {
        +          "additionalProperties": false,
        +          "description": "Article body section",
        +          "properties": {
        +            "label": {
        +              "description": "Section label",
        +              "type": "string"
        +            },
        +            "subsections": {
        +              "description": "Nested subsections",
        +              "items": {
        +                "additionalProperties": false,
        +                "description": "Article subsection",
        +                "properties": {
        +                  "label": {
        +                    "description": "Subsection label",
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "description": "Subsection body text",
        +                    "type": "string"
        +                  },
        +                  "title": {
        +                    "description": "Subsection heading",
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "type": "array"
        +            },
        +            "text": {
        +              "description": "Section body text",
        +              "type": "string"
        +            },
        +            "title": {
        +              "description": "Section heading",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "text"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "source": {
        +        "const": "pmc",
        +        "description": "Structured JATS — same DTD whether sourced from NCBI PMC or Europe PMC",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Article title",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "description": "Which layer produced the JATS: `pmc` for NCBI PMC EFetch (db=pmc), `europepmc` for Europe PMC `fullTextXML`. Both paths return the same JATS shape; the discriminator records origin for observability and license attribution.",
        +        "enum": [
        +          "pmc",
        +          "europepmc"
        +        ],
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "sections"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "additionalProperties": false,
        +    "description": "Best-effort full text from an open-access copy",
        +    "properties": {
        +      "content": {
        +        "description": "Full article text — Markdown or plain text per `contentFormat`",
        +        "type": "string"
        +      },
        +      "contentFormat": {
        +        "description": "How `content` was extracted. html-markdown: Defuddle extracted Markdown from an HTML landing page; light section structure may survive but is not guaranteed. pdf-text: unpdf extracted plain text from a PDF; no section, reference, or heading structure.",
        +        "enum": [
        +          "html-markdown",
        +          "pdf-text"
        +        ],
        +        "type": "string"
        +      },
        +      "doi": {
        +        "description": "DOI used to locate the open-access copy",
        +        "type": "string"
        +      },
        +      "hostType": {
        +        "description": "`publisher` or `repository` — where the OA copy is hosted",
        +        "type": "string"
        +      },
        +      "license": {
        +        "description": "License identifier from Unpaywall (e.g. cc-by, cc0)",
        +        "type": "string"
        +      },
        +      "pmcId": {
        +        "description": "PMC ID this article was requested under, in `PMC<digits>` form — present for `pmcids` input, absent for `pmids` and `dois` input. Ties the article back to the requested identifier, which `unavailable[]` keys on for the ids that found nothing.",
        +        "type": "string"
        +      },
        +      "pmid": {
        +        "description": "PubMed ID when input was `pmids`; absent for `pmcids` and `dois` input",
        +        "type": "string"
        +      },
        +      "pubmedUrl": {
        +        "description": "PubMed URL — present when `pmid` is set",
        +        "type": "string"
        +      },
        +      "source": {
        +        "const": "unpaywall",
        +        "description": "Content fetched from an open-access copy indexed by Unpaywall. Best-effort — structural fidelity depends on `contentFormat`.",
        +        "type": "string"
        +      },
        +      "sourceUrl": {
        +        "description": "URL the content was fetched from",
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "Detected article title when present",
        +        "type": "string"
        +      },
        +      "totalPages": {
        +        "description": "Page count reported by the PDF extractor; absent for HTML",
        +        "type": "number"
        +      },
        +      "version": {
        +        "description": "OA version: submittedVersion | acceptedVersion | publishedVersion",
        +        "type": "string"
        +      },
        +      "viaSource": {
        +        "const": "unpaywall",
        +        "description": "Layer that produced this article. Constant `unpaywall` for this branch.",
        +        "type": "string"
        +      },
        +      "wordCount": {
        +        "description": "Approximate word count reported by the HTML extractor; absent for PDFs",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "viaSource",
        +      "contentFormat",
        +      "doi",
        +      "sourceUrl",
        +      "content"
        +    ],
        +    "type": "object"
        +  }
        +]
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance when a `sections` filter removed every body section — names the requested section terms and the affected article id(s), and suggests retrying without `sections` or using broader headings. Absent when no section filter was applied or sections matched."New value: +"Optional guidance for a partial or empty body. A `sections`-filter miss names the requested terms and affected article id(s) and suggests retrying without `sections` or using broader headings. A metadata-only record names the id(s) the chain could retrieve as front matter only and points at `pubmed_fetch_articles` for the abstract. A budgeted response names the characters returned versus carried and points at `truncation`. Absent when none of those applies."
      • addedOutput schema / properties / truncation
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "Character accounting for full text the budget shortened. Present only when a budget actually removed characters — its absence means every returned article carries its full post-filter body.",
        +  "properties": {
        +    "articles": {
        +      "description": "Per-article accounting, covering only the articles the budget shortened",
        +      "items": {
        +        "additionalProperties": false,
        +        "description": "Character accounting for one article the budget shortened",
        +        "properties": {
        +          "id": {
        +            "description": "Identifier for the article — PMCID, PMID, DOI, or Europe PMC id, whichever the article carries first",
        +            "type": "string"
        +          },
        +          "originalCharacters": {
        +            "description": "Body characters this article carried before the budget pass",
        +            "type": "number"
        +          },
        +          "returnedCharacters": {
        +            "description": "Body characters this article carries in the response",
        +            "type": "number"
        +          },
        +          "sections": {
        +            "description": "Per-section accounting for `source: pmc` articles, in document order, including sections dropped for budget. Absent for `source: unpaywall`, whose body has no section structure.",
        +            "items": {
        +              "additionalProperties": false,
        +              "description": "Character accounting for one body section of a budgeted article",
        +              "properties": {
        +                "originalCharacters": {
        +                  "description": "Body characters this section carried before the budget pass",
        +                  "type": "number"
        +                },
        +                "returnedCharacters": {
        +                  "description": "Body characters this section carries in the response. Zero means the section was dropped in `truncate` mode, or kept as a heading-only entry in `outline` mode.",
        +                  "type": "number"
        +                },
        +                "title": {
        +                  "description": "Section heading, when the section carries one",
        +                  "type": "string"
        +                },
        +                "truncated": {
        +                  "description": "True when the section returned fewer characters than it originally carried",
        +                  "type": "boolean"
        +                }
        +              },
        +              "required": [
        +                "originalCharacters",
        +                "returnedCharacters",
        +                "truncated"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          },
        +          "source": {
        +            "description": "Which output shape was budgeted: `pmc` budgets body sections and subsections, `unpaywall` budgets the single `content` body",
        +            "enum": [
        +              "pmc",
        +              "unpaywall"
        +            ],
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "source",
        +          "originalCharacters",
        +          "returnedCharacters"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "maxCharacters": {
        +      "description": "The `maxCharacters` budget applied, when set",
        +      "type": "number"
        +    },
        +    "maxCharactersPerSection": {
        +      "description": "The `maxCharactersPerSection` budget applied, when set",
        +      "type": "number"
        +    },
        +    "mode": {
        +      "description": "The `overflowMode` that produced these results",
        +      "enum": [
        +        "truncate",
        +        "outline"
        +      ],
        +      "type": "string"
        +    },
        +    "omittedSections": {
        +      "description": "Body sections dropped entirely because an article budget was exhausted before reaching them. Always 0 in `outline` mode, which keeps every heading.",
        +      "type": "number"
        +    },
        +    "originalCharacters": {
        +      "description": "Body characters the shortened articles carried before the budget pass",
        +      "type": "number"
        +    },
        +    "returnedCharacters": {
        +      "description": "Body characters the shortened articles carry in this response",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "mode",
        +    "originalCharacters",
        +    "returnedCharacters",
        +    "omittedSections",
        +    "articles"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / unavailable / items / properties / reason / description
        Previous value: -"Why no full text was returned. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data)."New value: +"Why no full text was returned. not-found: upstream returned no record for this ID. no-pmc-fallback-disabled: every tier was skipped (`triedTiers` is all `not-attempted`) — typically because EPMC (`EUROPEPMC_ENABLED`) and Unpaywall (`UNPAYWALL_EMAIL`) are not configured. no-epmc-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the record was retrieved but carries front matter and abstract only, with no body sections — use `pubmed_fetch_articles` for the metadata. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall has no OA copy. fetch-failed: download failed. parse-failed: extraction empty. service-error: upstream server failure (threw, timed out, or returned malformed data)."
      • changedOutput schema / properties / unavailable / items / properties / reason / enum
        Previous value: -[
        -  "not-found",
        -  "no-pmc-fallback-disabled",
        -  "no-epmc-fulltext",
        -  "no-doi",
        -  "no-oa",
        -  "fetch-failed",
        -  "parse-failed",
        -  "service-error"
        -]New value: +[
        +  "not-found",
        +  "no-pmc-fallback-disabled",
        +  "no-epmc-fulltext",
        +  "no-body",
        +  "no-doi",
        +  "no-oa",
        +  "fetch-failed",
        +  "parse-failed",
        +  "service-error"
        +]
      • changedOutput schema / properties / unavailable / items / properties / triedTiers / items / properties / outcome / description
        Previous value: -"Per-tier outcome. not-attempted: tier was skipped. miss: tier returned no record. no-fulltext: EPMC indexed the record but publishes no fullTextXML. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall reports no open-access copy. fetch-failed: OA copy download failed. parse-failed: extraction produced empty content. service-error: tier service threw."New value: +"Per-tier outcome. not-attempted: tier was skipped. miss: tier returned no record. no-fulltext: EPMC indexed the record but publishes no fullTextXML. no-body: the tier returned a record with front matter and abstract but no body sections, so the chain continued. no-doi: no DOI to query Unpaywall. no-oa: Unpaywall reports no open-access copy. fetch-failed: OA copy download failed. parse-failed: extraction produced empty content. service-error: tier service threw."
      • changedOutput schema / properties / unavailable / items / properties / triedTiers / items / properties / outcome / enum
        Previous value: -[
        -  "not-attempted",
        -  "miss",
        -  "no-fulltext",
        -  "no-doi",
        -  "no-oa",
        -  "fetch-failed",
        -  "parse-failed",
        -  "service-error"
        -]New value: +[
        +  "not-attempted",
        +  "miss",
        +  "no-fulltext",
        +  "no-body",
        +  "no-doi",
        +  "no-oa",
        +  "fetch-failed",
        +  "parse-failed",
        +  "service-error"
        +]
    • Changedpubmed_lookup_mesh6 fields changed
      • addedInput schema / properties / offset
        Added value: +{
        +  "default": 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.",
        +  "maximum": 9007199254740991,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / nextOffset
        Added value: +{
        +  "description": "Offset to request for the next page. Omitted when this is the last page, so its absence is the end-of-results signal.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance when no descriptors matched — suggests spell-check or free-text search. Absent on successful results."New value: +"Optional guidance when no descriptors matched or the offset overshot the result set — suggests spell-check, free-text search, or resetting the offset. Absent on successful result pages."
      • addedOutput schema / properties / offset
        Added value: +{
        +  "description": "Result offset this page was read from",
        +  "type": "number"
        +}
      • changedOutput schema / properties / totalCount / description
        Previous value: -"Total matching MeSH descriptors"New value: +"Total MeSH descriptors matching the query upstream, before the maxResults cap"
      • changedOutput schema / required
        Previous value: -[
        -  "query",
        -  "results",
        -  "totalCount"
        -]New value: +[
        +  "query",
        +  "offset",
        +  "results",
        +  "totalCount"
        +]
    • Changedpubmed_search_articles5 fields changed
      • changedInput schema / properties / offset / description
        Previous value: -"Result offset for pagination (0-based)"New value: +"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."
      • changedInput schema / properties / offset / maximum
        Previous value: -9007199254740991New value: +9998
      • changedInput schema / properties / summaryCount / description
        Previous value: -"Fetch brief summaries for top N results (0 = PMIDs only)"New value: +"Fetch brief summaries for top N results (0 = PMIDs only). Above the 50 cap, pass the remaining PMIDs to pubmed_fetch_articles."
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance when results are empty or paging overshot — e.g. how to broaden filters or reset offset. Absent on successful result pages."New value: +"Optional guidance when the result set does not reflect what was asked for — a field tag PubMed ignored, a phrase it matched nothing for, a dateRange dropped for having one bound, no matches at all, or paging past the end. Absent when nothing applies."
      • changedOutput schema / properties / summaries / items / properties / doi / description
        Previous value: -"DOI"New value: +"DOI, cased as NCBI reports it. DOIs are case-insensitive by spec and no case normalization is applied here, so casing can differ from a Europe PMC-sourced `doi` — compare the two case-insensitively."
  10. 1 tool updatev2.9.8
    • Changedpubmed_fetch_fulltext1 field changed
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Optional guidance when a `sections` filter removed every body section — names the requested section terms and the affected article id(s), and suggests retrying without `sections` or using broader headings. Absent when no section filter was applied or sections matched.",
        +  "type": "string"
        +}
  11. 3 tool updatesv2.9.6
    • Changedpubmed_convert_ids1 field changed
      • changedInput schema / properties / ids / description
        Previous value: -"Article identifiers to convert. All IDs must be the same type. DOIs: \"10.1093/nar/gks1195\", PMIDs: \"23193287\", PMCIDs: \"PMC3531190\"."New value: +"Article identifiers to convert. All IDs must be the same type. DOIs: \"10.1093/nar/gks1195\", PMIDs: \"23193287\", PMCIDs: \"PMC3531190\" (the \"PMC\" prefix is optional — bare digits like \"3531190\" are also accepted)."
    • Changedpubmed_europepmc_search1 field changed
      • changedOutput schema / properties / hits / items / properties / abstractSnippet / description
        Previous value: -"First few hundred characters of the abstract when `resultType: \"core\"` is requested"New value: +"First few hundred characters of the abstract as display-ready plain text — JATS/HTML markup stripped and HTML entities decoded — when `resultType: \"core\"` is requested"
    • Changedpubmed_lookup_mesh4 fields changed
      • addedOutput schema / properties / results / items / properties / entrezUid
        Added value: +{
        +  "description": "NCBI Entrez UID for this record — the join key for E-utilities (eSummary/eFetch db=mesh).",
        +  "type": "string"
        +}
      • changedOutput schema / properties / results / items / properties / meshId / description
        Previous value: -"MeSH descriptor unique identifier"New value: +"Canonical MeSH DescriptorUI (e.g. \"D003924\") — resolves at the MeSH Browser and NLM linked data. Falls back to the raw Entrez UID when a record is not decodable."
      • changedOutput schema / properties / results / items / properties / treeNumbers / description
        Previous value: -"MeSH tree numbers"New value: +"Navigable MeSH tree numbers (e.g. \"D02.078.370.141.450\"). Omitted for supplementary concept records (SCRs), which map to a heading rather than occupying a tree position."
      • changedOutput schema / properties / results / items / required
        Previous value: -[
        -  "meshId",
        -  "name"
        -]New value: +[
        +  "meshId",
        +  "entrezUid",
        +  "name"
        +]
  12. 4 tool updatesv2.9.4
    • Changedpubmed_europepmc_search3 fields changed
      • removedOutput schema / properties / hitCount
        Removed value: -{
        -  "description": "Total matching records across all pages",
        -  "type": "number"
        -}
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total matching records across all pages",
        +  "type": "number"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "hits",
        -  "cursorMark",
        -  "searchUrl",
        -  "query",
        -  "hitCount",
        -  "appliedSources"
        -]New value: +[
        +  "hits",
        +  "cursorMark",
        +  "searchUrl",
        +  "query",
        +  "totalCount",
        +  "appliedSources"
        +]
    • Changedpubmed_find_related3 fields changed
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total related articles found before windowing",
        +  "type": "number"
        +}
      • removedOutput schema / properties / totalFound
        Removed value: -{
        -  "description": "Total related articles found before windowing",
        -  "type": "number"
        -}
      • changedOutput schema / required
        Previous value: -[
        -  "sourcePmid",
        -  "relationship",
        -  "offset",
        -  "articles",
        -  "totalFound",
        -  "source"
        -]New value: +[
        +  "sourcePmid",
        +  "relationship",
        +  "offset",
        +  "articles",
        +  "totalCount",
        +  "source"
        +]
    • Changedpubmed_lookup_mesh2 fields changed
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total matching MeSH descriptors",
        +  "type": "number"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "query",
        -  "results"
        -]New value: +[
        +  "query",
        +  "results",
        +  "totalCount"
        +]
    • Changedpubmed_search_articles3 fields changed
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total matching articles",
        +  "type": "number"
        +}
      • removedOutput schema / properties / totalFound
        Removed value: -{
        -  "description": "Total matching articles",
        -  "type": "number"
        -}
      • changedOutput schema / required
        Previous value: -[
        -  "query",
        -  "offset",
        -  "pmids",
        -  "summaries",
        -  "searchUrl",
        -  "effectiveQuery",
        -  "totalFound",
        -  "appliedFilters"
        -]New value: +[
        +  "query",
        +  "offset",
        +  "pmids",
        +  "summaries",
        +  "searchUrl",
        +  "effectiveQuery",
        +  "totalCount",
        +  "appliedFilters"
        +]
  13. 6 tool updatesv2.9.1
    • Changedpubmed_europepmc_search1 field changed
      • changedInput schema / properties / sort / description
        Previous value: -"Optional EPMC sort: `<field> asc|desc`. Documented sortable fields: `P_PDATE_D` (publication date), `CITED` (citation count), `AUTH_FIRST` (first author surname), `PUB_YEAR` (publication year). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking. Fields outside the documented set are rejected by EPMC."New value: +"Optional EPMC sort: `<field> asc|desc`. Documented sortable fields: `P_PDATE_D` (publication date), `CITED` (citation count), `AUTH_FIRST` (first author surname), `PUB_YEAR` (publication year). Examples: `P_PDATE_D desc` (newest first), `CITED desc` (most cited). Omit for relevance ranking. Fields outside the documented set are rejected by EPMC. Note: `P_PDATE_D` is ignored for preprint-only (`sources: [\"PPR\"]`) result sets — preprints have no populated publication date, so use `PUB_YEAR` to order preprints by date."
    • Changedpubmed_fetch_articles1 field changed
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Optional guidance when no articles were returned — points to discovery tools. Absent on successful fetches.",
        +  "type": "string"
        +}
    • Changedpubmed_fetch_fulltext1 field changed
      • changedInput schema / properties / dois / description
        Previous value: -"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`. Covers preprints and EPMC-only OA records that lack PMID/PMCID. Chain: Europe PMC search-by-DOI → fullTextXML → Unpaywall."New value: +"DOIs to resolve (e.g. [\"10.21203/rs.3.rs-9010375/v1\"]). Provide exactly one of `pmcids`, `pmids`, or `dois`. 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."
    • Changedpubmed_find_related6 fields changed
      • addedInput schema / properties / offset
        Added value: +{
        +  "default": 0,
        +  "description": "Result offset for pagination (0-based); page through results by incrementing by maxResults",
        +  "maximum": 9007199254740991,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Optional guidance when results are empty — e.g. invalid source PMID, or references requested for a non-PMC source. Absent on successful result pages."New value: +"Guidance when results are empty, a fallback provider answered, or offset overshot. Absent on a clean NCBI result page."
      • addedOutput schema / properties / offset
        Added value: +{
        +  "description": "Result offset used",
        +  "type": "number"
        +}
      • addedOutput schema / properties / source
        Added value: +{
        +  "description": "Provider that answered this request",
        +  "enum": [
        +    "ncbi",
        +    "europepmc",
        +    "openalex"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / totalFound / description
        Previous value: -"Total related articles found before truncation"New value: +"Total related articles found before windowing"
      • changedOutput schema / required
        Previous value: -[
        -  "sourcePmid",
        -  "relationship",
        -  "articles",
        -  "totalFound"
        -]New value: +[
        +  "sourcePmid",
        +  "relationship",
        +  "offset",
        +  "articles",
        +  "totalFound",
        +  "source"
        +]
    • Changedpubmed_format_citations3 fields changed
      • changedInput schema / properties / format / anyOf
        Previous value: -[
        -  {
        -    "description": "Single citation style. One of: apa, mla, bibtex, ris.",
        -    "enum": [
        -      "apa",
        -      "mla",
        -      "bibtex",
        -      "ris"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "description": "Multiple citation styles to generate. Each entry: apa, mla, bibtex, or ris.",
        -    "items": {
        -      "enum": [
        -        "apa",
        -        "mla",
        -        "bibtex",
        -        "ris"
        -      ],
        -      "type": "string"
        -    },
        -    "minItems": 1,
        -    "type": "array"
        -  }
        -]New value: +[
        +  {
        +    "description": "Single citation style. One of: apa, mla, bibtex, ris, vancouver.",
        +    "enum": [
        +      "apa",
        +      "mla",
        +      "bibtex",
        +      "ris",
        +      "vancouver"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "description": "Multiple citation styles to generate. Each entry: apa, mla, bibtex, ris, or vancouver.",
        +    "items": {
        +      "enum": [
        +        "apa",
        +        "mla",
        +        "bibtex",
        +        "ris",
        +        "vancouver"
        +      ],
        +      "type": "string"
        +    },
        +    "minItems": 1,
        +    "type": "array"
        +  }
        +]
      • changedInput schema / properties / format / description
        Previous value: -"Citation format(s) to generate — single style as a string or multiple as an array. Allowed values: apa, mla, bibtex, ris."New value: +"Citation format(s) to generate — single style as a string or multiple as an array. Allowed values: apa, mla, bibtex, ris, vancouver."
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Optional guidance when no citations were produced — points to discovery tools. Absent when at least one citation was produced.",
        +  "type": "string"
        +}
    • Changedpubmed_lookup_citation1 field changed
      • changedInput schema / properties / citations / items / description
        Previous value: -"Citation to match against PubMed. Must include at least one bibliographic field (journal, year, volume, firstPage, or authorName)."New value: +"Citation to match against PubMed. Must include at least journal or year — ECitMatch primary-keys on journal+volume+page, so author-only or volume-only inputs guarantee no match."
  14. 2 tool updatesv2.7.8
    • Changedpubmed_europepmc_search1 field changed
      • changedOutput schema / required
        Previous value: -[
        -  "query",
        -  "hits",
        -  "hitCount",
        -  "cursorMark",
        -  "appliedSources",
        -  "searchUrl"
        -]New value: +[
        +  "hits",
        +  "cursorMark",
        +  "searchUrl",
        +  "query",
        +  "hitCount",
        +  "appliedSources"
        +]
    • Changedpubmed_search_articles1 field changed
      • changedOutput schema / required
        Previous value: -[
        -  "query",
        -  "effectiveQuery",
        -  "appliedFilters",
        -  "totalFound",
        -  "offset",
        -  "pmids",
        -  "summaries",
        -  "searchUrl"
        -]New value: +[
        +  "query",
        +  "offset",
        +  "pmids",
        +  "summaries",
        +  "searchUrl",
        +  "effectiveQuery",
        +  "totalFound",
        +  "appliedFilters"
        +]
  15. 10 tool updatesv2.7.6
    • Addedpubmed_convert_ids
    • Addedpubmed_europepmc_search
    • Addedpubmed_fetch_articles
    • Addedpubmed_fetch_fulltext
    • Addedpubmed_find_related
    • Addedpubmed_format_citations
    • Addedpubmed_lookup_citation
    • Addedpubmed_lookup_mesh
    • Addedpubmed_search_articles
    • Addedpubmed_spell_check

TDQS

A4.1/5.0

Scored across 12 tools

Disambiguation4/5

The tools mostly target distinct operations differentiated by corpus, identifier type, and output. There is some overlap between pubmed_search_articles and pubmed_europepmc_search, and between pubmed_fetch_articles and pubmed_europepmc_fetch, but the descriptions clarify when each should be used.

Naming Consistency4/5

All tool names use snake_case with a consistent pubmed_ prefix, which makes the namespace clear and readable. However, verb-object ordering is not fully consistent, as seen when comparing pubmed_fetch_articles with pubmed_europepmc_fetch.

Tool Count5/5

The 12 tools are well-scoped for a biomedical literature research server and fall within a reasonable range. Search, retrieval, metrics, citation, identifier, related-article, and MeSH operations each justify their place.

Completeness5/5

The surface covers discovery, metadata and full-text retrieval, Europe PMC coverage, citation formatting and lookup, identifier conversion, related articles, citation metrics, spell checking, and MeSH vocabulary lookup. No obvious critical gap remains for common biomedical literature workflows.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers