CCM
/MCP
SkillsMCPMarketplacesDigestTools

This week in Claude

Every Monday: Claude Code, Agent SDK, MCP, and the Anthropic platform moves worth your time.

Skills by Category
Frontend DevelopmentBackend & APIsTesting & QASecurityDevOps & CI/CDGit & Pull RequestsDocumentationCode Review & QualityAI & Agent BuildingSkill Development
MCP Servers by Category
Sales & MarketingWeb & Browser AutomationDatabasesAI & LLM ToolsCloud & InfrastructureCommunication & MessagingDeveloper ToolsDesign & CreativeDocuments & KnowledgeSearch & Web Crawling
Marketplaces by Category
AI Agents & OrchestrationLLM IntegrationDevelopment ToolsFrontend & UIBackend & APIsDatabasesTesting & Code QualityDevOps & CloudSecurity & ComplianceGit & Version Control

Claude Code Marketplaces

Discover Claude Code plugins, extensions, and tools. Automatically updated directory of Anthropic Claude AI marketplaces with development tools, productivity plugins, and integrations.

Resources

  • Browse Skills
  • Browse MCP Servers
  • Browse Marketplaces
  • Skill index
  • MCP index
  • Marketplace index
  • Plugins Reference

Community

  • About
  • Tools
  • Feedback
  • Privacy Policy

Built for the Claude Code community with Claude Code by mertbuilds.com

Independent project, not affiliated with Anthropic
cyanheads avatar

Openalex Mcp Server

cyanheads/openalex-mcp-server
75 toolsSTDIO, HTTPregistry active
Summary

Connects Claude to OpenAlex's catalog of 270 million academic publications, authors, institutions, and topics. Exposes four tools: entity search with filters and pagination, trend aggregation for distribution analysis, name-to-ID resolution via autocomplete, and citation graph traversal in both directions. Also includes two structured prompts for literature reviews and research landscape analysis. Built on the author's mcp-ts-core framework with typed API clients, automatic ID normalization across DOI/ORCID/ROR/PMID formats, and abstract reconstruction from OpenAlex's inverted indices. Available as a public hosted instance at openalex.caseyjhand.com or self-hosted via npx. Useful for systematic literature searches, citation network analysis, tracking research trends, or resolving ambiguous author and institution names before filtering.

Install to Claude Code

verified
claude mcp add --transport http openalex https://openalex.caseyjhand.com/mcp

Run in your terminal. Add --scope user to make it available in every project.

Review the command, arguments, and environment values before installing — MCP servers run with your local permissions.

CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done. Free and open source.
Download free →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
Devtools for design engineers
Devtools for design engineers
Test dark mode, contrast, motion, text size, locale and time zone from one panel on your dev server. Free and open source.
Try it live →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done. Free and open source.
Download free →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
Devtools for design engineers
Devtools for design engineers
Test dark mode, contrast, motion, text size, locale and time zone from one panel on your dev server. Free and open source.
Try it live →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →

Tools

Verified live against the running server on Jun 10, 2026.

verified live5 tools
openalex_resolve_nameResolve a name or partial name to an OpenAlex ID. Returns up to 10 matches with disambiguation hints. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. Also accepts DOIs directly for quick lookup.3 params

Resolve a name or partial name to an OpenAlex ID. Returns up to 10 matches with disambiguation hints. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. Also accepts DOIs directly for quick lookup.

Parameters* required
entity_typestring
Entity type to search. Omit for cross-entity search (useful when entity type is unknown).one of works · authors · sources · institutions · topics · keywords
filtersobject
Narrow autocomplete results with filters. Example: restrict to a specific country or publication year range.
query*string
Name or partial name to resolve. Also accepts DOIs for quick lookup.
openalex_search_entitiesSearch, 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...11 params

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

Parameters* required
cursorstring
Pagination cursor from a previous response. Pass to get the next page.
entity_type*string
Type of scholarly entity to search.one of works · authors · sources · institutions · topics · keywords
filtersobject
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...
idstring
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 ("PMC1...
per_pageinteger
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).default: 25
querystring
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-onl...
sampleinteger
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 explo...
search_modestring
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...one of keyword · exact · semanticdefault: keyword
seedstring
Deterministic seed for `sample`. Same seed + same filters = same results — pass when reproducibility matters. Has no effect (and is rejected) without `sample`.
selectarray
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-enti...
sortstring
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 sor...
openalex_analyze_trendsAggregate 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. Re...7 params

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

