openalex-mcp-server
Server Details
Access the OpenAlex academic research catalog — 270M+ publications.
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP
- URL
- Repository
- cyanheads/openalex-mcp-server
- GitHub Stars
- 14
- Server Listing
- @cyanheads/openalex-mcp-server
Available Tools
5 toolsopenalex_analyze_trendsOpenalex Analyze TrendsARead-onlyIdempotentInspect
Aggregate OpenAlex entities into groups and count them. Use for trend analysis (group works by publication_year), distribution analysis (group by oa_status, type, country), and comparative analysis (group by institution or topic). Combine with filters to scope the analysis. Returns up to 200 groups per page — use cursor pagination for fields with many distinct values.
| Name | Required | Description | Default |
|---|---|---|---|
| order | No | 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". | |
| cursor | No | 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. | |
| filters | No | Filter criteria (same syntax as openalex_search_entities filters). Narrows the population before aggregation. For full-text within filters, use abstract.search, title.search, or default.search — there is no bare 'search' filter key. Example: group works by year filtered to a specific topic. | |
| group_by | Yes | 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. | |
| per_page | No | 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. | |
| entity_type | Yes | Entity type to aggregate. | |
| include_unknown | No | Include a group for entities with no value for the grouped field. Hidden by default. |
Output Schema
| Name | Required | Description |
|---|---|---|
| echo | No | Compact echo of the input criteria (entity_type, group_by, filters) — surfaces what was actually requested when no groups are returned. |
| meta | No | Aggregation metadata. |
| error | No | Present when the call failed. Absent on success. |
| budget | No | 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. |
| groups | No | Aggregation groups with counts. |
| notice | No | 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. |
| totalCount | No | Total entities matching the filters before grouping (across all pages). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, world-open, and idempotent behavior, and the description adds useful execution traits: the 200-group page cap, cursor pagination for high-cardinality fields, and count-based grouping semantics. It neither contradicts the annotations nor withholds operationally significant behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences deliver the core behavior, canonical use cases, filter guidance, and pagination constraint with no filler. The most important information (what it does) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter aggregation tool with full schema descriptions, an output schema, and read-only/idempotent annotations, the description covers purpose, use cases, scoping, and pagination behavior. It does not need to restate return shapes because the output schema exists, so nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the prose adds use-case framing (e.g., grouping by oa_status for distribution) and general filter scoping, but the per-parameter details are already fully documented in the input schema. The description does not need to compensate and does not substantially exceed the schema's own documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific operation ('Aggregate OpenAlex entities into groups and count them') rather than restating the title, and immediately distinguishes this analysis endpoint from sibling search/describe/citation/resolve tools. Concrete grouping examples (publication_year, oa_status, country) make the resource and intent unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly lists when to use the tool: trend, distribution, and comparative analysis, and notes that filters can scope the population. It does not name sibling alternatives or state when not to use them, so it misses the explicit exclusion step needed for a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
openalex_describe_fieldsOpenalex Describe FieldsARead-onlyIdempotentInspect
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 narrow the results by name similarity — useful when you have a partial or guessed field name.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | 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. | |
| context | Yes | 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. | |
| entity_type | Yes | OpenAlex entity type to list fields for. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| total | No | Total number of valid fields for this entity_type + context. |
| fields | No | Valid field names, ranked by similarity to query when provided. |
| context | No | Context queried (filter, group_by, or select). |
| entity_type | No | Entity type queried. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and idempotentHint=true, so the description doesn't need to repeat that information. It adds context about the tool's purpose and usage, but doesn't disclose additional behavioral traits beyond that—such as return format (though output schema exists) or error handling. With annotations covering the safety profile, the description adds value by explaining the context enum semantics and the query behavior, but it's not particularly rich in behavioral disclosure. The description doesn't contradict annotations; it's consistent with read-only/idempotent behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a concise paragraph that front-loads the primary purpose (listing field names to avoid 400 errors) and then explains the optional `query` parameter. Every sentence serves a purpose: stating the action, proscribing proactive use, and clarifying the narrowing behavior. It's not overly verbose, but it could be slightly more structured (e.g., bullets for contexts) without losing conciseness. Still, it's efficient and well-organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (3 params, 1 fully documented via schema and output schema present), the description adequately covers what an agent needs to know: when to use it, how to refine with query, and that it covers filter/group_by/select contexts. The output schema likely explains return values, so the description doesn't need to. The only minor gap is that it doesn't explicitly state that the tool is read-only, but annotations cover that. Overall, it's complete enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, meaning the schema already fully documents all three parameters. The description adds minimal extra semantics beyond what the schema provides—it reinforces the purpose of `query` and describes the context enum's meaning, but these details are largely already present in the schema property descriptions. Since coverage is high, the baseline of 3 is appropriate; the description doesn't need to compensate for missing schema info but also doesn't add significant value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose — listing valid field names for an OpenAlex entity type and context (filter, group_by, or select) — with a specific verb ('list'), a specific resource (OpenAlex field names), and explicit context of use (before constructing filters/group_by). It distinguishes itself from siblings by focusing solely on field enumeration, not on searching entities or analyzing trends, which are covered by sibling tools like openalex_search_entities and openalex_analyze_trends.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Use proactively before constructing a filter or group_by to avoid invalid-field 400 errors.' It also explains the optional `query` parameter's purpose for narrowing results by name similarity and implies when not to use it (e.g., when the exact field name is known, omit query). It indirectly differentiates from siblings by focusing on field discovery, not entity retrieval or analysis.
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 GraphARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | 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. | |
| cursor | No | Pagination cursor from a previous response. Pass to get the next page. | |
| select | No | OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted. | |
| filters | No | 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. | |
| seed_id | Yes | 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. | |
| per_page | No | Results per page (1-100). Default 25. | |
| direction | Yes | "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
| Name | Required | Description |
|---|---|---|
| echo | No | Compact echo of seed_id, direction, filters, sort — surfaces what was actually queried when no edges are returned. |
| meta | No | Result metadata including pagination. |
| error | No | Present when the call failed. Absent on success. |
| budget | No | 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. |
| notice | No | Recovery guidance when no edges are returned — suggests verifying the seed_id, broadening filters, or trying a different direction. Absent when results are present. |
| results | No | Works on the citation graph in this direction. |
| totalCount | No | Total edges from seed_id in this direction across all pages. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds valuable behavioral context beyond the readOnly/openWorld/idempotent annotations, especially the counterintuitive OpenAlex filter convention: 'cites returns works that cite the seed; cited_by returns works the seed cites.' It also discloses the one-hop boundary and that results use the works schema, making the tool's behavior predictable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loads the core purpose, and includes only high-value caveats such as the inverted direction naming and the resolve_name prerequisite. Every sentence contributes to correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich input schema, detailed per-parameter descriptions, output schema, and annotations covering read-only/idempotent behavior, the description is complete enough for an agent to invoke the tool correctly. It explains the graph-walk semantics, direction confusion, result schema, and the prerequisite for resolving titles.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already carries the full parameter semantics. The description adds some orientation by explaining the direction convention and mentioning filter/sort combinations, but it does not need to and does not substantially extend what the rich property descriptions already provide.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific, actionable statement: 'Walk the citation graph one hop from a seed work.' It names the resource (citation graph), the operation (walk/hop), and the key parameter (direction), and it clearly distinguishes this graph-traversal tool from the search-oriented siblings like openalex_search_entities.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool and even names a prerequisite alternative: 'Use openalex_resolve_name first if you only have a title.' It also explains how filters relate to search syntax via openalex_search_entities, but it does not explicitly state when NOT to use the tool versus search_entities or analyze_trends.
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 NameARead-onlyIdempotentInspect
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, PMCID, or ISSN, bare or in URL form — resolves directly to the one record it addresses, and needs no entity_type.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | 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. | |
| filters | No | 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. | |
| entity_type | No | 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. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| budget | No | 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. |
| notice | No | 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. |
| results | No | Autocomplete matches, up to 10. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/openWorld/idempotent annotations, the description discloses meaningful behavioral details: name queries return up to 10 autocomplete matches with disambiguation hints, while identifiers resolve directly to a single record. It also clarifies that identifiers make entity_type unnecessary, which is helpful behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, front-loaded with the most important usage rule, and every sentence earns its place. It covers purpose, conflict, behavior, and parameter nuance without redundancy or padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only resolution tool with a full output schema and already-annotated safety profile, the description is complete. It tells the agent how the two input forms behave, what to expect from name queries, and when not to supply entity_type. No critical calling context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% parameter coverage with rich descriptions for query, filters, and entity_type. The tool description reinforces those semantics but doesn't add much beyond the schema. The extra details about autocomplete limits and identifier behaviors are useful context but not parameter-level semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's action ('Resolve') and its resource ('a name or an identifier to an OpenAlex ID'). It is easy to distinguish from siblings like search_entities because it explicitly frames itself as the disambiguation/resolution step rather than a general search.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives strong usage guidance: 'ALWAYS use this before filtering by entity' and explains why names are ambiguous while IDs are not. It also details when entity_type can be omitted. It doesn't explicitly name alternative sibling tools for comparison, but the contextual guidance is clear enough for correct selection.
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 EntitiesARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | 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. | |
| seed | No | 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. | |
| sort | No | 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. | |
| query | No | 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. | |
| cursor | No | Pagination cursor from a previous response. Pass to get the next page. | |
| sample | No | 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. | |
| select | No | 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"]. | |
| filters | No | 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`. | |
| per_page | No | 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. | |
| entity_type | Yes | Type of scholarly entity to search. | |
| search_mode | No | 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). | keyword |
Output Schema
| Name | Required | Description |
|---|---|---|
| echo | No | 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. |
| meta | No | Result metadata including pagination. |
| error | No | Present when the call failed. Absent on success. |
| budget | No | 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. |
| notice | No | 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. |
| results | No | OpenAlex entity objects passed through unchanged. Additional fields depend on entity_type and select. |
| totalCount | No | Total results matching the query/filters across all pages. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the tool read-only, open-world, and idempotent, lowering the baseline burden. The description adds substantial behavioral detail beyond annotations: ignored parameters when `id` is supplied, a response `notice` naming ignored arguments, rejection of `seed` without `sample`, the interaction between explicit sort and relevance, semantic search caps, and rate limits upstream. These are valuable and non-contradictory.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but appropriately sized for an 11-parameter tool with complex interactions. The core purpose and entity scope are front-loaded, and nearly every sentence conveys a distinct operational fact: ID semantics, filter syntax, sort effects, pagination caps, selection behavior, and error handling. No sentence is redundant with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers all major usage modes: ID lookup, keyword/boolean/exact/semantic search, filtering, sorting, pagination, sampling, field selection, and error behavior. The output schema handles return shape, the required `entity_type` is fully enumerated, and annotations cover safety and idempotency. Nothing an agent needs to call this tool correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds significant meaning beyond the schema. It explains accepted external ID formats for `id` (DOI, ORCID, ROR, PMID, PMCID, ISSN), filtering syntax such as pipe-separated OR and bang-prefixed NOT, multi-key sort semantics, `select` override behavior, and the concrete meaning of each `search_mode` value. This goes well beyond the structured schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Search, filter, sort, or retrieve by ID,' a specific set of verbs plus the OpenAlex entity resource, and enumerates all eight entity types. It explicitly distinguishes ID retrieval from query/filter discovery and names openalex_resolve_name as the tool for name resolution, making sibling differentiation clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: 'Pass `id` to retrieve a single entity. Otherwise, use `query` and/or `filters` for discovery' and instructs to 'Use openalex_resolve_name to resolve names to IDs before filtering.' It also clarifies when to drop `id`, when explicit sort overrides relevance, and when to use `select`, so an agent knows how to choose behavior within the tool.
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. Dates show when Glama detected each change.
4 tool updates
- Changed
openalex_analyze_trends4 fields changed- removed
Output schema / properties / meta / properties / groups_count / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / meta / properties / groups_count / typeAdded value: +[ + "number", + "null" +] - removed
Output schema / properties / meta / properties / next_cursor / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / meta / properties / next_cursor / typeAdded value: +[ + "string", + "null" +]
- Changed
openalex_get_citation_graph4 fields changed- removed
Output schema / properties / meta / properties / next_cursor / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / meta / properties / next_cursor / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / display_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / display_name / typeAdded value: +[ + "string", + "null" +]
- Changed
openalex_resolve_name8 fields changed- removed
Output schema / properties / results / items / properties / display_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / display_name / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / external_id / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / external_id / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / hint / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / hint / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / works_count / anyOfRemoved value: -[ - { - "type": "number" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / works_count / typeAdded value: +[ + "number", + "null" +]
- Changed
openalex_search_entities4 fields changed- removed
Output schema / properties / meta / properties / next_cursor / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / meta / properties / next_cursor / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / results / items / properties / display_name / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / results / items / properties / display_name / typeAdded value: +[ + "string", + "null" +]
5 tool updates
- Changed
openalex_analyze_trends6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "meta", + "groups", + "echo", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "meta", - "groups", - "echo", - "totalCount" -]
- Changed
openalex_describe_fields6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "entity_type", + "context", + "fields", + "total" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "entity_type", - "context", - "fields", - "total" -]
- Changed
openalex_get_citation_graph6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "meta", + "results", + "echo", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "meta", - "results", - "echo", - "totalCount" -]
- Changed
openalex_resolve_name6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "results" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "results" -]
- Changed
openalex_search_entities6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "meta", + "results", + "echo", + "totalCount" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded 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" +} - removed
Output schema / requiredRemoved value: -[ - "meta", - "results", - "echo", - "totalCount" -]
4 tool updates
- Changed
openalex_analyze_trends1 field changed- added
Output schema / properties / budget / properties / prepaidRemainingUsdAdded 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" +}
- Changed
openalex_get_citation_graph1 field changed- added
Output schema / properties / budget / properties / prepaidRemainingUsdAdded 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" +}
- Changed
openalex_resolve_name9 fields changed- changed
Input schema / properties / entity_type / descriptionPrevious 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." - changed
Input schema / properties / filters / descriptionPrevious 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." - changed
Input schema / properties / query / descriptionPrevious 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." - added
Output schema / properties / budget / properties / prepaidRemainingUsdAdded 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" +} - changed
Output schema / properties / notice / descriptionPrevious 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." - added
Output schema / properties / results / items / properties / display_name / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Output schema / properties / results / items / properties / display_name / descriptionPrevious 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." - removed
Output schema / properties / results / items / properties / display_name / typeRemoved value: -"string" - changed
Output schema / properties / results / items / properties / hint / descriptionPrevious 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."
- Changed
openalex_search_entities6 fields changed- changed
Input schema / properties / id / descriptionPrevious 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." - changed
Input schema / properties / per_page / descriptionPrevious 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." - changed
Input schema / properties / seed / descriptionPrevious 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." - added
Output schema / properties / budget / properties / prepaidRemainingUsdAdded 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" +} - changed
Output schema / properties / echo / descriptionPrevious 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." - changed
Output schema / properties / notice / descriptionPrevious 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."
4 tool updates
- Changed
openalex_analyze_trends1 field changed- added
Output schema / properties / budgetAdded 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" +}
- Changed
openalex_get_citation_graph1 field changed- added
Output schema / properties / budgetAdded 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" +}
- Changed
openalex_resolve_name1 field changed- added
Output schema / properties / budgetAdded 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" +}
- Changed
openalex_search_entities1 field changed- added
Output schema / properties / budgetAdded 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" +}
2 tool updates
- Changed
openalex_get_citation_graph4 fields changed- changed
Input schema / properties / sort / descriptionPrevious 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." - added
Output schema / properties / results / items / properties / display_name / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Output schema / properties / results / items / properties / display_name / descriptionPrevious 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." - removed
Output schema / properties / results / items / properties / display_name / typeRemoved value: -"string"
- Changed
openalex_search_entities5 fields changed- changed
Input schema / properties / sort / descriptionPrevious 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." - changed
Output schema / properties / results / items / descriptionPrevious 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`." - added
Output schema / properties / results / items / properties / display_name / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Output schema / properties / results / items / properties / display_name / descriptionPrevious 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." - removed
Output schema / properties / results / items / properties / display_name / typeRemoved value: -"string"
1 tool update
- Changed
openalex_describe_fields1 field changed- changed
Input schema / properties / context / descriptionPrevious 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."
2 tool updates
- Changed
openalex_analyze_trends3 fields changed- removed
Output schema / properties / entityTotalRemoved value: -{ - "description": "Total entities matching the filters before grouping (across all pages).", - "type": "number" -} - added
Output schema / properties / totalCountAdded value: +{ + "description": "Total entities matching the filters before grouping (across all pages).", + "type": "number" +} - changed
Output schema / requiredPrevious value: -[ - "meta", - "groups", - "echo", - "entityTotal" -]New value: +[ + "meta", + "groups", + "echo", + "totalCount" +]
- Changed
openalex_get_citation_graph3 fields changed- added
Output schema / properties / totalCountAdded value: +{ + "description": "Total edges from seed_id in this direction across all pages.", + "type": "number" +} - removed
Output schema / properties / totalEdgesRemoved value: -{ - "description": "Total edges from seed_id in this direction across all pages.", - "type": "number" -} - changed
Output schema / requiredPrevious value: -[ - "meta", - "results", - "echo", - "totalEdges" -]New value: +[ + "meta", + "results", + "echo", + "totalCount" +]
1 tool update
- Added
openalex_describe_fields
1 tool update
- Changed
openalex_analyze_trends4 fields changed- changed
Input schema / properties / cursor / descriptionPrevious 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." - added
Input schema / properties / orderAdded 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" +} - changed
Input schema / properties / per_page / descriptionPrevious 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." - changed
Output schema / properties / notice / descriptionPrevious 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."
3 tool updates
- Changed
openalex_analyze_trends2 fields changed- changed
Input schema / properties / per_page / descriptionPrevious value: -"Maximum groups per page (1-200). Default 200 (the upstream cap). Use lower values when only the top-N groups are relevant — reduces token spend without changing the underlying aggregation."New value: +"Maximum groups per page (1-200). Default 200 (the upstream cap). Reduce when only the top-N groups matter — same aggregation, smaller payload." - changed
Output schema / properties / echo / descriptionPrevious value: -"Compact echo of the input criteria (entity_type, group_by, filters) — useful when no groups are returned so callers see what was actually requested."New value: +"Compact echo of the input criteria (entity_type, group_by, filters) — surfaces what was actually requested when no groups are returned."
- Changed
openalex_get_citation_graph1 field changed- changed
Output schema / properties / echo / descriptionPrevious value: -"Compact echo of seed_id, direction, filters, sort — useful when no edges are returned so callers see what was actually queried."New value: +"Compact echo of seed_id, direction, filters, sort — surfaces what was actually queried when no edges are returned."
- Changed
openalex_search_entities3 fields changed- changed
Input schema / properties / id / descriptionPrevious 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. 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, 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." - changed
Input schema / properties / select / descriptionPrevious value: -"OpenAlex top-level field names to return. Always returned: `id`, `display_name` — additional fields you list are appended. Searches apply a curated default per entity type; pass to override. Single-entity lookups (by `id`) return the full record unless set. Invalid field names produce an error listing the valid ones. 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). Invalid field names produce an error identifying the rejected field. Example: [\"doi\", \"authorships\", \"primary_topic\"]." - changed
Output schema / properties / echo / descriptionPrevious value: -"Compact echo of the input criteria (entity_type, query, filters, sort, search_mode) — useful when results are empty so callers see what was actually searched."New value: +"Compact echo of the input criteria (entity_type, query, filters, sort, search_mode) — surfaces what was actually searched when results are empty."
4 tool updates
- Changed
openalex_analyze_trends6 fields changed- added
Output schema / properties / echoAdded value: +{ + "description": "Compact echo of the input criteria (entity_type, group_by, filters) — useful when no groups are returned so callers see what was actually requested.", + "type": "string" +} - added
Output schema / properties / entityTotalAdded value: +{ + "description": "Total entities matching the filters before grouping (across all pages).", + "type": "number" +} - removed
Output schema / properties / meta / properties / echoRemoved value: -{ - "description": "Compact echo of the input criteria (entity_type, group_by, filters) — useful when no groups are returned so callers see what was actually requested.", - "type": "string" -} - changed
Output schema / properties / meta / requiredPrevious value: -[ - "count", - "groups_count", - "next_cursor", - "echo" -]New value: +[ + "count", + "groups_count", + "next_cursor" +] - added
Output schema / properties / noticeAdded value: +{ + "description": "Recovery guidance when no groups are returned — echoes the criteria and suggests how to adjust. Absent when groups are present.", + "type": "string" +} - changed
Output schema / requiredPrevious value: -[ - "meta", - "groups" -]New value: +[ + "meta", + "groups", + "echo", + "entityTotal" +]
- Changed
openalex_get_citation_graph6 fields changed- added
Output schema / properties / echoAdded value: +{ + "description": "Compact echo of seed_id, direction, filters, sort — useful when no edges are returned so callers see what was actually queried.", + "type": "string" +} - removed
Output schema / properties / meta / properties / echoRemoved value: -{ - "description": "Compact echo of seed_id, direction, filters, sort — useful when empty.", - "type": "string" -} - changed
Output schema / properties / meta / requiredPrevious value: -[ - "count", - "per_page", - "next_cursor", - "echo" -]New value: +[ + "count", + "per_page", + "next_cursor" +] - added
Output schema / properties / noticeAdded value: +{ + "description": "Recovery guidance when no edges are returned — suggests verifying the seed_id, broadening filters, or trying a different direction. Absent when results are present.", + "type": "string" +} - added
Output schema / properties / totalEdgesAdded value: +{ + "description": "Total edges from seed_id in this direction across all pages.", + "type": "number" +} - changed
Output schema / requiredPrevious value: -[ - "meta", - "results" -]New value: +[ + "meta", + "results", + "echo", + "totalEdges" +]
- Changed
openalex_resolve_name1 field changed- added
Output schema / properties / noticeAdded value: +{ + "description": "Recovery guidance when no matches were found — echoes the query and suggests corrections. Absent when results are present.", + "type": "string" +}
- Changed
openalex_search_entities6 fields changed- added
Output schema / properties / echoAdded value: +{ + "description": "Compact echo of the input criteria (entity_type, query, filters, sort, search_mode) — useful when results are empty so callers see what was actually searched.", + "type": "string" +} - removed
Output schema / properties / meta / properties / echoRemoved value: -{ - "description": "Compact echo of the input criteria (entity_type, query, filters, sort, search_mode) — useful when results are empty so callers see what was actually searched.", - "type": "string" -} - changed
Output schema / properties / meta / requiredPrevious value: -[ - "count", - "per_page", - "next_cursor", - "echo" -]New value: +[ + "count", + "per_page", + "next_cursor" +] - added
Output schema / properties / noticeAdded value: +{ + "description": "Recovery guidance when results are empty — echoes the criteria and suggests how to broaden. Absent on successful result pages.", + "type": "string" +} - added
Output schema / properties / totalCountAdded value: +{ + "description": "Total results matching the query/filters across all pages.", + "type": "number" +} - changed
Output schema / requiredPrevious value: -[ - "meta", - "results" -]New value: +[ + "meta", + "results", + "echo", + "totalCount" +]
1 tool update
- Changed
openalex_get_citation_graph1 field changed- changed
Input schema / properties / filters / descriptionPrevious value: -"Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=\">2020\", is_oa=\"true\". The direction filter is added automatically — do not pass cites/cited_by/related_to here."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 — those are set by the `direction` parameter."
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
Academic literature search, retrieval, and private library management on top of OpenAlex.
Scholarly search: OpenAlex, Crossref, arXiv, OpenCitations and PubMed in one endpoint.
OpenAlex MCP — wraps the OpenAlex API (scholarly works, free, no auth)
Search 340M+ academic papers — citation graphs, semantic similarity, and AI literature reviews.
Related MCP Servers
- AlicenseBqualityFmaintenanceProvides 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.11565MIT
- -licenseNot gradedqualityNot gradedmaintenanceProvides access to the OpenAlex API, enabling search and retrieval of scholarly works, authors, sources, and more.-
- FlicenseNot gradedqualityCmaintenanceEnables searching and retrieving academic papers, authors, institutions, and citations from the OpenAlex open scholarly index.-
- AlicenseNot gradedqualityDmaintenanceEnables searching and retrieving scholarly works, authors, institutions, and citation networks from the OpenAlex catalog via natural language.34ISC
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
Each tool has a clearly distinct purpose: searching entities, resolving names/IDs, aggregating counts, walking the citation graph, and validating field names. The descriptions explicitly cross-reference when to use which tool, eliminating ambiguity.
All tool names share the openalex_ prefix and follow a consistent verb_noun pattern: search_entities, resolve_name, analyze_trends, get_citation_graph, describe_fields. The naming is uniform and predictable.
Five tools is well-scoped for an OpenAlex API server covering discovery, resolution, aggregation, citation traversal, and schema introspection. Each tool earns its place without redundancy or bloat.
The tool set covers the core OpenAlex workflows: search/filter/retrieve entities, resolve ambiguous identifiers, aggregate for trends, explore citation relationships, and avoid invalid-field errors. Since OpenAlex is a read-only API, no CRUD operations are expected, and no significant gaps are apparent.