Skip to main content
Glama
cyanheads

@cyanheads/openalex-mcp-server

by cyanheads

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://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

Tool

Description

openalex_search_entities

Search, filter, sort, or retrieve by ID across all 8 entity types

openalex_analyze_trends

Group-by aggregation for trend and distribution analysis

openalex_resolve_name

Resolve a name or an identifier (DOI, ORCID, ROR, PMID, ISSN, OpenAlex ID) to an OpenAlex ID

openalex_get_citation_graph

Walk the citation graph one hop from a seed work: cites, cited_by, or related_to

openalex_describe_fields

List valid filter, group_by, and select field names for an entity type

Prompts

Prompt

Description

openalex_literature_review

Guides a systematic literature search: formulate query, search, filter, analyze citation network, synthesize findings

openalex_research_landscape

Analyzes the research landscape for a topic: volume trends, top authors/institutions, open access rates, funding sources

Related MCP server: mcp-openalex

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). 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 caps at 50 results per page and ~1 req/sec

  • 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, up to 100 results per page (default 25); sample (up to 100, single page only, no cursor) 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


  • 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, or ISSN, 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 directly — those 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

Variable

Description

Default

MCP_TRANSPORT_TYPE

Transport: stdio or http.

stdio

MCP_HTTP_PORT

Port for HTTP server.

3010

MCP_SESSION_MODE

HTTP session mode: stateless, stateful, or auto (resolves to stateful). The server declares stateless in code; an explicit value overrides it.

stateless

MCP_AUTH_MODE

Auth mode: none, jwt, or oauth.

none

MCP_ALLOWED_ORIGINS

Comma-separated allow-list of browser Origin headers for HTTP transport. Unset = loopback-only; set to * to disable.

loopback only

MCP_LOG_LEVEL

Log level (RFC 5424).

debug

LOGS_DIR

Directory for log files (Node.js only).

<project-root>/logs

STORAGE_PROVIDER_TYPE

Storage backend.

in-memory

OPENALEX_API_KEY

OpenAlex account API key, sent upstream as api_key= (free from openalex.org/settings/api). Without it, anonymous rate limits apply.

OPENALEX_MAILTO

Email sent upstream as mailto= to identify yourself to OpenAlex (the "polite pool"); a courtesy identifier, separate from the API key.

OPENALEX_BASE_URL

OpenAlex API base URL.

https://api.openalex.org

OTEL_ENABLED

Enable 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

Directory

Purpose

src/index.ts

createApp() 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.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    Provides access to OpenAlex's catalog of 240M+ scholarly works, enabling search and retrieval of research papers, authors, institutions, journals, concepts, and funders with advanced filtering and classification capabilities.
    11
    42 npm
    5
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for the OpenAlex scholarly database, providing AI agents with tools to search and retrieve academic works, authors, and institutions via natural language queries.
    8
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    A local MCP server for searching scientific papers, retrieving metadata and abstracts, and legally downloading Open Access PDFs via OpenAlex, CrossRef, and Unpaywall APIs.
    5
    3
    MIT