Skip to main content
Glama

Server Details

Access the OpenAlex academic research catalog — 270M+ publications.

If you are the author of this connector, you can claim ownership with GitHub, an HTTP challenge, or a DNS record. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
Status
Healthy
Uptime
99.9% over 40 days
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
cyanheads/openalex-mcp-server
GitHub Stars
14
Server Listing
@cyanheads/openalex-mcp-server

TDQS

A4.5/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct operation: search/retrieve, name/ID resolution, aggregation, citation graph traversal, and field metadata. Even the adjacent pair (search_entities and resolve_name) is clearly separated by descriptions and explicit usage guidance. No two tools could reasonably be substituted for one another.

Naming Consistency5/5

All tools follow the same `openalex_<verb>_<object>` snake_case pattern: analyze_trends, describe_fields, get_citation_graph, resolve_name, search_entities. The verbs are semantically appropriate and the consistency makes the API surface predictable.

Tool Count5/5

Five tools is a well-scoped size for an OpenAlex API wrapper. Each tool covers a distinct high-value capability without redundancy, and the count is neither thin nor bloated for the breadth of the underlying service.

Completeness5/5

For a read-only scholarly data API, the surface is complete: full search/filter/retrieve across all entity types, ID and identifier resolution, aggregation/trend analysis, citation graph traversal, and proactive field validation. The tools have clear workflows with no dead ends and all common OpenAlex operations are covered.

Available Tools

5 tools
openalex_describe_fieldsOpenalex Describe FieldsA
Read-onlyIdempotent
Inspect

List valid field names for an OpenAlex entity type and context (filter, group_by, or select). Use proactively before constructing a filter or group_by to avoid invalid-field 400 errors. Pass query to rank the list by name similarity — useful when you have a partial or guessed field name. Ranking never drops a field: the full list comes back either way.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoOptional partial or guessed field name to sort results by similarity. Pass the field you tried (e.g. "funder") to get the closest matches first. The complete field list is returned either way — a query reorders it, it does not filter it, so a nested value's parent object (e.g. `summary_stats` for "h_index") is still reachable further down.
contextYesField usage context. "filter": fields accepted in the filter param. "group_by": fields accepted in group_by — a subset of the filter set that leaves out what OpenAlex refuses to aggregate (raw dates, *.search operators, decimal scores, display_name, and external-ID fields among them). "select": fields accepted in select.
entity_typeYesOpenAlex entity type to list fields for.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
totalNoTotal number of valid fields for this entity_type + context.
fieldsNoEvery valid field name for this entity_type + context — the complete pool, ranked by similarity when `query` is provided. Never truncated, so this always holds `total` entries.
contextNoContext queried (filter, group_by, or select).
entity_typeNoEntity type queried.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint and idempotentHint. The description adds useful behavioral nuance beyond these: 'Ranking never drops a field: the full list comes back either way,' clarifying that the query parameter only reorders results. This is helpful context not captured in structured fields. No contradiction with annotations.

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?

The description is three sentences, each with a distinct purpose: state the main action, give the proactive use case, and explain the query behavior. It is front-loaded with the verb and resource, and every sentence earns its place without redundancy or fluff.

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 that an output schema exists, the description does not need to detail return values. It covers the purpose, when to use it, and the notable query ranking behavior. Parameter details and entity/context enumerations are fully handled by the schema. The description is complete for correct agent invocation.

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?

The input schema has 100% description coverage, with detailed explanations for all three parameters (query, context, entity_type). The description itself adds no further parameter-level meaning, so it does not need to compensate. Baseline of 3 is appropriate because the schema carries the semantic weight.

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 opens with a specific verb and resource: 'List valid field names for an OpenAlex entity type and context (filter, group_by, or select).' The tool's operation is unmistakable and clearly distinct from the siblings (analyze_trends, get_citation_graph, resolve_name, search_entities), which perform different functions.

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 description gives explicit usage context: 'Use proactively before constructing a filter or group_by to avoid invalid-field 400 errors.' This tells the agent when to invoke the tool, but it does not explicitly mention alternatives or when not to use it. The sibling names themselves make the distinction obvious, so a small deduction is warranted.

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

openalex_get_citation_graphOpenalex Get Citation GraphA
Read-onlyIdempotent
Inspect

Walk the citation graph one hop from a seed work. Direction picks the edge: incoming citations (cites), the seed's own references (cited_by), or OpenAlex's algorithmically-related works (related_to). Note: direction follows OpenAlex's filter convention, which inverts the common English reading — cites returns works that cite the seed; cited_by returns works the seed cites. Results use the works schema; combine with filters/sort to narrow further.

ParametersJSON Schema
NameRequiredDescriptionDefault
sortNoSort field. Prefix with "-" for descending. Comma-separate for a multi-key sort, applied left to right, with the "-" prefix set per key ("-publication_year,cited_by_count" sorts by year descending, then citations ascending). Common: "cited_by_count", "-publication_date". Default is OpenAlex relevance.
cursorNoPagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page.
selectNoOpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted.
filtersNoAdditional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=">2020", is_oa="true". Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter.
seed_idYesSeed work identifier. Accepts OpenAlex ID ("W2741809807"), DOI ("10.1038/nature12373" or full URL), or PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title.
per_pageNoResults per page (1-100). Default 25.
directionYes"cites": works that cite seed_id (incoming citations). "cited_by": works that seed_id cites (its reference list). "related_to": OpenAlex algorithmically-related works (~8-30 typical, may be empty for less-cited seeds).

Output Schema

ParametersJSON Schema
NameRequiredDescription
echoNoCompact echo of seed_id, direction, filters, sort — surfaces what was actually queried when no edges are returned.
metaNoResult metadata including pagination.
errorNoPresent when the call failed. Absent on success.
budgetNoWhat this call cost against the OpenAlex daily budget and what is left of it. Price a full walk before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.
noticeNoGuidance when no edges are returned. A first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead. Absent when results are present.
resultsNoWorks on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed.
totalCountNoTotal edges from seed_id in this direction across all pages.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the readOnlyHint and idempotentHint annotations, the description discloses a critical behavioral quirk: the direction parameter follows OpenAlex's filter convention, so 'cites' returns works that cite the seed and 'cited_by' returns works the seed cites. It also sets expectations that related_to returns roughly 8-30 results and may be empty for less-cited seeds. This is exactly the kind of non-obvious behavior that prevents incorrect invocation.

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 sentences, each earning its place: the first defines scope, the second defines the critical parameter semantics, and the third provides the key inversion warning and result schema context. There is no filler, redundancy, or restating of the title. The most important caveat is front-loaded.

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 the annotations, full schema coverage, and presence of an output schema, the description is complete for its complexity. It covers the one-hop traversal, all direction semantics, the counterintuitive naming, result schema, and how to narrow results. Nothing an agent needs to call this tool correctly is missing.

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

Parameters5/5

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

Even though schema coverage is 100%, the description adds conceptual meaning not present in the schema: it explains that direction selects the edge type and warns about the inverted naming convention. It also clarifies that filters/sort operate over the works schema and should not include the direction-related keys, which prevents a common misuse. This meaningfully supplements the already-rich parameter descriptions.

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 opens with a specific verb and resource: 'Walk the citation graph one hop from a seed work.' It clearly distinguishes this tool from siblings like openalex_search_entities by framing it as graph traversal from a seed rather than entity search. The three directional options are explicitly enumerated, making the tool's purpose unmistakable.

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 description provides clear context: use this when you need one-hop citation graph traversal from a seed workcache. It advises combining with filters/sort to narrow furtherchers, but it does not explicitly state when to prefer this over sibling tools like openalex_search_entities or openalex_analyze_trends. No alternatives are named, though the purpose is clear enough that an agent can infer appropriate usage.

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

openalex_resolve_nameOpenalex Resolve NameA
Read-onlyIdempotent
Inspect

Resolve a name or an identifier to an OpenAlex ID. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. A name returns up to 10 autocomplete matches with disambiguation hints. An identifier — OpenAlex ID, DOI, ORCID, ROR, PMID, or ISSN, bare or in URL form — resolves directly to the one record it addresses, and needs no entity_type. A PMCID is recognized as well, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesName or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID ("W2741809807", "F4320332161"), DOI ("10.1038/nature12373"), ORCID ("0000-0002-1825-0097"), ROR ("https://ror.org/00hx57361"), PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"), ISSN ("1234-5678") — which resolves straight to that one record instead of running a name search. A keyword URL ("https://openalex.org/keywords/groundwater") resolves the same way; a bare keyword slug reads as a name and runs a name search, which finds it too. A PMCID ("PMC1234567" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead.
filtersNoNarrow autocomplete results with filters. Example: restrict to a specific country or publication year range. Applies to name queries only — an identifier already addresses a single record.
entity_typeNoEntity type to search. Omit for cross-entity search (useful when entity type is unknown). Not applied when `query` is an identifier — an identifier determines its own entity type.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
budgetNoWhat this call cost against the OpenAlex daily budget and what is left of it. Read `remainingUsd` here to size the search or traversal this resolution feeds. Absent when OpenAlex omitted the accounting headers.
noticeNoGuidance notice. Set when nothing matched (echoes the query and suggests corrections) or when an identifier query was passed name-search parameters that do not apply to it. Absent otherwise.
resultsNoAutocomplete matches, up to 10.