Parameters* required
cursorstring
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.
entity_type*string
Entity type to aggregate.one of works · authors · sources · institutions · topics · keywords
filtersobject
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 — th...
group_by*string
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.coun...
include_unknownboolean
Include a group for entities with no value for the grouped field. Hidden by default.default: false
orderstring
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 curs...one of count · key
per_pageinteger
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.default: 200
openalex_get_citation_graphWalk 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 t...7 params

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

Parameters* required
cursorstring
Pagination cursor from a previous response. Pass to get the next page.
direction*string
"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 f...one of cites · cited_by · related_to
filtersobject
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 `di...
per_pageinteger
Results per page (1-100). Default 25.default: 25
seed_id*string
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.
selectarray
OpenAlex work field names to return. Always returned: id, display_name. Defaults to the curated works select if omitted.
sortstring
Sort field. Prefix with "-" for descending. Common: "cited_by_count", "-publication_date". Default is OpenAlex relevance.
openalex_describe_fieldsList 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 g...3 params

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

Parameters* required
context*string
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.one of filter · group_by · select
entity_type*string
OpenAlex entity type to list fields for.one of works · authors · sources · institutions · topics · keywords
querystring
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 + cont...

@cyanheads/openalex-mcp-server

Access the OpenAlex academic research catalog — 270M+ publications — via MCP. STDIO or Streamable HTTP.

5 Tools • 2 Prompts

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

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


Overview

Scholarly catalog data from OpenAlex — 270M+ works, 90M+ authors, 100K+ sources, plus institutions, topics, keywords, publishers, and funders. Search, filter, and aggregate across all eight entity types, resolve ambiguous names to canonical IDs, and walk the citation graph one hop at a time.

Tools

ToolDescription
openalex_search_entitiesSearch, filter, sort, or retrieve by ID across all 8 entity types
openalex_analyze_trendsGroup-by aggregation for trend and distribution analysis
openalex_resolve_nameResolve a name or an identifier (DOI, ORCID, ROR, PMID, ISSN, OpenAlex ID) to an OpenAlex ID
openalex_get_citation_graphWalk the citation graph one hop from a seed work: cites, cited_by, or related_to
openalex_describe_fieldsList valid filter, group_by, and select field names for an entity type

Prompts

PromptDescription
openalex_literature_reviewGuides a systematic literature search: formulate query, search, filter, analyze citation network, synthesize findings
openalex_research_landscapeAnalyzes the research landscape for a topic: volume trends, top authors/institutions, open access rates, funding sources

Capability reference

openalex_search_entities tool

  • Retrieve a single entity by ID — OpenAlex ID, DOI, ORCID, ROR, PMID, ISSN, or PMCID (bare or URL form, including OpenAlex record-page and API URLs such as https://openalex.org/works/W2741809807), or a keyword by its slug or keyword URL. Under entity_type: "funders", a funder's Crossref Funder Registry DOI (10.13039/…) resolves through a one-result list request. OpenAlex does not bill a lookup by ID, with three exceptions it bills like one list call: a keyword looked up by its slug or URL, an id carrying characters past the identifier itself (W2741809807#x still returns W2741809807), and that funder-DOI request. id takes precedence: search parameters passed alongside it are dropped, and the response names which ones. An OpenAlex ID whose letter names another entity type than entity_type fails as id_type_mismatch before any upstream call, and an id holding a . or .. path segment, plain or percent-encoded, fails as id_dot_segment; a ?, #, \, tab, or line break inside an identifier (SICI DOIs end in #) is sent percent-encoded as part of it. A PMCID resolves nothing (OpenAlex indexes none) — use the work's PMID or DOI instead
  • Keyword search (boolean operators, quoted phrases, wildcards, fuzzy match) plus exact and semantic search modes — semantic ranks a query-dependent candidate set at ~1 req/sec (up to 50 per page), and its meta.count reports the candidate count rather than a match total
  • Rich filter syntax: AND across fields, OR within a field (|), AND within a field (+), NOT (!), ranges, comparisons. One value cannot hold | beside an AND operator — a +, or a space between values on an ID, code, or language key (a space inside a language name is part of the name, and on a country field a space is always part of a country name, never AND: DE US|FR fails as invalid_country_code): OpenAlex splits a value on | first and drops every alternative that ANDs more than one value, so DE|FR+US and authorships.author.id: A1 A2|A3 both fail as mixed_and_or_in_filter_value before any request — put the AND in a second key instead (institutions.country_code: US beside authorships.institutions.country_code: DE|FR on works). A comma inside a free-text search filter (any .search key — title.search, abstract.search.exact, title_abstract_keywords.search) or a field OpenAlex matches as whole text (listed below) is sent double-quoted as part of the phrase, while a comma in any other filter value fails as comma_in_filter_value (use | for OR); inside a .search value, a DOI, a date (a timestamp's +00:00 offset), a URL (landing_page_url; not the boolean has_pdf_url), an exactly matched name (display_name, awards.funder_display_name — C++, Erasmus+), a biblio field (issue 1+2), or awards.funder_award_id, a + is a literal character; a value with spaces on a field OpenAlex matches as whole text (display_name, awards.funder_display_name, awards.funder_award_id, geo.city, geo.region, raw_affiliation_strings, block_key, a biblio field) is sent double-quoted, so it matches as one phrase (awards.funder_display_name: National Science Foundation) — ! and | work with it (geo.city: New York|Boston), but + cannot AND it with another value in the same field. A comma is part of such a value too (raw_affiliation_strings: Associazione Meteonetwork OdV, Milano, Italy): a value holding one goes out quoted with its + as a literal character. A double quote inside the outer pair of any value sent quoted this way, or of a value you wrap in double quotes on any filter but a free-text search, fails as quote_in_filter_value
  • Country and language values are converted to the codes OpenAlex stores, here and in every other tool that takes filters: on a country field (a key ending in country_code, country_codes, or countries), an ISO 3166-1 alpha-3 code, numeric code, or English name — ISO's, or the one OpenAlex's own country facets display (Russia, Ivory Coast, Trinidad & Tobago) — becomes its alpha-2 code (DEU, 276, Germany → DE) and UK becomes GB, while a value that cannot be a country code (Germanyy, 999, unknown, or two codes joined by a space, DE US) fails as invalid_country_code without the filter being sent; on language, an ISO 639-2 code becomes its ISO 639-1 code and an English name becomes its code (eng, English → en; Cebuano → ceb). Each |/+ segment is trimmed and converted on its own, so a multi-word name is one value (South Korea|Japan → KR|JP, Scottish Gaelic|English → gd|en), and anything else — null, XK, ceb, zh-cn, the countries/DEU path form — is sent as given
  • select returns a curated per-entity-type default unless overridden, or ["*"] for the full record; invalid field names error with the valid set. On works, author_list returns each author in authorship order as {id, display_name, orcid} — about a tenth the size of authorships, which adds institutions and affiliations — and is never part of the default or ["*"]
  • Cursor pagination for keyword and exact search, up to 100 results per page (default 25); semantic search walks its candidates with page (1-based) instead, and mixing either knob with the wrong search_mode is rejected before the upstream call
  • sample (up to 100, single page only — neither cursor nor page applies) plus a deterministic seed for reproducible random sampling; keyword and exact modes only, and never alongside sort — OpenAlex does not sample a semantic search and refuses a sorted sample, so both combinations are rejected before the upstream call
  • Each response holds to 64,000 bytes per surface (structuredContent, and content[] with its trailer) unless the least it can return is larger, which it flags as over_budget: a page that would run over keeps its leading records whole and names the rest in omitted with the one call that returns them (as a set, in OpenAlex's order), without disturbing next_cursor; a record too large on its own comes back with its largest arrays windowed, filled in turn so each shows what fits and labeled with how much of the array it holds, and slice: {field, offset} on an id lookup pages any array to the end
  • A list record carrying exactly 100 authorships or author_list entries — the most OpenAlex returns in a list response — is flagged as possibly capped, with the unbilled id + slice call that fetches the rest of that array
  • display_name is nullable for untitled records; every call reports OpenAlex daily-budget cost and remaining balance

openalex_analyze_trends tool

  • Group any supported field for trend, distribution, or comparative analysis; combine with filters to scope the population before aggregation
  • Up to 200 groups per page (default). order: "count" (default) returns the top-N by count with no further pages; order: "key" enumerates all distinct values key-ascending with cursor pagination
  • include_unknown (default false) adds a group for entities with no value for the grouped field, flagged is_unknown: true and labeled as unknown in the text output. OpenAlex keys it -111/-111.0 (numeric fields), unknown (text fields and order: "key"), or an ID ending in /unknown; the key is a sentinel, not a filter value. Boolean fields have no such group — a missing value counts as false
  • Not every field is groupable — raw date fields, .search operators, from_*/to_* range modifiers, decimal scores such as fwci, display_name, external-ID fields such as doi, and fields OpenAlex looks up from a related entity such as primary_location.source.country_code are rejected; openalex_describe_fields(entity_type, "group_by") lists the fields that group
  • A works group_by on authorships.countries says in its notice that OpenAlex counts only each work's first 100 authorships for that field, and names authorships.institutions.country_code as the field that counts all of them
  • Reports OpenAlex daily-budget cost and remaining balance — aggregation is priced far below paging the same entities

openalex_resolve_name tool

  • A name or partial name runs an autocomplete search: up to 10 matches with disambiguation hints (last institution, host organization, place, etc.)
  • An identifier — OpenAlex ID, DOI, ORCID, ROR, PMID, ISSN, or keyword URL, bare or in URL form — resolves directly to the record it addresses, as the entity type it belongs to. A ROR can name an organization's institution, funder, and publisher records at once: with no entity_type (or any other type) all three are looked up in parallel as unbilled singleton fetches and every record found is returned, institution first; entity_type institutions, funders, or publishers narrows it to that one. A Crossref Funder Registry DOI (10.13039/…) resolves to the funder unless entity_type is works. A PMCID is recognized but resolves nothing — OpenAlex indexes none
  • Short names share the bare OpenAlex ID shape (keyword p53 reads as publisher P53), so a bare OpenAlex ID that resolves nothing is searched as a name next, under entity_type when given, and the notice names both outcomes; a real ID still resolves with its one request. Under entity_type: "keywords" the shape is searched as a keyword name from the start
  • filters narrows autocomplete only and requires entity_type — a name query passing filters without it is rejected before any request (filters_without_entity_type). On an identifier lookup they're ignored and named in a notice; a name search that matches nothing under filters echoes them and suggests loosening them first
  • With entity_type set, OpenAlex autocomplete fails on a name over 1,000 characters; that failure is reported once as query_too_long with advice to shorten the name, not retried as an outage
  • Reports OpenAlex daily-budget cost and remaining balance

openalex_get_citation_graph tool

  • direction sets the edge: cites (works citing the seed), cited_by (the seed's own reference list), related_to (OpenAlex's algorithmic related works, ~8-30 typical, may be empty)
  • seed_id accepts an OpenAlex work ID (bare, or its OpenAlex record-page or API URL), DOI, or PMID (PMCID recognized but resolves nothing); validated against a live lookup first, so a non-existent seed fails as NotFound rather than returning an empty graph, and the ID of another entity type fails as id_type_mismatch before any upstream call
  • Stacks with filters/sort/select (author_list included) to narrow the graph; filters cannot set cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are reserved for direction
  • Cursor pagination, up to 100 results per page (default 25)
  • The same 64,000-byte response budget and 100-authorship cap disclosure as openalex_search_entities; works cut from a page are named in omitted with the openalex_search_entities call that returns them, in OpenAlex's order
  • Reports OpenAlex daily-budget cost, covering both the seed-validation lookup and the graph page, plus remaining balance

openalex_describe_fields tool

  • Lists every valid field name for an entity type + context (filter, group_by, select) — the complete pool, never truncated
  • group_by is the filter set minus what OpenAlex rejects as an aggregation key: raw date fields, .search/.search.exact operators, from_*/to_* range modifiers, and a per-entity-type set found by grouping every listed field against the live API — decimal scores such as fwci, several source year fields, display_name, and external-ID fields among them
  • created_date, updated_date, and their from_/to_ forms are listed filters that OpenAlex serves only on a paid plan; without one, a filter on them fails on its first request as upstream_plan_required, with no retry
  • Optional query reorders results by name similarity without dropping any field — a nested value's parent object stays reachable further down the list
  • Backed by a generated field catalog — no live API calls

openalex_literature_review prompt

  • Arguments: topic required; scope (narrow / broad) optional, defaults to narrow
  • Returns one user message walking a 6-step workflow: resolve entities, search literature, identify key papers, trace citations, analyze the landscape, synthesize findings
  • scope changes the search step: narrow favors exact search with tight topic filters; broad adds semantic search across multiple related topic IDs

openalex_research_landscape prompt

  • Arguments: topic required
  • Returns one user message walking a 7-step quantitative workflow: resolve the topic ID, volume trends, top contributors (institutions/countries/journals), open access rate, funding sources, most-cited works, emerging fronts
  • The funding step groups by funders.id, which counts every work that lists a funder and labels each group with the funder's name; awards.* fields stay the route for grant-level questions such as award numbers

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

OpenAlex-specific:

  • Typed API client with automatic ID normalization (DOI, ORCID, ROR, PMID, PMCID, ISSN, OpenAlex record-page and API URLs over http, https, or no scheme, PubMed/PubMed Central URLs); a PMCID normalizes but resolves nothing since OpenAlex indexes none
  • Keyless by default — an optional API key raises rate and daily-budget limits, and an optional mailto identifies the caller to OpenAlex's polite pool
  • HTTP status codes mapped to specific MCP error classes (400 → InvalidParams, or ServiceUnavailable when a semantic search's query embedding failed upstream; 422 → ValidationError; 429 → RateLimited, or Forbidden when the 429 refuses a paid-plan filter) with upstream messages surfaced
  • Timeout-aware request retries and cancellation support via AbortSignal

Agent-friendly output:

  • Provenance — every API-calling tool reports OpenAlex daily-budget cost, remaining balance, and reset time (budget.costUsd, remainingUsd, resetsInSeconds)
  • Effective-query echo — search, trends, and citation-graph responses echo the criteria that actually ran, so an empty result is diagnosable without re-reading the request
  • Discriminated output contracts — typed error reasons (entity_not_found, upstream_budget_exhausted, semantic_per_page_cap, reserved_filter_key, and more) each carrying an explicit recovery hint
  • Response shaping — abstracts are reconstructed from OpenAlex's inverted-index encoding into plaintext, and display_name stays null for untitled or paratext records instead of being backfilled
  • Clean provider text — OpenAlex passes titles, abstracts, and names through unsanitized; HTML entities are decoded against the full WHATWG table, comments and HTML/JATS/MathML formatting tags are removed, CDATA wrappers and IOP's <?CDATA …?> math markers are unwrapped with their TeX kept, MathType equation blobs (% MathType!MTEF!…!44A1!) are removed while the TeX that restates them stays, and a double-escaped line break (a literal \n) becomes a space; literal text such as A < B, 95% CI, and TeX such as \nu is kept. Identifier and URL fields (id, doi, ids, orcid, ror, issn, *_url, lineages) are returned exactly as OpenAlex stores them, so each one resolves when passed back. The text output escapes that text for Markdown, so a stray *, <tag>, or [x](y) renders as written; IDs, and URLs that render as links, stay byte-identical

Getting started

Public Hosted Instance

A public instance is available at https://openalex.caseyjhand.com/mcp — no installation required. Point an MCP client that supports remote servers at it via Streamable HTTP (Claude Desktop adds remote servers under Customize > Connectors > Add custom connector; its claude_desktop_config.json holds local servers only):

{
  "mcpServers": {
    "openalex-mcp-server": {
      "type": "streamable-http",
      "url": "https://openalex.caseyjhand.com/mcp"
    }
  }
}

Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "openalex-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/openalex-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OPENALEX_API_KEY": "your-api-key"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "openalex-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/openalex-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OPENALEX_API_KEY": "your-api-key"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "openalex-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT_TYPE=stdio",
        "-e", "OPENALEX_API_KEY=your-api-key",
        "ghcr.io/cyanheads/openalex-mcp-server:latest"
      ]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 OPENALEX_API_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcp

OPENALEX_API_KEY is optional — set it to a free OpenAlex account key for keyed rate limits and budget under OpenAlex's usage-based pricing, or omit it for anonymous access. Set OPENALEX_MAILTO to an email if you want to identify yourself to OpenAlex (the polite pool).

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • Optional: an OpenAlex account API key for keyed rate limits and budget — omit for anonymous access.

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/openalex-mcp-server.git
  1. Navigate into the directory:
cd openalex-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env and set required vars

Configuration

VariableDescriptionDefault
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTPort for HTTP server.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto (resolves to stateful). The server declares stateless in code; an explicit value overrides it.stateless
MCP_AUTH_MODEAuth mode: none, jwt, or oauth.none
MCP_ALLOWED_ORIGINSComma-separated allow-list of browser Origin headers for HTTP transport. Unset = loopback-only; set to * to disable.loopback only
MCP_LOG_LEVELLog level (RFC 5424).debug
LOGS_DIRDirectory for log files (Node.js only).<project-root>/logs
STORAGE_PROVIDER_TYPEStorage backend.in-memory
OPENALEX_API_KEYOpenAlex account API key, sent upstream as api_key= (free from openalex.org/settings/api). Without it, anonymous rate limits apply.—
OPENALEX_MAILTOEmail sent upstream as mailto= to identify yourself to OpenAlex (the "polite pool"); a courtesy identifier, separate from the API key.—
OPENALEX_BASE_URLOpenAlex API base URL.https://api.openalex.org
OTEL_ENABLEDEnable OpenTelemetry instrumentation (spans, metrics, completion logs).false

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    bun run devcheck   # Lints, formats, type-checks
    bun run test       # Runs the test suite
    

Docker

docker build -t openalex-mcp-server .
docker run --rm -e OPENALEX_API_KEY=your-key -p 3010:3010 openalex-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/openalex-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools and prompts.
src/config/Server-specific environment variable parsing and validation with Zod.
src/mcp-server/tools/definitions/Tool definitions (*.tool.ts).
src/mcp-server/prompts/definitions/Prompt definitions (*.prompt.ts).
src/services/openalex/OpenAlex API client, field catalog, and domain types.
tests/Unit and integration tests, mirroring the src/ structure.

Development guide

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

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for storage
  • Wrap OpenAlex responses: validate the raw payload → normalize to a domain type → return the output schema; never fabricate missing fields
  • Always resolve names to IDs via openalex_resolve_name before filtering by entity

Contributing

Issues are welcome. Run checks before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Featured
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
Lid closed, agents working
Lid closed, agents working
Keeps your Mac awake while Claude Code, Codex or Cursor works. Lets it sleep when they are done. Free and open source.
Download free →
Open Steps
Open Steps
Free an open-source skills that make AI coding agents easier to understand, verify, and control.
Download for free →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Block distraction from your iPhone for freeBlock distraction from your iPhone for free
Block distraction from your iPhone for free
Block distracting apps from your iPhone permanently without a 3rd party app. Free and open source.
Block now (100% free) →
Granola, the best AI meeting recorder
Granola, the best AI meeting recorder
Notes, actions and memory. Without a meeting bot. First month 100% off.
Download for free →
Devtools for design engineers
Devtools for design engineers
Test dark mode, contrast, motion, text size, locale and time zone from one panel on your dev server. Free and open source.
Try it live →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →

Configuration

OPENALEX_API_KEY

OpenAlex API key or mailto email — optional. Without: anonymous rate limits. With: polite pool access.

OPENALEX_BASE_URLdefault: https://api.openalex.org

OpenAlex API base URL.

MCP_LOG_LEVELdefault: info

Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').

MCP_HTTP_HOSTdefault: 127.0.0.1

The hostname for the HTTP server.

MCP_HTTP_PORTdefault: 3010

The port to run the HTTP server on.

MCP_HTTP_ENDPOINT_PATHdefault: /mcp

The endpoint path for the MCP server.

MCP_AUTH_MODEdefault: none

Authentication mode to use: 'none', 'jwt', or 'oauth'.

Registryactive
Package@cyanheads/openalex-mcp-server
TransportSTDIO, HTTP
Prompts2
Tools verifiedJun 10, 2026
UpdatedJun 8, 2026
View on GitHub

More from cyanheads

  • Congressgov Mcp Server1
  • Calculator Mcp Server1
  • Nws Weather Mcp Server1
  • Nhtsa Vehicle Safety Mcp Server1
  • Obsidian Mcp Server582
  • Hn Mcp Server3
  • Git Mcp Server221
  • Secedgar Mcp Server5
  • Pubchem Mcp Server9
  • Cdc Health Mcp Server3
  • Met Museum Mcp Server3
  • Mcp Ts Core145
  • Courtlistener Mcp Server2
  • Open Meteo Mcp Server2
  • Treasury Fiscaldata Mcp Server2
  • Usaspending Mcp Server2
  • Worldbank Mcp Server2
  • Bls Labor Mcp Server1
  • Cpsc Recalls Mcp Server1
  • Devops Status Mcp Server1
  • Earthquake Mcp Server1
  • Eia Energy Mcp Server1
  • Eurostat Mcp Server1
  • Fcc Broadband Mcp Server1