scholarly-mcp
An MCP server that lets an agent look references up instead of recalling them: search the
scholarly record, check that a citation is real and correctly described, audit a whole
bibliography, and spot retracted work. It reads four public services — Crossref, OpenAlex, arXiv
and doi.org — and needs no account.
The rule it enforces: a citation is verified when its DOI resolves and the registered title,
first author and year agree with what is cited. A DOI that merely exists verifies nothing; a real
DOI pasted onto the wrong paper is reported as a mismatch.
Tools
| Tool | What it does | Services |
|---|
verify_doi | DOI resolves and the record matches the citation | Crossref, doi.org, arXiv |
verify_bibliography | One status per entry of a .bib file, BibTeX text or reference list | Crossref, doi.org, arXiv |
resolve_reference | Finds the record of a reference without DOI, or says there is none | Crossref, arXiv |
check_retractions | Retractions, expressions of concern, corrections for up to 200 DOIs | Crossref, OpenAlex |
search_works | Search by text, title, author, year, type | Crossref (default) or OpenAlex |
get_work | Registered metadata of one work | Crossref, doi.org, arXiv, OpenAlex |
search_arxiv | Search or look up preprints | arXiv |
get_citations | Works citing a work | OpenAlex |
get_references | What a work cites | Crossref, OpenAlex |
related_works | Works related by shared concepts | OpenAlex |
author_works | Works of an author by ORCID, id or name | OpenAlex |
get_open_access | Legal open copies of a work | OpenAlex, arXiv |
format_citation | APA, Chicago, MLA, BibTeX, RIS, CSL-JSON from registry metadata | as get_work |
bibtex_from_doi | BibTeX from the registration agency, or generated | doi.org, Crossref |
parse_bibtex | Offline: entries, missing DOIs, duplicate keys, syntax errors | none |
scholarly_status | Configuration, counters, OpenAlex budget left | none |
Every tool is read-only. Statuses returned by the verification tools: verified,
doi_missing_found, metadata_mismatch, doi_not_found, doi_invalid, ambiguous,
not_found, retracted, unverifiable, error. A registry outage is reported as error,
never as not_found.
Install
Node.js 20 or newer.
npm install
node server.mjs
Claude Code:
claude mcp add scholarly -- node /absolute/path/to/scholarly-mcp/server.mjs
Any MCP client:
{ "mcpServers": { "scholarly": { "command": "node", "args": ["/absolute/path/to/scholarly-mcp/server.mjs"], "env": { "SCHOLARLY_ROOT": "/path/to/your/papers" } } } }
Configuration
| Variable | Default | Effect |
|---|
SCHOLARLY_ROOT | working directory | Only directory bibliography files may be read from |
OPENALEX_API_KEY | unset | Your own OpenAlex key, for a larger allowance on OpenAlex-backed tools |
SCHOLARLY_MAILTO | unset | Contact address sent to Crossref and OpenAlex ("polite pool") |
SCHOLARLY_TIMEOUT_MS | 20000 | Per-request timeout |
What it sends, and what it does not
- Each request carries the client name
scholarly-mcp/1.0 and the query itself (a DOI, a title,
an author name). Nothing about the user, the machine or the project is added.
- No contact address is sent unless you set
SCHOLARLY_MAILTO. It then goes to Crossref and
OpenAlex only.
OPENALEX_API_KEY is sent to OpenAlex only and never appears in tool output.
- Files: only
.bib, .bibtex, .txt and .md inside SCHOLARLY_ROOT, 5 MB at most,
symbolic links resolved before the check. The server writes nothing.
- Registry text (titles, abstracts, notices) is returned as data with a notice that it is
untrusted. That reduces the risk of prompt injection through a crafted title; it does not
remove it.
Limits you should know
- OpenAlex without a key shares a small daily allowance among everyone behind the same IP
address (0.10 USD on 2026-11-10: roughly a hundred list calls). When it is spent, the
OpenAlex-backed tools return
rate_limited. Verification tools do not depend on OpenAlex.
- Coverage. Crossref and arXiv do not list most books, theses, reports and working papers,
nor many regional journals.
not_found means "not in these registries".
- Heuristics. Matching rests on title similarity (Sørensen-Dice on normalised words, 0.9 to
match, 0.75 to be "close"), family-name comparison and year. Thresholds are in
lib/match.mjs; every verdict includes the compared values.
- Registry errors. Crossref metadata is sometimes wrong. A mismatch says the citation and
the registry disagree, not which one is right.
- Retractions come from Crossref update notices and the OpenAlex flag. Both lag and both
have gaps:
no_notice is not a guarantee.
- Open access.
get_open_access reports authorised copies only.
- arXiv allows one request every three seconds; the server queues accordingly, so a
bibliography with many arXiv-only entries is slow.
- It cannot check that a cited work supports the claim citing it.
Tests
npm test
npm run test:live
Live tests need the network and no account. The OpenAlex test skips when the key-less budget is
exhausted.
Checked once by hand on 2026-11-10 but not in the automated suite, to spare the shared OpenAlex
budget: related_works, author_works (by ORCID and by name), search_works with source
openalex, get_references with source openalex, get_open_access for a journal article and
a pmid: lookup. Each returned a plausible answer; a later change in OpenAlex would not be caught
by npm run test:live.
Not tested: bibliographies above a few dozen entries in one call; references in non-Latin
scripts; BibLaTeX constructs such as crossref inheritance; behaviour under a real OpenAlex
budget exhaustion (the rate_limited path is tested with a simulated HTTP 429 only).
Companion skill
@spboucher/citation-verification packages the same engine as a command-line tool with the
rules for reading its results.
MIT licence.