Skip to main content
Glama
README.md
# OpenAlex MCP

An MCP server that lets Claude search [OpenAlex](https://openalex.org): 250M+ scholarly
works, plus authors, institutions, journals, funders and research grants.

Packaged as a one-click Claude Desktop extension. Read-only, no writes to anything.

## Tools

| Tool | Purpose |
| --- | --- |
| `search_works` | Search by topic, author, institution, journal, funder, award number, year range, citation thresholds, retraction status. Optionally returns grouped counts as a chart or CSV. |
| `count_by_year` | Publication counts per year via `group_by`, one call regardless of corpus size. |
| `lookup_works` | Up to 100 DOIs per call, with citation counts, FWCI, OA status and retraction warnings. For auditing a reference list. |
| `search_awards` | Grants as first-class records: funder, award number, amount, duration, scheme, resulting publications. |
| `get_work` | Full record for one work including the reconstructed abstract. Free call. |
| `resolve_entity` | Name to OpenAlex ID for authors, funders, institutions, topics and journals. |

Names are resolved to IDs automatically, since OpenAlex rejects filtering by name, and
every result set echoes which entity was chosen along with the alternatives.

## Install

1. Get a free API key at <https://openalex.org/settings/api>. Keys have been mandatory
   since 13 February 2026.
2. Download `openalex-search.mcpb` from
   [Releases](../../releases).
3. Claude Desktop: Settings, Extensions, Advanced settings, Extension Developer,
   Install Extension.
4. Paste the key. Optionally set your email, a daily spend cap and call logging.
5. Restart Claude Desktop.

If an older version is already installed, uninstall it first. A stale registration can
keep serving the previous server while the new bundle appears installed.

## Design notes

**Cost.** OpenAlex bills per call: singleton free, list $0.0001, keyword search $0.001,
semantic search far higher. A key includes $1.00 per day free. The server tracks actual
spend from `meta.cost_usd`, enforces a rolling daily cap (default $0.25) with a
pre-flight check so a blocked call costs nothing, and appends one CSV row per call to
`~/.openalex-mcp/calls.csv`. That log is also how the cap survives a restart.

**Context.** Tool schemas sit in the model's context on every turn, so they are kept
terse and results are compact by default. Roughly 1.2k tokens of schema and ~45 tokens
per result. `detail: "full"` restores verbose output per call.

**Permissions.** All six tools declare `readOnlyHint: true` and
`destructiveHint: false`. Servers that ship no annotations get worst-case defaults
applied, which is why unannotated servers prompt for approval on every tool.

**Robustness.** Timeouts span both the header exchange and the body read. An earlier
version cleared the abort timer once headers arrived, which let a stalled body hang
indefinitely. A select field the API rejects is dropped, the query retried and the
field remembered, so a schema rename degrades rather than breaking every query.

## Safety posture

The published build is the one that gets tested. There is no separate "unsafe"
variant, and the internal build differs only in configuration defaults.

- **Spend cap on by default**, $0.25/day, checked before each call so a blocked
  request costs nothing. Removing it requires typing the literal word `unlimited`;
  a plain `0`, an empty value or a typo falls back to the default rather than
  silently disabling the guard. While disabled, every response says so.
- **API key redaction.** The key travels as a query parameter, so it can surface
  inside errors produced by the HTTP stack. Everything reaching the model or the
  log is redacted first. Tested with a mock that echoes the full URL into an error.
- **No silent degradation.** If OpenAlex rejects a field, the server drops it and
  retries so the query still works, then reports what was dropped. A quiet drop is
  how a schema change stays invisible until it breaks something else.
- **Read-only.** Every tool is HTTP GET only and annotated `readOnlyHint: true`.
- **Local logging only.** The call log stays in your home directory and is
  gitignored. It records endpoint, cost and filter string, never the key.
- **No HTTP server.** Transport is stdio only. `test/bundle.mjs` asserts the
  shipped bundle contains no `hono`, `serve-static` or `.listen(`, which is why
  advisories against the SDK's HTTP transport do not reach users. See
  [SECURITY.md](SECURITY.md) for the `npm audit` assessment.

### Public and internal builds

```bash
npm run pack            # openalex-search.mcpb          public defaults
npm run pack:internal   # openalex-search-internal.mcpb group defaults
```

Both bundle the same `server/index.js`. Only `manifest.json` and
`manifest.internal.json` differ, in default budget and prefilled contact email.
`test/manifests.mjs` fails the build if the server block, version or tool list
ever diverge, because two codebases would mean fixing every bug twice while CI
covers only one of them.

## Testing

```bash
npm test                                   # mock suites, free, ~40s
node smoke-live.mjs YOUR_KEY               # real API contract, ~$0.02
node stress-live.mjs --key YOUR_KEY        # full server end to end, ~$0.05
node stress-live.mjs --key YOUR_KEY --tier full   # adds soak and batch, ~$0.15
```

**Mock tests alone are not sufficient, and this is not a theoretical concern.**
OpenAlex removed the `grants` field from works in its Walden rewrite. Every mock suite
stayed green and the extension returned HTTP 400 on every search in production, because
the mocks encoded the schema as it used to be. `smoke-live.mjs` tests each select field
individually against the live API for exactly this reason, and the scheduled workflow in
`.github/workflows/live-check.yml` runs it weekly.

`stress-live.mjs` additionally checks cross-tool invariants that a single-tool test
cannot: that `search_works` and `count_by_year` report the same total for identical
filters, that yearly counts sum to that total, and that adding filters never increases
the result count.

A worked example of why live testing matters, from v2.7.0. OpenAlex offers two ways
to match free text: `search=` covers full text, while `filter=title_and_abstract.search:`
does not. For one probe they returned 29,261 and 16,654 works. `search_works` used the
first and `count_by_year` the second, so a year curve did not describe the same corpus
as the hit list. The mock parity test passed throughout, because it normalised the
free-text term away before comparing. Both tools now build the whole query through one
function and echo a canonical `Query:` line derived from the parameters actually sent,
and `test/parity.mjs` compares those lines verbatim.

## Relationship to OpenAlex

**This is an independent project. It is not affiliated with, endorsed by, or
supported by OpenAlex or OurResearch**, who build and operate OpenAlex. It is a
third-party client that calls their public API.

### Where to report what

| Problem | Where |
| --- | --- |
| A tool errors, returns nothing, hangs, or formats badly | [Issues in this repo](../../issues) |
| A paper is missing, an author record is split or merged wrongly, a citation count looks off, funding metadata is absent | <https://openalex.org/help> |
| API keys, credits, rate limits, billing | <https://openalex.org/help> |

Please do not send bugs in this client to OpenAlex. Their support handles a free
service used by the whole research community, and misrouted reports cost them time
that should go to the data. If you are unsure which side a problem sits on, open an
issue here and it can be triaged.

Data-quality caveats worth knowing before reporting anything as a bug are in
[Known limits](#known-limits); fragmented author records in particular are an
OpenAlex-side issue and are already documented upstream.

### Using the API responsibly

- **Bring your own key.** None is bundled. Get one free at
  <https://openalex.org/settings/api>.
- **Keep the spend cap.** It defaults to $0.25/day and exists to stop an agent loop
  hammering a free service. If you fork this, leave it on.
- **Keep an honest User-Agent.** The server identifies itself as
  `openalex-mcp/<version>`, with your email appended if you set one, so OpenAlex can
  see what is generating traffic and contact you if something misbehaves. If you fork
  and change behaviour, change the User-Agent string too, so problems from your fork
  are not attributed to this project or to anyone else's deployment.
- **Prefer the free calls.** `get_work` by DOI is a free singleton lookup. Use it
  instead of a search when you already have the identifier.

### Credit

OpenAlex data is released under CC0. If you use this tool in work you publish, cite
OpenAlex rather than this client:

> Priem, J., Piwowar, H., & Orr, R. (2022). OpenAlex: A fully-open index of scholarly
> works, authors, venues, institutions, and concepts. arXiv:2205.01833.
> <https://doi.org/10.48550/arXiv.2205.01833>

## Known limits

- **Author identity is fragmented.** OpenAlex may split one researcher across many
  author records. Searching by name silently undercounts; in one measured case a name
  query returned 16 works where the ORCID returned 25. Pass an ORCID whenever you have
  one. OpenAlex is rewriting author disambiguation during 2026.
- **Funding coverage is uneven.** Award data depends on funders and publishers
  depositing grant metadata. An empty award result is evidence about deposit practice,
  not about the publication record.
- Abstracts are reconstructed from an inverted index, so spacing and punctuation can be
  imperfect. The DOI is authoritative.

## Development

```bash
npm ci
npm test
npm run pack        # writes openalex-search.mcpb
```

The published artifact is the esbuild bundle in `dist/`, not `server/index.js`. Editing
the source without rebuilding ships nothing.

## Licence

MIT. See [LICENSE](LICENSE).

The MIT licence covers this client only. It grants no rights over OpenAlex, their
API, or their trademarks, and carries no warranty: if you deploy this and it
misbehaves against a third-party service, that is on you.

Data from [OpenAlex](https://openalex.org), released under CC0.