Back to Directory/Developer Tools

io.github.cyanheads/openalex-mcp-server

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

Developer ToolsTypeScriptv0.8.0

@cyanheads/openalex-mcp-server

Access the OpenAlex academic research catalog - 270M+ publications through MCP. STDIO & 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. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.

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), or a keyword by its slug or keyword URL. id takes precedence: search parameters passed alongside it are dropped, and the response names which ones. 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 at most 50 candidates at ~1 req/sec, and its meta.count reports that ceiling rather than a match total
  • Rich filter syntax: AND across fields, OR within a field (|), NOT (!), ranges, comparisons; a comma inside a filter value is rejected (use |, or a .search filter for free text)
  • select returns a curated per-entity-type default unless overridden, or ["*"] for the full record; invalid field names error with the valid set
  • 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
  • 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
  • Not every field is groupable — raw date fields, .search operators, and from_*/to_* range modifiers are rejected; check with openalex_describe_fields(entity_type, "group_by")
  • 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 one record it addresses; no entity_type needed, since the identifier determines its own. A PMCID is recognized but resolves nothing — OpenAlex indexes none
  • filters narrows autocomplete only; on an identifier lookup they're ignored and named in a notice
  • 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 ID, 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
  • Stacks with filters/sort/select 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)
  • 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 raw date fields, .search/.search.exact operators, and from_*/to_* range modifiers, which OpenAlex rejects as aggregation keys
  • 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 awards.funder_id (resolve names via openalex_resolve_name) or awards.funder_display_name for readable labels in a single hop

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 and 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, 422 → ValidationError, 429 → RateLimited) 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

Getting started

Public Hosted Instance

A public instance is available at https://openalex.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "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

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.

Installation

Source-derived launch command. Check the maintainer’s required arguments and credentials before running:

bash
npx -y @cyanheads/openalex-mcp-server

Set up in your AI client

Merge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.

json
{
  "mcpServers": {
    "io-github-cyanheads-openalex-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@cyanheads/openalex-mcp-server"
      ]
    }
  }
}

Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.

Claude Desktop setup reference

Package

@cyanheads/openalex-mcp-servernpm

Compatible MCP Clients

io.github.cyanheads/openalex-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More