TDQS

A5/5.0
Behavior5/5

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

Beyond the annotations (readOnlyHint, openWorldHint, idempotentHint), the description discloses concrete behaviors: names return up to 10 autocomplete matches with disambiguation hints, identifiers resolve to exactly one record, and the PMCID edge case (recognized but resolves nothing). It even explains why (OpenAlex indexes no PMCIDs). This adds substantial value over the structured hints.

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?

Two sentences carry a wealth of information without padding. The most critical rule ('ALWAYS use this before filtering') is front-loaded, and the second sentence efficiently packs identifier types, the autocomplete limit, and the PMCID caveat. Every clause 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?

The description covers input types (names vs. identifiers), output behavior (autocomplete vs. single record), parameter applicability (filters, entity_type), and the one edge case an agent might trip on (PMCID). An output schema exists, so return format needs no explanation. Nothing an agent needs to invoke this correctly is missing.

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

Parameters5/5

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

While the schema already describes each parameter with 100% coverage, the description enriches them with interplay rules: filters apply only to name queries (an identifier already addresses one record), entity_type is not applied for identifiers, and query accepts a wide range of identifier formats (DOI, ORCID, ROR, PMID, ISSN, keyword URLs) not fully enumerated in the schema's description. This goes well beyond a baseline of 3.

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 opens with the exact action: 'Resolve a name or an identifier to an OpenAlex ID.' It immediately distinguishes this from searching by stating 'ALWAYS use this before filtering by entity' and contrasts names (ambiguous, return autocomplete matches) with identifiers (resolve directly). This clearly differentiates it from sibling tools like search_entities.

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?

The description explicitly states 'ALWAYS use this before filtering by entity — names are ambiguous, IDs are not,' giving a strong when-to-use directive. It also clarifies that identifiers need no entity_type and warns that a PMCID resolves nothing, directing the user to pass the PMID or DOI instead. This is explicit, actionable guidance with no ambiguity.

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

openalex_search_entitiesOpenalex Search EntitiesA
Read-onlyIdempotent
Inspect

Search, filter, sort, or retrieve by ID. Covers all OpenAlex entity types (works, authors, sources, institutions, topics, keywords, publishers, funders). Pass id to retrieve a single entity. Otherwise, use query and/or filters for discovery. Supports keyword search with boolean operators, exact phrase matching, and AI semantic search. Use openalex_resolve_name to resolve names to IDs before filtering. Searches and ID lookups return a curated set of fields by default; pass select to override with specific fields, or ["*"] for the full record.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoRetrieve a single entity by ID. Supports: OpenAlex ID ("W2741809807"), DOI ("10.1038/nature12373"), ORCID ("0000-0002-1825-0097"), ROR ("https://ror.org/00hx57361"), PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"), ISSN ("1234-5678"). Keywords are identified by slug rather than a native ID — pass either the slug ("groundwater") or the URL a search returns ("https://openalex.org/keywords/groundwater"). A PMCID is recognized too, bare ("PMC1234567") or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `["*"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown.
pageNoPage number (1-based) for semantic search, the one mode that paginates with `page` instead of `cursor`. Semantic search ranks a query-dependent candidate set whose size `meta.count` reports, so the last reachable page is ceil(meta.count / per_page) — e.g. page 14 for a count of 70 with per_page=5. Passing it under any other search_mode is rejected.
seedNoDeterministic seed for `sample`. Same seed + same filters = same results — pass when reproducibility matters. Has no effect without `sample`, and a search that passes it alone is rejected.
sortNoSort field. Prefix with "-" for descending. Comma-separate for a multi-key sort, applied left to right, with the "-" prefix set per key ("-publication_year,cited_by_count" sorts by year descending, then citations ascending). Common: "cited_by_count", "-publication_date", "-relevance_score" (default when query present). Note: when combined with a keyword query, an explicit sort overrides relevance ranking entirely — top results may be highly cited but only tangentially on-topic. Use "-relevance_score" or omit sort to keep the most relevant results first. "-relevance_score" requires an active search via "query" or a "filter:search" filter — passing it without one will fail. Not combinable with `sample` — a search passing both is rejected.
queryNoText search query. Supports boolean operators (AND, OR, NOT), quoted phrases ("exact match"), wildcards (machin*), fuzzy matching (machin~1), and proximity ("climate change"~5). Omit for filter-only queries — an empty string is rejected, since a blank search is a mistake rather than a request for the whole catalog.
cursorNoPagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for page 1. Keyword and exact modes only — semantic search walks its candidates with `page`, and a `cursor` sent with it is rejected.
sampleNoReturn a random sample of this many entities matching the filters (1-100). Single page only — neither `cursor` nor `page` pagination applies to sampling, and a search that passes either alongside it is rejected. Keyword and exact modes only: OpenAlex does not sample a semantic search, so `sample` with search_mode "semantic" is rejected. Cannot be combined with `sort` — a sample has no order, and a search passing both is rejected. Overrides `per_page`. Useful for unbiased exploration: spot-checking filter correctness, stratified review prompts, or generating exploration sets without bias toward most-cited.
selectNoOpenAlex top-level field names to return. Always returned: `id`, `display_name` — additional fields you list are appended. A curated default per entity type applies to both searches and single-entity (`id`) lookups; pass field names to override it, or `["*"]` to retrieve the complete record (every field). Only top-level fields project, so a nested value is requested by its parent object: bibliometrics (`h_index`, `i10_index`, `2yr_mean_citedness`) live under `summary_stats` on authors, sources, institutions, publishers, and funders, and naming a leaf returns that object. Invalid field names produce an error identifying the rejected field. Example: ["doi", "authorships", "primary_topic"].
filtersNoFilter criteria as field:value pairs. AND across fields (multiple keys). OR within field: pipe-separate ("us|gb"). NOT: prefix "!" ("!us"). Range: "2020-2024". Comparison: ">100", "<50". AND within same field: "+"-separate. Two keys that resolve to the same upstream field (an alias and its canonical name, e.g. `year` and `publication_year`) are both applied and AND'd, so they narrow rather than override each other. Use OpenAlex IDs (not names) for entity filters — resolve names first. Common keys: `openalex` (filter by entity ID, e.g. {"openalex": "W123|W456"}), `cites` (works citing a given work), `publication_year` (range "2020-2024"), `authorships.author.id`, `type`, `is_oa`.
per_pageNoResults per page (1-100). Default 25. Semantic search caps at 50 — when search_mode="semantic", set per_page ≤ 50 (also subject to a 1 req/sec rate limit upstream). The cap applies to searches only; an `id` lookup returns its one record regardless of both.
entity_typeYesType of scholarly entity to search.
search_modeNoSearch strategy. "keyword": stemmed full-text (default). "exact": no stemming, matches individual words (use quoted phrases for multi-word exact match). "semantic": AI embedding similarity over a query-dependent candidate set whose size `meta.count` reports, at ~1 req/sec, up to 50 per page, and paginated with `page` rather than `cursor`.keyword

Output Schema

ParametersJSON Schema
NameRequiredDescription
echoNoCompact echo of the criteria that actually ran (entity_type, query, filters, sort, search_mode) — surfaces what was searched when results are empty. An `id` lookup echoes entity_type and id alone, because the search criteria are not applied on that path.
metaNoResult metadata including pagination.
errorNoPresent when the call failed. Absent on success.
budgetNoWhat this call cost against the OpenAlex daily budget and what is left of it. Price a full traversal before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.
noticeNoGuidance notice. Set when a first call returns no results (echoes the criteria and suggests how to broaden), when a paginated call ran past its last page (says the traversal is finished instead of advising a broader query), when an `id` lookup was passed search criteria it does not apply (names them), and on every semantic search to disclose that `meta.count` is a candidate total rather than a match total. Absent otherwise.
resultsNoOpenAlex entity objects. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — and an abstract arrives reconstructed as `abstract`. Additional fields depend on entity_type and select.
totalCountNoTotal results matching the query/filters across all pages.

TDQS

A4.1/5.0
Behavior5/5

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

The description adds substantial behavioral detail beyond the readOnlyHint/openWorldHint/idempotentHint annotations: keyword vs exact vs semantic search, boolean operators, exact phrase matching, default curated field sets, `select` override behavior, and the fact that `id` overrides query/filters/sort. None of this 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?

The description is five sentences, front-loaded with the primary purpose and scoped to entity types. It contains no filler and reads well as an overview, though some statements (e.g., curated fields/`select` behavior) are redundant with the schema. Still tight for a tool with 12 parameters.

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 the very rich input schema, detailed parameter descriptions, and the presence of an output schema, the description provides an adequate top-level map: search, filter, sort, ID retrieval, and name resolution. Details like pagination, sampling, and rate limits are left to the schema, which already covers them, so the description is complete enough for orientation.

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 each parameter is extensively documented in the schema. The description itself adds only a high-level mention of id-vs-query routing, resolve_name usage, and curated fields—most of which is repeated from the schema. Baseline 3 is appropriate because the schema carries the parameter-level meaning.

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 opens with a specific action ('Search, filter, sort, or retrieve by ID') and clearly scopes it to all OpenAlex entity types, even listing them. It also points to openalex_resolve_name for name resolution, which distinguishes this search tool from that sibling. However, it does not explicitly contrast with the analytical siblings (analyze_trends, describe_fields, get_citation_graph), so sibling differentiation is only partial.

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 description gives clear when-to-use guidance: pass `id` for single-entity retrieval, otherwise use `query` and/or `filters` for discovery, and use openalex_resolve_name to resolve names before filtering. It also mentions the three search modes at a high level. It does not explicitly say when to prefer this tool over the analytical siblings, but the main usage paths are unambiguous.

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. 5 tool updates
    • Changedopenalex_analyze_trends6 fields changed
      • changedInput schema / properties / group_by / description
        Previous value: -"Field to group by. Works examples: \"publication_year\", \"type\", \"oa_status\", \"primary_topic.field.id\", \"authorships.institutions.country_code\", \"is_retracted\". Authors: \"last_known_institutions.country_code\", \"has_orcid\". Sources: \"type\", \"is_oa\", \"country_code\". Not all fields support group_by — check entity docs if unsure."New value: +"Field to group by. Works examples: \"publication_year\", \"type\", \"oa_status\", \"primary_topic.field.id\", \"authorships.institutions.country_code\", \"is_retracted\". Authors: \"last_known_institutions.country_code\", \"has_orcid\". Sources: \"type\", \"is_oa\", \"country_code\". Not all fields support group_by — call openalex_describe_fields(entity_type, \"group_by\") for the groupable set."
      • changedInput schema / properties / include_unknown / description
        Previous value: -"Include a group for entities with no value for the grouped field. Hidden by default."New value: +"Add a group for entities with no value for the grouped field. Hidden by default. That group carries `is_unknown: true`; OpenAlex keys it -111 or -111.0 on numeric fields, \"unknown\" on text fields and under order \"key\", and an ID ending in /unknown on ID fields — a sentinel, not a measured value. The key is not a filter value: passing -111 as a filter matches a numeric range, not the entities with no value. Boolean fields have no separate group — a missing value counts as false."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid group_by or filter field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_ungroupable_group_by`: group_by targets a raw date, float, or *.search field OpenAlex cannot aggregate (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). `upstream_missing_group_by`: OpenAlex answered with its plain list shape, carrying no aggregation for the requested group_by. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid group_by or filter field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_ungroupable_group_by`: group_by targets a field OpenAlex cannot aggregate — a raw date, a decimal score, a *.search operator, a field such as display_name, doi, or referenced_works, or a concept key on authors, which OpenAlex reports as an invalid OpenAlex ID (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). `upstream_missing_group_by`: OpenAlex answered with its plain list shape, carrying no aggregation for the requested group_by. Other values are possible when a failure originates below the handler."
      • addedOutput schema / properties / groups / items / properties / is_unknown
        Added value: +{
        +  "const": true,
        +  "description": "Present only on the group include_unknown adds for entities with no value; its key is an OpenAlex sentinel (-111, -111.0, unknown, or an ID ending in /unknown), not a measured value.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / groups / items / properties / key / description
        Previous value: -"Group key (OpenAlex ID or raw value)."New value: +"Group key (OpenAlex ID or raw value), exactly as OpenAlex returns it."
      • changedOutput schema / properties / groups / items / properties / key_display_name / description
        Previous value: -"Human-readable group label."New value: +"Human-readable group label as plain text, with HTML entities decoded and markup removed."
    • Changedopenalex_describe_fields1 field changed
      • changedInput schema / properties / context / description
        Previous value: -"Field usage context. \"filter\": fields accepted in the filter param. \"group_by\": fields accepted in group_by — a subset of the filter set (raw date and *.search fields are excluded; they cannot be grouped). \"select\": fields accepted in select."New value: +"Field usage context. \"filter\": fields accepted in the filter param. \"group_by\": fields accepted in group_by — a subset of the filter set that leaves out what OpenAlex refuses to aggregate (raw dates, *.search operators, decimal scores, display_name, and external-ID fields among them). \"select\": fields accepted in select."
    • Changedopenalex_get_citation_graph1 field changed
      • changedOutput schema / properties / results / description
        Previous value: -"Works on the citation graph in this direction."New value: +"Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed."
    • Changedopenalex_resolve_name4 fields changed
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the autocomplete request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to autocomplete (HTTP 403). `comma_in_filter_value`: A `filters` value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter field name on the autocomplete query (HTTP 400). `upstream_invalid_id_value`: A `filters` entry expecting an entity ID received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the autocomplete request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the autocomplete request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the autocomplete request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to autocomplete (HTTP 403). `comma_in_filter_value`: A `filters` value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter field name on the autocomplete query (HTTP 400). `upstream_invalid_id_value`: A `filters` entry expecting an entity ID received a value that is not an OpenAlex ID — usually a name (HTTP 400). `query_too_long`: OpenAlex autocomplete failed (HTTP 500) on a `query` longer than the 1,000 characters it accepts when entity_type is set. `upstream_invalid_params_other`: OpenAlex rejected the autocomplete request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the autocomplete request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "rate_limited",
        -  "upstream_budget_exhausted",
        -  "upstream_timeout",
        -  "upstream_unavailable",
        -  "upstream_unauthorized",
        -  "upstream_forbidden",
        -  "comma_in_filter_value",
        -  "upstream_invalid_params",
        -  "upstream_invalid_id_value",
        -  "upstream_invalid_params_other",
        -  "upstream_validation_failed"
        -]New value: +[
        +  "rate_limited",
        +  "upstream_budget_exhausted",
        +  "upstream_timeout",
        +  "upstream_unavailable",
        +  "upstream_unauthorized",
        +  "upstream_forbidden",
        +  "comma_in_filter_value",
        +  "upstream_invalid_params",
        +  "upstream_invalid_id_value",
        +  "query_too_long",
        +  "upstream_invalid_params_other",
        +  "upstream_validation_failed"
        +]
      • changedOutput schema / properties / results / items / properties / display_name / description
        Previous value: -"Human-readable name. null only for an identifier lookup that landed on a record OpenAlex holds no title for (paratext works and other untitled entries) — use `id` to identify it."New value: +"Human-readable name as plain text, with HTML entities decoded and markup removed. null only for an identifier lookup that landed on a record OpenAlex holds no title for (paratext works and other untitled entries) — use `id` to identify it."
      • changedOutput schema / properties / results / items / properties / hint / description
        Previous value: -"Disambiguation context — last institution (authors), host organization (sources), place or country (institutions); author names (works) from a name search, publication year from an identifier lookup. null when the record carries none."New value: +"Disambiguation context as plain text — last institution (authors), host organization (sources), place or country (institutions); author names (works) from a name search, publication year from an identifier lookup. null when the record carries none."
    • Changedopenalex_search_entities9 fields changed
      • changedInput schema / properties / page / description
        Previous value: -"Page number (1-based) for semantic search, the one mode that paginates with `page` instead of `cursor`. Semantic search ranks at most 50 candidates, so the last reachable page is ceil(50 / per_page) — e.g. page 17 with per_page=3. Passing it under any other search_mode is rejected."New value: +"Page number (1-based) for semantic search, the one mode that paginates with `page` instead of `cursor`. Semantic search ranks a query-dependent candidate set whose size `meta.count` reports, so the last reachable page is ceil(meta.count / per_page) — e.g. page 14 for a count of 70 with per_page=5. Passing it under any other search_mode is rejected."
      • changedInput schema / properties / sample / description
        Previous value: -"Return a random sample of this many entities matching the filters (1-100). Single page only — neither `cursor` nor `page` pagination applies to sampling, and a search that passes either alongside it is rejected. Overrides `per_page`. Useful for unbiased exploration: spot-checking filter correctness, stratified review prompts, or generating exploration sets without bias toward most-cited."New value: +"Return a random sample of this many entities matching the filters (1-100). Single page only — neither `cursor` nor `page` pagination applies to sampling, and a search that passes either alongside it is rejected. Keyword and exact modes only: OpenAlex does not sample a semantic search, so `sample` with search_mode \"semantic\" is rejected. Cannot be combined with `sort` — a sample has no order, and a search passing both is rejected. Overrides `per_page`. Useful for unbiased exploration: spot-checking filter correctness, stratified review prompts, or generating exploration sets without bias toward most-cited."
      • changedInput schema / properties / search_mode / description
        Previous value: -"Search strategy. \"keyword\": stemmed full-text (default). \"exact\": no stemming, matches individual words (use quoted phrases for multi-word exact match). \"semantic\": AI embedding similarity, ranking at most 50 candidates at ~1 req/sec and paginated with `page` rather than `cursor`."New value: +"Search strategy. \"keyword\": stemmed full-text (default). \"exact\": no stemming, matches individual words (use quoted phrases for multi-word exact match). \"semantic\": AI embedding similarity over a query-dependent candidate set whose size `meta.count` reports, at ~1 req/sec, up to 50 per page, and paginated with `page` rather than `cursor`."
      • changedInput schema / properties / sort / description
        Previous value: -"Sort field. Prefix with \"-\" for descending. Comma-separate for a multi-key sort, applied left to right, with the \"-\" prefix set per key (\"-publication_year,cited_by_count\" sorts by year descending, then citations ascending). Common: \"cited_by_count\", \"-publication_date\", \"-relevance_score\" (default when query present). Note: when combined with a keyword query, an explicit sort overrides relevance ranking entirely — top results may be highly cited but only tangentially on-topic. Use \"-relevance_score\" or omit sort to keep the most relevant results first. \"-relevance_score\" requires an active search via \"query\" or a \"filter:search\" filter — passing it without one will fail."New value: +"Sort field. Prefix with \"-\" for descending. Comma-separate for a multi-key sort, applied left to right, with the \"-\" prefix set per key (\"-publication_year,cited_by_count\" sorts by year descending, then citations ascending). Common: \"cited_by_count\", \"-publication_date\", \"-relevance_score\" (default when query present). Note: when combined with a keyword query, an explicit sort overrides relevance ranking entirely — top results may be highly cited but only tangentially on-topic. Use \"-relevance_score\" or omit sort to keep the most relevant results first. \"-relevance_score\" requires an active search via \"query\" or a \"filter:search\" filter — passing it without one will fail. Not combinable with `sample` — a search passing both is rejected."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `semantic_per_page_cap`: A search (no `id`) set per_page above the semantic-search cap of 50. `semantic_without_query`: A search set search_mode to \"semantic\" without supplying the `query` it embeds. `semantic_with_cursor`: A search set search_mode to \"semantic\" and supplied `cursor`, which OpenAlex rejects on a semantic query. `page_without_semantic`: A search supplied `page` under a search_mode other than \"semantic\". `sample_with_cursor`: A search (no `id`) provided both `sample` and `cursor`. `sample_with_page`: A search (no `id`) provided both `sample` and `page`. `seed_without_sample`: A search (no `id`) provided `seed` without `sample`. `entity_not_found`: Lookup by id matched no OpenAlex entity. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used without an active search (HTTP 400). `query_too_long`: OpenAlex rejected `query` as longer than the search length it accepts (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `semantic_per_page_cap`: A search (no `id`) set per_page above the semantic-search cap of 50. `semantic_without_query`: A search set search_mode to \"semantic\" without supplying the `query` it embeds. `semantic_with_cursor`: A search set search_mode to \"semantic\" and supplied `cursor`, which OpenAlex rejects on a semantic query. `page_without_semantic`: A search supplied `page` under a search_mode other than \"semantic\". `sample_with_cursor`: A search (no `id`) provided both `sample` and `cursor`. `sample_with_page`: A search (no `id`) provided both `sample` and `page`. `sample_with_semantic`: A search (no `id`) provided `sample` with search_mode \"semantic\", which OpenAlex does not sample — it returns the same ranked candidates under every seed. `sample_with_sort`: A search (no `id`) provided both `sample` and `sort`, which OpenAlex refuses together. `seed_without_sample`: A search (no `id`) provided `seed` without `sample`. `entity_not_found`: Lookup by id matched no OpenAlex entity. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used without an active search (HTTP 400). `query_too_long`: OpenAlex rejected `query` as longer than the search length it accepts (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "semantic_per_page_cap",
        -  "semantic_without_query",
        -  "semantic_with_cursor",
        -  "page_without_semantic",
        -  "sample_with_cursor",
        -  "sample_with_page",
        -  "seed_without_sample",
        -  "entity_not_found",
        -  "rate_limited",
        -  "upstream_budget_exhausted",
        -  "upstream_timeout",
        -  "upstream_unavailable",
        -  "upstream_unauthorized",
        -  "upstream_forbidden",
        -  "comma_in_filter_value",
        -  "upstream_invalid_params",
        -  "upstream_invalid_id_value",
        -  "upstream_sort_requires_search",
        -  "query_too_long",
        -  "upstream_invalid_params_other",
        -  "upstream_validation_failed"
        -]New value: +[
        +  "semantic_per_page_cap",
        +  "semantic_without_query",
        +  "semantic_with_cursor",
        +  "page_without_semantic",
        +  "sample_with_cursor",
        +  "sample_with_page",
        +  "sample_with_semantic",
        +  "sample_with_sort",
        +  "seed_without_sample",
        +  "entity_not_found",
        +  "rate_limited",
        +  "upstream_budget_exhausted",
        +  "upstream_timeout",
        +  "upstream_unavailable",
        +  "upstream_unauthorized",
        +  "upstream_forbidden",
        +  "comma_in_filter_value",
        +  "upstream_invalid_params",
        +  "upstream_invalid_id_value",
        +  "upstream_sort_requires_search",
        +  "query_too_long",
        +  "upstream_invalid_params_other",
        +  "upstream_validation_failed"
        +]
      • changedOutput schema / properties / meta / properties / count / description
        Previous value: -"Total results matching the query/filters. Under search_mode \"semantic\" it is instead the capped candidate count — at most 50 — not an exhaustive match total."New value: +"Total results matching the query/filters. Under search_mode \"semantic\" it is instead the size of the ranked candidate set — the most results `page` can reach — not an exhaustive match total."
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance notice. Set when a first call returns no results (echoes the criteria and suggests how to broaden), when a paginated call ran past its last page (says the traversal is finished instead of advising a broader query), when an `id` lookup was passed search criteria it does not apply (names them), and on every semantic search to disclose that `meta.count` is a capped candidate total. Absent otherwise."New value: +"Guidance notice. Set when a first call returns no results (echoes the criteria and suggests how to broaden), when a paginated call ran past its last page (says the traversal is finished instead of advising a broader query), when an `id` lookup was passed search criteria it does not apply (names them), and on every semantic search to disclose that `meta.count` is a candidate total rather than a match total. Absent otherwise."
      • changedOutput schema / properties / results / description
        Previous value: -"OpenAlex entity objects passed through unchanged. Additional fields depend on entity_type and select."New value: +"OpenAlex entity objects. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed — and an abstract arrives reconstructed as `abstract`. Additional fields depend on entity_type and select."
  2. 4 tool updates
    • Changedopenalex_analyze_trends6 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Pagination cursor from a previous response. Only relevant when order is \"key\" — count-descending results have no next page. Pass the next_cursor from the previous response to advance."New value: +"Pagination cursor from a previous response. Only relevant when order is \"key\" — count-descending results have no next page. Pass the next_cursor from the previous response to advance. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page."
      • addedInput schema / properties / cursor / minLength
        Added value: +1
      • addedInput schema / properties / group_by / minLength
        Added value: +1
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid group_by or filter field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_ungroupable_group_by`: group_by targets a raw date, float, or *.search field OpenAlex cannot aggregate (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid group_by or filter field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_ungroupable_group_by`: group_by targets a raw date, float, or *.search field OpenAlex cannot aggregate (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). `upstream_missing_group_by`: OpenAlex answered with its plain list shape, carrying no aggregation for the requested group_by. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "rate_limited",
        -  "upstream_budget_exhausted",
        -  "upstream_timeout",
        -  "upstream_unavailable",
        -  "upstream_unauthorized",
        -  "upstream_forbidden",
        -  "comma_in_filter_value",
        -  "upstream_invalid_params",
        -  "upstream_invalid_id_value",
        -  "upstream_ungroupable_group_by",
        -  "upstream_invalid_params_other",
        -  "upstream_validation_failed"
        -]New value: +[
        +  "rate_limited",
        +  "upstream_budget_exhausted",
        +  "upstream_timeout",
        +  "upstream_unavailable",
        +  "upstream_unauthorized",
        +  "upstream_forbidden",
        +  "comma_in_filter_value",
        +  "upstream_invalid_params",
        +  "upstream_invalid_id_value",
        +  "upstream_ungroupable_group_by",
        +  "upstream_invalid_params_other",
        +  "upstream_validation_failed",
        +  "upstream_missing_group_by"
        +]
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance notice. Set when no groups are returned (recovery suggestions) or when the page is full and more groups likely exist (truncation signal with narrowing advice). Absent otherwise."New value: +"Guidance notice. Set when a first call returns no groups (recovery suggestions), when a `cursor` continuation returns none because the traversal is already finished, or when the page is full and more groups likely exist (truncation signal with narrowing advice). Absent otherwise."
    • Changedopenalex_get_citation_graph6 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Pagination cursor from a previous response. Pass to get the next page."New value: +"Pagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page."
      • addedInput schema / properties / cursor / minLength
        Added value: +1
      • changedInput schema / properties / filters / description
        Previous value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to — those are set by the `direction` parameter."New value: +"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / meta / properties / per_page / description
        Previous value: -"Records on this page."New value: +"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this."
      • changedOutput schema / properties / notice / description
        Previous value: -"Recovery guidance when no edges are returned — suggests verifying the seed_id, broadening filters, or trying a different direction. Absent when results are present."New value: +"Guidance when no edges are returned. A first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead. Absent when results are present."
    • Changedopenalex_resolve_name1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search. A PMCID (\"PMC1234567\" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead."New value: +"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search. A keyword URL (\"https://openalex.org/keywords/groundwater\") resolves the same way; a bare keyword slug reads as a name and runs a name search, which finds it too. A PMCID (\"PMC1234567\" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead."
    • Changedopenalex_search_entities15 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Pagination cursor from a previous response. Pass to get the next page."New value: +"Pagination cursor from a previous response. Pass to get the next page. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for page 1. Keyword and exact modes only — semantic search walks its candidates with `page`, and a `cursor` sent with it is rejected."
      • addedInput schema / properties / cursor / minLength
        Added value: +1
      • changedInput schema / properties / filters / description
        Previous value: -"Filter criteria as field:value pairs. AND across fields (multiple keys). OR within field: pipe-separate (\"us|gb\"). NOT: prefix \"!\" (\"!us\"). Range: \"2020-2024\". Comparison: \">100\", \"<50\". AND within same field: \"+\"-separate. Use OpenAlex IDs (not names) for entity filters — resolve names first. Common keys: `openalex` (filter by entity ID, e.g. {\"openalex\": \"W123|W456\"}), `cites` (works citing a given work), `publication_year` (range \"2020-2024\"), `authorships.author.id`, `type`, `is_oa`."New value: +"Filter criteria as field:value pairs. AND across fields (multiple keys). OR within field: pipe-separate (\"us|gb\"). NOT: prefix \"!\" (\"!us\"). Range: \"2020-2024\". Comparison: \">100\", \"<50\". AND within same field: \"+\"-separate. Two keys that resolve to the same upstream field (an alias and its canonical name, e.g. `year` and `publication_year`) are both applied and AND'd, so they narrow rather than override each other. Use OpenAlex IDs (not names) for entity filters — resolve names first. Common keys: `openalex` (filter by entity ID, e.g. {\"openalex\": \"W123|W456\"}), `cites` (works citing a given work), `publication_year` (range \"2020-2024\"), `authorships.author.id`, `type`, `is_oa`."
      • changedInput schema / properties / id / description
        Previous value: -"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\"). A PMCID is recognized too, bare (\"PMC1234567\") or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."New value: +"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\"). Keywords are identified by slug rather than a native ID — pass either the slug (\"groundwater\") or the URL a search returns (\"https://openalex.org/keywords/groundwater\"). A PMCID is recognized too, bare (\"PMC1234567\") or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."
      • addedInput schema / properties / id / minLength
        Added value: +1
      • addedInput schema / properties / page
        Added value: +{
        +  "description": "Page number (1-based) for semantic search, the one mode that paginates with `page` instead of `cursor`. Semantic search ranks at most 50 candidates, so the last reachable page is ceil(50 / per_page) — e.g. page 17 with per_page=3. Passing it under any other search_mode is rejected.",
        +  "maximum": 9007199254740991,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • changedInput schema / properties / query / description
        Previous value: -"Text search query. Supports boolean operators (AND, OR, NOT), quoted phrases (\"exact match\"), wildcards (machin*), fuzzy matching (machin~1), and proximity (\"climate change\"~5). Omit for filter-only queries."New value: +"Text search query. Supports boolean operators (AND, OR, NOT), quoted phrases (\"exact match\"), wildcards (machin*), fuzzy matching (machin~1), and proximity (\"climate change\"~5). Omit for filter-only queries — an empty string is rejected, since a blank search is a mistake rather than a request for the whole catalog."
      • addedInput schema / properties / query / minLength
        Added value: +1
      • changedInput schema / properties / sample / description
        Previous value: -"Return a random sample of this many entities matching the filters (1-100). Single page only — pagination via `cursor` is not supported with sampling. Overrides `per_page`. Useful for unbiased exploration: spot-checking filter correctness, stratified review prompts, or generating exploration sets without bias toward most-cited."New value: +"Return a random sample of this many entities matching the filters (1-100). Single page only — neither `cursor` nor `page` pagination applies to sampling, and a search that passes either alongside it is rejected. Overrides `per_page`. Useful for unbiased exploration: spot-checking filter correctness, stratified review prompts, or generating exploration sets without bias toward most-cited."
      • changedInput schema / properties / search_mode / description
        Previous value: -"Search strategy. \"keyword\": stemmed full-text (default). \"exact\": no stemming, matches individual words (use quoted phrases for multi-word exact match). \"semantic\": AI embedding similarity (max 50 results, 1 req/sec)."New value: +"Search strategy. \"keyword\": stemmed full-text (default). \"exact\": no stemming, matches individual words (use quoted phrases for multi-word exact match). \"semantic\": AI embedding similarity, ranking at most 50 candidates at ~1 req/sec and paginated with `page` rather than `cursor`."
      • changedOutput schema / properties / error / properties / data / properties / reason / description
        Previous value: -"Machine-readable failure mode. Declared by this tool: `semantic_per_page_cap`: A search (no `id`) set per_page above the semantic-search cap of 50. `sample_with_cursor`: A search (no `id`) provided both `sample` and `cursor`. `seed_without_sample`: A search (no `id`) provided `seed` without `sample`. `entity_not_found`: Lookup by id matched no OpenAlex entity. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used without an active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `semantic_per_page_cap`: A search (no `id`) set per_page above the semantic-search cap of 50. `semantic_without_query`: A search set search_mode to \"semantic\" without supplying the `query` it embeds. `semantic_with_cursor`: A search set search_mode to \"semantic\" and supplied `cursor`, which OpenAlex rejects on a semantic query. `page_without_semantic`: A search supplied `page` under a search_mode other than \"semantic\". `sample_with_cursor`: A search (no `id`) provided both `sample` and `cursor`. `sample_with_page`: A search (no `id`) provided both `sample` and `page`. `seed_without_sample`: A search (no `id`) provided `seed` without `sample`. `entity_not_found`: Lookup by id matched no OpenAlex entity. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used without an active search (HTTP 400). `query_too_long`: OpenAlex rejected `query` as longer than the search length it accepts (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler."
      • changedOutput schema / properties / error / properties / data / properties / reason / examples
        Previous value: -[
        -  "semantic_per_page_cap",
        -  "sample_with_cursor",
        -  "seed_without_sample",
        -  "entity_not_found",
        -  "rate_limited",
        -  "upstream_budget_exhausted",
        -  "upstream_timeout",
        -  "upstream_unavailable",
        -  "upstream_unauthorized",
        -  "upstream_forbidden",
        -  "comma_in_filter_value",
        -  "upstream_invalid_params",
        -  "upstream_invalid_id_value",
        -  "upstream_sort_requires_search",
        -  "upstream_invalid_params_other",
        -  "upstream_validation_failed"
        -]New value: +[
        +  "semantic_per_page_cap",
        +  "semantic_without_query",
        +  "semantic_with_cursor",
        +  "page_without_semantic",
        +  "sample_with_cursor",
        +  "sample_with_page",
        +  "seed_without_sample",
        +  "entity_not_found",
        +  "rate_limited",
        +  "upstream_budget_exhausted",
        +  "upstream_timeout",
        +  "upstream_unavailable",
        +  "upstream_unauthorized",
        +  "upstream_forbidden",
        +  "comma_in_filter_value",
        +  "upstream_invalid_params",
        +  "upstream_invalid_id_value",
        +  "upstream_sort_requires_search",
        +  "query_too_long",
        +  "upstream_invalid_params_other",
        +  "upstream_validation_failed"
        +]
      • changedOutput schema / properties / meta / properties / count / description
        Previous value: -"Total results matching the query/filters."New value: +"Total results matching the query/filters. Under search_mode \"semantic\" it is instead the capped candidate count — at most 50 — not an exhaustive match total."
      • changedOutput schema / properties / meta / properties / per_page / description
        Previous value: -"Results on this page."New value: +"Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this."
      • changedOutput schema / properties / notice / description
        Previous value: -"Guidance notice. Set when results are empty (echoes the criteria and suggests how to broaden) or when an `id` lookup was passed search criteria it does not apply (names them). Absent otherwise."New value: +"Guidance notice. Set when a first call returns no results (echoes the criteria and suggests how to broaden), when a paginated call ran past its last page (says the traversal is finished instead of advising a broader query), when an `id` lookup was passed search criteria it does not apply (names them), and on every semantic search to disclose that `meta.count` is a capped candidate total. Absent otherwise."
  3. 3 tool updates
    • Changedopenalex_get_citation_graph1 field changed
      • changedInput schema / properties / seed_id / description
        Previous value: -"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), or PMCID. Use openalex_resolve_name first if you only have a title."New value: +"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), or PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"). A PMCID is recognized too, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. Use openalex_resolve_name first if you only have a title."
    • Changedopenalex_resolve_name1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search."New value: +"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search. A PMCID (\"PMC1234567\" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead."
    • Changedopenalex_search_entities1 field changed
      • changedInput schema / properties / id / description
        Previous value: -"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\"). When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."New value: +"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), ISSN (\"1234-5678\"). A PMCID is recognized too, bare (\"PMC1234567\") or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead. When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."
  4. 4 tool updates
    • Changedopenalex_describe_fields2 fields changed
      • changedInput schema / properties / query / description
        Previous value: -"Optional partial or guessed field name to rank results by similarity. Pass the field you tried (e.g. \"funder\") to get the closest matches first. Omit to return all fields for the entity_type + context."New value: +"Optional partial or guessed field name to sort results by similarity. Pass the field you tried (e.g. \"funder\") to get the closest matches first. The complete field list is returned either way — a query reorders it, it does not filter it, so a nested value's parent object (e.g. `summary_stats` for \"h_index\") is still reachable further down."
      • changedOutput schema / properties / fields / description
        Previous value: -"Valid field names, ranked by similarity to query when provided."New value: +"Every valid field name for this entity_type + context — the complete pool, ranked by similarity when `query` is provided. Never truncated, so this always holds `total` entries."
    • Changedopenalex_get_citation_graph1 field changed
      • changedInput schema / properties / seed_id / description
        Previous value: -"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID, or PMCID. Use openalex_resolve_name first if you only have a title."New value: +"Seed work identifier. Accepts OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\" or full URL), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), or PMCID. Use openalex_resolve_name first if you only have a title."
    • Changedopenalex_resolve_name1 field changed
      • changedInput schema / properties / query / description
        Previous value: -"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search."New value: +"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search."
    • Changedopenalex_search_entities2 fields changed
      • changedInput schema / properties / id / description
        Previous value: -"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\"). When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."New value: +"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\" or \"https://pubmed.ncbi.nlm.nih.gov/12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\"). When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."
      • changedInput schema / properties / select / description
        Previous value: -"OpenAlex top-level field names to return. Always returned: `id`, `display_name` — additional fields you list are appended. A curated default per entity type applies to both searches and single-entity (`id`) lookups; pass field names to override it, or `[\"*\"]` to retrieve the complete record (every field). Invalid field names produce an error identifying the rejected field. Example: [\"doi\", \"authorships\", \"primary_topic\"]."New value: +"OpenAlex top-level field names to return. Always returned: `id`, `display_name` — additional fields you list are appended. A curated default per entity type applies to both searches and single-entity (`id`) lookups; pass field names to override it, or `[\"*\"]` to retrieve the complete record (every field). Only top-level fields project, so a nested value is requested by its parent object: bibliometrics (`h_index`, `i10_index`, `2yr_mean_citedness`) live under `summary_stats` on authors, sources, institutions, publishers, and funders, and naming a leaf returns that object. Invalid field names produce an error identifying the rejected field. Example: [\"doi\", \"authorships\", \"primary_topic\"]."
  5. 4 tool updates
    • Changedopenalex_analyze_trends4 fields changed
      • removedOutput schema / properties / meta / properties / groups_count / anyOf
        Removed value: -[
        -  {
        -    "type": "number"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / meta / properties / groups_count / type
        Added value: +[
        +  "number",
        +  "null"
        +]
      • removedOutput schema / properties / meta / properties / next_cursor / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / meta / properties / next_cursor / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedopenalex_get_citation_graph4 fields changed
      • removedOutput schema / properties / meta / properties / next_cursor / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / meta / properties / next_cursor / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / display_name / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / display_name / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedopenalex_resolve_name8 fields changed
      • removedOutput schema / properties / results / items / properties / display_name / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / display_name / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / external_id / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / external_id / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / hint / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / hint / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / works_count / anyOf
        Removed value: -[
        -  {
        -    "type": "number"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / works_count / type
        Added value: +[
        +  "number",
        +  "null"
        +]
    • Changedopenalex_search_entities4 fields changed
      • removedOutput schema / properties / meta / properties / next_cursor / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / meta / properties / next_cursor / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / results / items / properties / display_name / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / results / items / properties / display_name / type
        Added value: +[
        +  "string",
        +  "null"
        +]
  6. 5 tool updates
    • Changedopenalex_analyze_trends6 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": [
        +      "meta",
        +      "groups",
        +      "echo",
        +      "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: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid group_by or filter field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_ungroupable_group_by`: group_by targets a raw date, float, or *.search field OpenAlex cannot aggregate (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "rate_limited",
        +            "upstream_budget_exhausted",
        +            "upstream_timeout",
        +            "upstream_unavailable",
        +            "upstream_unauthorized",
        +            "upstream_forbidden",
        +            "comma_in_filter_value",
        +            "upstream_invalid_params",
        +            "upstream_invalid_id_value",
        +            "upstream_ungroupable_group_by",
        +            "upstream_invalid_params_other",
        +            "upstream_validation_failed"
        +          ],
        +          "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: -[
        -  "meta",
        -  "groups",
        -  "echo",
        -  "totalCount"
        -]
    • Changedopenalex_describe_fields6 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": [
        +      "entity_type",
        +      "context",
        +      "fields",
        +      "total"
        +    ]
        +  },
        +  {
        +    "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.",
        +          "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: -[
        -  "entity_type",
        -  "context",
        -  "fields",
        -  "total"
        -]
    • Changedopenalex_get_citation_graph6 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": [
        +      "meta",
        +      "results",
        +      "echo",
        +      "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: `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `reserved_filter_key`: filters contains cites/cited_by/related_to — the direction parameter reserves those keys. `entity_not_found`: OpenAlex has no work matching the seed_id. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "rate_limited",
        +            "upstream_budget_exhausted",
        +            "upstream_timeout",
        +            "upstream_unavailable",
        +            "upstream_unauthorized",
        +            "upstream_forbidden",
        +            "comma_in_filter_value",
        +            "upstream_invalid_params",
        +            "upstream_invalid_id_value",
        +            "upstream_sort_requires_search",
        +            "upstream_invalid_params_other",
        +            "reserved_filter_key",
        +            "entity_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: -[
        -  "meta",
        -  "results",
        -  "echo",
        -  "totalCount"
        -]
    • Changedopenalex_resolve_name6 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"
        +    ]
        +  },
        +  {
        +    "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: `rate_limited`: OpenAlex throttled the autocomplete request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to autocomplete (HTTP 403). `comma_in_filter_value`: A `filters` value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter field name on the autocomplete query (HTTP 400). `upstream_invalid_id_value`: A `filters` entry expecting an entity ID received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the autocomplete request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the autocomplete request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "rate_limited",
        +            "upstream_budget_exhausted",
        +            "upstream_timeout",
        +            "upstream_unavailable",
        +            "upstream_unauthorized",
        +            "upstream_forbidden",
        +            "comma_in_filter_value",
        +            "upstream_invalid_params",
        +            "upstream_invalid_id_value",
        +            "upstream_invalid_params_other",
        +            "upstream_validation_failed"
        +          ],
        +          "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"
        -]
    • Changedopenalex_search_entities6 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": [
        +      "meta",
        +      "results",
        +      "echo",
        +      "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: `semantic_per_page_cap`: A search (no `id`) set per_page above the semantic-search cap of 50. `sample_with_cursor`: A search (no `id`) provided both `sample` and `cursor`. `seed_without_sample`: A search (no `id`) provided `seed` without `sample`. `entity_not_found`: Lookup by id matched no OpenAlex entity. `rate_limited`: OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429). `upstream_budget_exhausted`: The OpenAlex daily usage budget is spent (HTTP 429). `upstream_timeout`: OpenAlex did not respond within the request deadline. `upstream_unavailable`: OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON. `upstream_unauthorized`: OpenAlex rejected the API key (HTTP 401). `upstream_forbidden`: OpenAlex denied access to the requested resource (HTTP 403). `comma_in_filter_value`: A filter value contains a comma, which collides with the OpenAlex filter separator. `upstream_invalid_params`: OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400). `upstream_invalid_id_value`: An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400). `upstream_sort_requires_search`: sort=-relevance_score was used without an active search (HTTP 400). `upstream_invalid_params_other`: OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name. `upstream_validation_failed`: OpenAlex rejected the request as semantically invalid (HTTP 422). Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "semantic_per_page_cap",
        +            "sample_with_cursor",
        +            "seed_without_sample",
        +            "entity_not_found",
        +            "rate_limited",
        +            "upstream_budget_exhausted",
        +            "upstream_timeout",
        +            "upstream_unavailable",
        +            "upstream_unauthorized",
        +            "upstream_forbidden",
        +            "comma_in_filter_value",
        +            "upstream_invalid_params",
        +            "upstream_invalid_id_value",
        +            "upstream_sort_requires_search",
        +            "upstream_invalid_params_other",
        +            "upstream_validation_failed"
        +          ],
        +          "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: -[
        -  "meta",
        -  "results",
        -  "echo",
        -  "totalCount"
        -]
  7. 4 tool updates
    • Changedopenalex_analyze_trends1 field changed
      • addedOutput schema / properties / budget / properties / prepaidRemainingUsd
        Added value: +{
        +  "description": "USD left in the prepaid balance — a separate pool OpenAlex draws on only after the daily allowance runs out, and which the daily reset does not refill. Add it to `remainingUsd` for the full spendable amount. Absent when the account holds no prepaid balance.",
        +  "type": "number"
        +}
    • Changedopenalex_get_citation_graph1 field changed
      • addedOutput schema / properties / budget / properties / prepaidRemainingUsd
        Added value: +{
        +  "description": "USD left in the prepaid balance — a separate pool OpenAlex draws on only after the daily allowance runs out, and which the daily reset does not refill. Add it to `remainingUsd` for the full spendable amount. Absent when the account holds no prepaid balance.",
        +  "type": "number"
        +}
    • Changedopenalex_resolve_name9 fields changed
      • changedInput schema / properties / entity_type / description
        Previous value: -"Entity type to search. Omit for cross-entity search (useful when entity type is unknown)."New value: +"Entity type to search. Omit for cross-entity search (useful when entity type is unknown). Not applied when `query` is an identifier — an identifier determines its own entity type."
      • changedInput schema / properties / filters / description
        Previous value: -"Narrow autocomplete results with filters. Example: restrict to a specific country or publication year range."New value: +"Narrow autocomplete results with filters. Example: restrict to a specific country or publication year range. Applies to name queries only — an identifier already addresses a single record."
      • changedInput schema / properties / query / description
        Previous value: -"Name or partial name to resolve. Also accepts DOIs for quick lookup."New value: +"Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID (\"W2741809807\", \"F4320332161\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\") — which resolves straight to that one record instead of running a name search."
      • addedOutput schema / properties / budget / properties / prepaidRemainingUsd
        Added value: +{
        +  "description": "USD left in the prepaid balance — a separate pool OpenAlex draws on only after the daily allowance runs out, and which the daily reset does not refill. Add it to `remainingUsd` for the full spendable amount. Absent when the account holds no prepaid balance.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Recovery guidance when no matches were found — echoes the query and suggests corrections. Absent when results are present."New value: +"Guidance notice. Set when nothing matched (echoes the query and suggests corrections) or when an identifier query was passed name-search parameters that do not apply to it. Absent otherwise."
      • addedOutput schema / properties / results / items / properties / display_name / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / results / items / properties / display_name / description
        Previous value: -"Human-readable name."New value: +"Human-readable name. null only for an identifier lookup that landed on a record OpenAlex holds no title for (paratext works and other untitled entries) — use `id` to identify it."
      • removedOutput schema / properties / results / items / properties / display_name / type
        Removed value: -"string"
      • changedOutput schema / properties / results / items / properties / hint / description
        Previous value: -"Disambiguation context: author names (works), last institution (authors), host org (sources), location (institutions)."New value: +"Disambiguation context — last institution (authors), host organization (sources), place or country (institutions); author names (works) from a name search, publication year from an identifier lookup. null when the record carries none."
    • Changedopenalex_search_entities6 fields changed
      • changedInput schema / properties / id / description
        Previous value: -"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\"). When provided, other search/filter/sort params are ignored — but `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). Use openalex_resolve_name to find the ID if unknown."New value: +"Retrieve a single entity by ID. Supports: OpenAlex ID (\"W2741809807\"), DOI (\"10.1038/nature12373\"), ORCID (\"0000-0002-1825-0097\"), ROR (\"https://ror.org/00hx57361\"), PMID (\"12345678\"), PMCID (\"PMC1234567\"), ISSN (\"1234-5678\"). When provided, `query`, `search_mode`, `filters`, `sort`, `sample`, and `seed` are not applied — the returned record is the entity at that ID regardless of them, and the response `notice` names any you passed. `select` still applies: the curated per-entity-type default is returned unless you pass `select` (use `[\"*\"]` for the complete record). To filter, drop `id` and search. Use openalex_resolve_name to find the ID if unknown."
      • changedInput schema / properties / per_page / description
        Previous value: -"Results per page (1-100). Default 25. Semantic search caps at 50 — when search_mode=\"semantic\", set per_page ≤ 50 (also subject to a 1 req/sec rate limit upstream)."New value: +"Results per page (1-100). Default 25. Semantic search caps at 50 — when search_mode=\"semantic\", set per_page ≤ 50 (also subject to a 1 req/sec rate limit upstream). The cap applies to searches only; an `id` lookup returns its one record regardless of both."
      • changedInput schema / properties / seed / description
        Previous value: -"Deterministic seed for `sample`. Same seed + same filters = same results — pass when reproducibility matters. Has no effect (and is rejected) without `sample`."New value: +"Deterministic seed for `sample`. Same seed + same filters = same results — pass when reproducibility matters. Has no effect without `sample`, and a search that passes it alone is rejected."
      • addedOutput schema / properties / budget / properties / prepaidRemainingUsd
        Added value: +{
        +  "description": "USD left in the prepaid balance — a separate pool OpenAlex draws on only after the daily allowance runs out, and which the daily reset does not refill. Add it to `remainingUsd` for the full spendable amount. Absent when the account holds no prepaid balance.",
        +  "type": "number"
        +}
      • changedOutput schema / properties / echo / description
        Previous value: -"Compact echo of the input criteria (entity_type, query, filters, sort, search_mode) — surfaces what was actually searched when results are empty."New value: +"Compact echo of the criteria that actually ran (entity_type, query, filters, sort, search_mode) — surfaces what was searched when results are empty. An `id` lookup echoes entity_type and id alone, because the search criteria are not applied on that path."
      • changedOutput schema / properties / notice / description
        Previous value: -"Recovery guidance when results are empty — echoes the criteria and suggests how to broaden. Absent on successful result pages."New value: +"Guidance notice. Set when results are empty (echoes the criteria and suggests how to broaden) or when an `id` lookup was passed search criteria it does not apply (names them). Absent otherwise."
  8. 4 tool updates
    • Changedopenalex_analyze_trends1 field changed
      • addedOutput schema / properties / budget
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "What this call cost against the OpenAlex daily budget and what is left of it — weigh `remainingUsd` against `costUsd` before enumerating every group with `order: \"key\"`. Absent when OpenAlex omitted the accounting headers.",
        +  "properties": {
        +    "costUsd": {
        +      "description": "USD this call spent. Aggregation is priced far below paging the same entities, so a group_by is the cheap way to size a population before searching it.",
        +      "type": "number"
        +    },
        +    "remainingUsd": {
        +      "description": "USD left in today's OpenAlex budget after this call.",
        +      "type": "number"
        +    },
        +    "resetsInSeconds": {
        +      "description": "Seconds until the daily budget refills (midnight UTC).",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "costUsd",
        +    "remainingUsd",
        +    "resetsInSeconds"
        +  ],
        +  "type": "object"
        +}
    • Changedopenalex_get_citation_graph1 field changed
      • addedOutput schema / properties / budget
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "What this call cost against the OpenAlex daily budget and what is left of it. Price a full walk before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.",
        +  "properties": {
        +    "costUsd": {
        +      "description": "USD this call spent, covering both upstream requests — the seed validation lookup (unbilled) and the graph page itself.",
        +      "type": "number"
        +    },
        +    "remainingUsd": {
        +      "description": "USD left in today's OpenAlex budget after this call.",
        +      "type": "number"
        +    },
        +    "resetsInSeconds": {
        +      "description": "Seconds until the daily budget refills (midnight UTC).",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "costUsd",
        +    "remainingUsd",
        +    "resetsInSeconds"
        +  ],
        +  "type": "object"
        +}
    • Changedopenalex_resolve_name1 field changed
      • addedOutput schema / properties / budget
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "What this call cost against the OpenAlex daily budget and what is left of it. Read `remainingUsd` here to size the search or traversal this resolution feeds. Absent when OpenAlex omitted the accounting headers.",
        +  "properties": {
        +    "costUsd": {
        +      "description": "USD this call spent. Autocomplete is priced at the floor — resolving a name before filtering costs far less than the failed searches an ambiguous name causes.",
        +      "type": "number"
        +    },
        +    "remainingUsd": {
        +      "description": "USD left in today's OpenAlex budget after this call.",
        +      "type": "number"
        +    },
        +    "resetsInSeconds": {
        +      "description": "Seconds until the daily budget refills (midnight UTC).",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "costUsd",
        +    "remainingUsd",
        +    "resetsInSeconds"
        +  ],
        +  "type": "object"
        +}
    • Changedopenalex_search_entities1 field changed
      • addedOutput schema / properties / budget
        Added value: +{
        +  "additionalProperties": false,
        +  "description": "What this call cost against the OpenAlex daily budget and what is left of it. Price a full traversal before committing to it: `totalCount` ÷ `per_page` × `costUsd` against `remainingUsd`. Absent when OpenAlex omitted the accounting headers.",
        +  "properties": {
        +    "costUsd": {
        +      "description": "USD this call spent. 0 for an `id` lookup — OpenAlex does not bill single-entity fetches, so batching known IDs beats paging a filtered list.",
        +      "type": "number"
        +    },
        +    "remainingUsd": {
        +      "description": "USD left in today's OpenAlex budget after this call.",
        +      "type": "number"
        +    },
        +    "resetsInSeconds": {
        +      "description": "Seconds until the daily budget refills (midnight UTC).",
        +      "type": "number"
        +    }
        +  },
        +  "required": [
        +    "costUsd",
        +    "remainingUsd",
        +    "resetsInSeconds"
        +  ],
        +  "type": "object"
        +}
  9. 2 tool updates
    • Changedopenalex_get_citation_graph4 fields changed
      • changedInput schema / properties / sort / description
        Previous value: -"Sort field. Prefix with \"-\" for descending. Common: \"cited_by_count\", \"-publication_date\". Default is OpenAlex relevance."New value: +"Sort field. Prefix with \"-\" for descending. Comma-separate for a multi-key sort, applied left to right, with the \"-\" prefix set per key (\"-publication_year,cited_by_count\" sorts by year descending, then citations ascending). Common: \"cited_by_count\", \"-publication_date\". Default is OpenAlex relevance."
      • addedOutput schema / properties / results / items / properties / display_name / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / results / items / properties / display_name / description
        Previous value: -"Work title."New value: +"Work title. null when OpenAlex holds no title for the record (paratext works and other untitled entries) — use `id` to identify it."
      • removedOutput schema / properties / results / items / properties / display_name / type
        Removed value: -"string"
    • Changedopenalex_search_entities5 fields changed
      • changedInput schema / properties / sort / description
        Previous value: -"Sort field. Prefix with \"-\" for descending. Common: \"cited_by_count\", \"-publication_date\", \"-relevance_score\" (default when query present). Note: when combined with a keyword query, an explicit sort overrides relevance ranking entirely — top results may be highly cited but only tangentially on-topic. Use \"-relevance_score\" or omit sort to keep the most relevant results first. \"-relevance_score\" requires an active search via \"query\" or a \"filter:search\" filter — passing it without one will fail."New value: +"Sort field. Prefix with \"-\" for descending. Comma-separate for a multi-key sort, applied left to right, with the \"-\" prefix set per key (\"-publication_year,cited_by_count\" sorts by year descending, then citations ascending). Common: \"cited_by_count\", \"-publication_date\", \"-relevance_score\" (default when query present). Note: when combined with a keyword query, an explicit sort overrides relevance ranking entirely — top results may be highly cited but only tangentially on-topic. Use \"-relevance_score\" or omit sort to keep the most relevant results first. \"-relevance_score\" requires an active search via \"query\" or a \"filter:search\" filter — passing it without one will fail."
      • changedOutput schema / properties / results / items / description
        Previous value: -"A single OpenAlex entity record. Core `id` and `display_name` are guaranteed; additional fields vary by entity_type and `select`."New value: +"A single OpenAlex entity record. `id` is always present and `display_name` is always returned (though it may be null); additional fields vary by entity_type and `select`."
      • addedOutput schema / properties / results / items / properties / display_name / anyOf
        Added value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / results / items / properties / display_name / description
        Previous value: -"Entity name or work title."New value: +"Entity name or work title. null when OpenAlex holds no title for the record (paratext works and other untitled entries) — use `id` to identify it."
      • removedOutput schema / properties / results / items / properties / display_name / type
        Removed value: -"string"
  10. 1 tool update
    • Changedopenalex_describe_fields1 field changed
      • changedInput schema / properties / context / description
        Previous value: -"Field usage context. \"filter\": fields accepted in the filter param. \"group_by\": fields accepted in group_by (same valid set as filter). \"select\": fields accepted in select."New value: +"Field usage context. \"filter\": fields accepted in the filter param. \"group_by\": fields accepted in group_by — a subset of the filter set (raw date and *.search fields are excluded; they cannot be grouped). \"select\": fields accepted in select."
  11. 2 tool updates
    • Changedopenalex_analyze_trends3 fields changed
      • removedOutput schema / properties / entityTotal
        Removed value: -{
        -  "description": "Total entities matching the filters before grouping (across all pages).",
        -  "type": "number"
        -}
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total entities matching the filters before grouping (across all pages).",
        +  "type": "number"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "meta",
        -  "groups",
        -  "echo",
        -  "entityTotal"
        -]New value: +[
        +  "meta",
        +  "groups",
        +  "echo",
        +  "totalCount"
        +]
    • Changedopenalex_get_citation_graph3 fields changed
      • addedOutput schema / properties / totalCount
        Added value: +{
        +  "description": "Total edges from seed_id in this direction across all pages.",
        +  "type": "number"
        +}
      • removedOutput schema / properties / totalEdges
        Removed value: -{
        -  "description": "Total edges from seed_id in this direction across all pages.",
        -  "type": "number"
        -}
      • changedOutput schema / required
        Previous value: -[
        -  "meta",
        -  "results",
        -  "echo",
        -  "totalEdges"
        -]New value: +[
        +  "meta",
        +  "results",
        +  "echo",
        +  "totalCount"
        +]
  12. 1 tool update
    • Addedopenalex_describe_fields
  13. 1 tool update
    • Changedopenalex_analyze_trends4 fields changed
      • changedInput schema / properties / cursor / description
        Previous value: -"Pagination cursor from a previous response. Group-by returns max 200 groups per page. Pass cursor to get the next page. The first page is sorted by count descending; subsequent pages (cursor pages) are sorted by key, not by count."New value: +"Pagination cursor from a previous response. Only relevant when order is \"key\" — count-descending results have no next page. Pass the next_cursor from the previous response to advance."
      • addedInput schema / properties / order
        Added value: +{
        +  "description": "Sort order for groups. Omit or pass \"count\" (default) to return the top-N groups by count descending — no further pages. Pass \"key\" to enumerate all distinct values in key-ascending order with cursor pagination. Use \"key\" only when you need a full traversal; most analysis calls want \"count\".",
        +  "enum": [
        +    "count",
        +    "key"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / per_page / description
        Previous value: -"Maximum groups per page (1-200). Default 200 (the upstream cap). Reduce when only the top-N groups matter — same aggregation, smaller payload."New value: +"Maximum groups per page (1-200). Default 200 (the upstream cap). A real top-N knob when order is count (the default) — reduce to return only the highest-count groups."
      • changedOutput schema / properties / notice / description
        Previous value: -"Recovery guidance when no groups are returned — echoes the criteria and suggests how to adjust. Absent when groups are present."New value: +"Guidance notice. Set when no groups are returned (recovery suggestions) or when the page is full and more groups likely exist (truncation signal with narrowing advice). Absent otherwise."

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    Provides access to OpenAlex's catalog of 240M+ scholarly works, enabling search and retrieval of research papers, authors, institutions, journals, concepts, and funders with advanced filtering and classification capabilities.
    11
    29 npm
    5
    MIT
  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides access to the OpenAlex API, enabling search and retrieval of scholarly works, authors, sources, and more.
    -
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.