crossref-mcp-server
Resolves DOIs to full Crossref metadata records, including titles, authors, affiliations, abstracts, journals, publication dates, licenses, full-text links, and funder acknowledgements.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@crossref-mcp-serverresolve DOI 10.1038/nature12373"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://crossref.caseyjhand.com/mcp
Overview
Scholarly metadata from the Crossref REST API. Resolve DOIs to full metadata records, search across ~155 million scholarly works by free text or structured filters, fetch outgoing reference lists, and look up journals, funders, and publishers. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Resolve a DOI to its full Crossref metadata record: title, authors, editors, affiliations, abstract, journal with volume/issue/pages, ISSNs and ISBNs, publication date, license, full-text links, funder acknowledgements, post-publication updates, and related identifiers |
| Search the Crossref works index by free text and/or structured filters, with field-scoped query parameters, sort, field selection, and offset or cursor-based paging |
| Return the outgoing reference list for a DOI — the works cited by this paper, with citation strings and resolved DOIs where available |
| Find Crossref journal records by ISSN or title query; optionally retrieve a page of the journal's most recent works |
| Find funders in the Crossref Funder Registry by name, registry ID, or funder DOI; optionally retrieve a page of funded works |
| Resolve a Crossref member ID to its publisher record — name, owned DOI prefixes, DOI counts, and per-category metadata deposit coverage |
| Resolve a DOI prefix (e.g. |
Related MCP server: CrossRef MCP Server
Capability reference
crossref_get_work tool
DOI validated against the
10.NNNN/suffixregex before the upstream call, accepted either bare or wrapped in its resolver (https://doi.org/…,https://dx.doi.org/…,doi:…) and unwrapped before the lookupReturns title, authors with affiliations, abstract (when deposited), container/journal, publication date, work type, ISSN, license URLs, full-text link URLs, and funder acknowledgements
Citation locators
volume,issue,page, andarticleNumberunder the same names and on the same rendered line ascrossref_search_works, plusisbn— each as deposited, omitted when absenteditorsin the author entry shape, returned whole and never counted inauthorCountAuthor list paged by
offset/limit(default 25, max 500);authorCountreports the full deposited total and anextOffsetcontinues when authors remain — every other field is returned in full on every pageupdatedBylists the corrections, retractions, expressions of concern, and new versions Crossref records against the work — notice DOI, type, source (publisherorretraction-watch, with its record ID), and date, entry for entry — andupdateTothe works this record is a notice for; whenupdatedByis present,noticenames each update type with its sources. An absentupdatedBydoes not mean the work was never updatedrelationsgroups related identifiers (preprint ↔ published version, versions, reviews, supplements) by relation type, identifier type, and asserting party, returned whole; only Crossref-registered DOIs among them resolve through this toolA funder or affiliation asserted only through the ROR registry (no name deposited) carries
rorin place ofname, never as a blank entryPublication date is the first of
published,published-print,published-online, andissued(this tool only) that names a value; a date component Crossref records as unknown is omitted, along with everything less precise below itOutgoing references are reported as a count (
referencesCount) — entries come fromcrossref_get_references; incoming citation count (isReferencedByCount) is included, but citing works are not exposed by Crossref — use OpenAlex for citation graphs
crossref_search_works tool
Free-text
queryplus a structuredfilterobject using Crossref's hyphenated keys (from-pub-date,type,funder,issn,has-abstract,license.url, etc.)Field-scoped parameters
queryTitle,queryAuthor,queryContainerTitle, andqueryBibliographiccombine withqueryand with each otherSort by
relevance,score,is-referenced-by-count,published,deposited, or other listed fields — the publication-date sorts page byoffsetonly, since Crossref refuses them with a cursorfieldsnarrows the payload to any of the 17 select names the summary projects — including the citation locatorsvolume,issue,page,article-number, andISSN— and refuses any other name;DOIis always returned, andcrossref_get_workcarries the rest of the recordA blank or whitespace-only query term or filter value, and an empty
sortororder, is read as omitted; when that leaves nothing to search by, thenoticesays the page is an unfiltered listingauthorLimitcaps authors per work (default 25, max 500);authorCountreports the full deposited total — chain a cut work's DOI intocrossref_get_workfor the restOffset paging is capped at ~10K;
cursor="*"starts deep paging via chainednextCursortokens — cursor and offset cannot be combinedA cursor walk ends on the page that omits
nextCursor— an empty page never carries one; every empty page'snoticenames which of the three causes applies
crossref_get_references tool
Takes the citing work's DOI bare or resolver-wrapped, the same forms
crossref_get_workacceptsEach reference carries its deposited citation string and, when Crossref has resolved it, a DOI for
crossref_get_workCitation strings have formatting markup stripped and character references decoded; a bracketed span that isn't a recognized tag (a cited URL, a Miller index, a DOI fragment) is left exactly as deposited
Paged by
offset/limit(default 100, max 500);referenceCountis the full deposited total andnextOffsetcontinues when more remainCoverage varies by publisher — pre-2000 works and non-participating publishers often have no indexed references
Single-hop only; incoming citations are not available through Crossref — use OpenAlex for citation graphs
crossref_search_journals tool
include_works: truealso returns a page of the journal's most recent works — newest published first byworks_offset, newest registered first on aworks_cursorwalk; requires an unambiguous journal — a title query matching more than one returnsambiguous_journal, naming candidates and ISSNsReturns journal title, publisher, ISSN-L, subject areas, and total DOI count
Title-query results page by
offset(ceilingoffset + rows ≤ 100,000); the works list pages separately byworks_offset(ceiling≤ 10,000) — a page that stalls at either ceiling, or an offset past the end of either list, carries anoticenaming itworks_cursor="*"pages the works list with no ceiling via chainednextWorksCursortokens; a cursor walk runs by Crossref registration date, newest first (Crossref does not walk a publication-date sort by cursor), and cannot combine withworks_offset > 0(works_cursor_offset_conflict)A matched journal with no ISSN registered has no addressable works list —
include_worksis skipped with anoticerather than returning an empty list
crossref_search_funders tool
Accepts a name
query, a bare registry ID (100000001), or a full funder DOI (10.13039/100000001, optionally behind adoi:/https://doi.org/prefix)include_works: truealso returns a page of funded works; requires an unambiguous funder — a name query matching more than one returnsambiguous_funder, naming candidates and registry IDsReturns funder name, registry ID, country, and alternate names
Name-query results page by
offset(ceiling≤ 100,000); the funded-works list pages separately byworks_offset(ceiling≤ 10,000) or, with no ceiling,works_cursor="*"chainingnextWorksCursor— offset pages run newest published first, a cursor walk newest registered first, and the two cannot combine (works_offset > 0); an offset past the end of either list carries anoticenaming itThe funded-works list also counts works funded by the funder's registry descendants, which a
crossref_search_worksfilter on{"funder": "10.13039/<id>"}does notA deprecated registry entry answers to its successor's name while counting only its own works — the response's
noticenames the superseding ID viareplacedBy; the replacement is never followed automatically
crossref_get_member tool
Members are the organizations that register DOIs — this answers "what does this publisher publish, and how completely do they deposit metadata?"
Returns primary name, alternate imprint names, owned DOI prefixes, DOI counts (total/current/backfile), a per-work-type breakdown, and per-category metadata deposit coverage (references, abstracts, ORCIDs, funders, licenses, and more) as current/backfile fractions
Pair with
crossref_get_prefixto resolve a DOI prefix to the member ID first
crossref_get_prefix tool
Accepts the registrant prefix of a DOI (e.g.
10.1038, no/suffix)Returns the publisher name and numeric member ID — the ID chains directly into
crossref_get_memberfor the full recordThe Crossref prefix record is thin by design (owner name and member link only); richer publisher data lives on the member record
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.
Crossref-specific:
Polite-pool
User-Agentheader injected on every request — priority access viaCROSSREF_MAILTO, keyless otherwise; no API token requiredRetry with exponential backoff on 429 (honoring
Retry-After), 5xx, HTTP 408/504, and network failures; a malformed response body and a request that hitsCROSSREF_TIMEOUT_MSare not retriedCursor-based deep paging on the works search and on both works sub-resources, for result sets beyond the offset cap
Crossref's rejections of a request come back as declared reasons —
unknown_filter(with the hyphenated key when Crossref lists one),invalid_parameter,sort_cursor_conflict,invalid_cursor— carrying the rejected inputs and a recovery hint; a malformedissnfilter value is refused before the request, and no upstream error relays Crossref's raw response bodyText normalization on every human-readable value: HTML character references decoded and whitespace collapsed; citation strings additionally have formatting markup stripped, so titles and abstracts read as plain text instead of raw JATS XML; a MathML formula keeps its structure — the TeX annotation where the deposit carries one, otherwise a linear form such as
x_i,A^{−1},√(m),(a+b)/c
Agent-friendly output:
Provenance — identifiers, URLs, and dates are returned byte-exact while human-readable text is normalized, so a caller can trust
doi,issn, and date fields without re-verificationGraceful partial failure — an empty page is a success, not an error: an offset past the end of a list, or a query nothing matched, carries a
noticesaying which, and so does the page that ends acrossref_search_workscursor walk; a journal or funder works walk ends on the page that omitsnextWorksCursorDiscriminated output contracts — the
nextCursor,nextOffset,nextWorksOffset, andnextWorksCursorcontinuation fields are withheld once a list is exhausted, and a page that stops at an offset ceiling says so in itsnotice; a cursor token can still ride the last partial page, and the empty page after it carries noneAmbiguity handled explicitly —
ambiguous_journalandambiguous_funderlist every candidate and its identifier in the error data rather than silently resolving to the first match
Getting started
Public Hosted Instance
A public instance is available at https://crossref.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"crossref-mcp-server": {
"type": "streamable-http",
"url": "https://crossref.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file. CROSSREF_MAILTO is optional but recommended — without it the server uses Crossref's anonymous pool with stricter rate limits.
{
"mcpServers": {
"crossref-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/crossref-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"CROSSREF_MAILTO": "your-email@example.com"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"crossref-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/crossref-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"CROSSREF_MAILTO": "your-email@example.com"
}
}
}
}Or with Docker:
{
"mcpServers": {
"crossref-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "CROSSREF_MAILTO=your-email@example.com",
"ghcr.io/cyanheads/crossref-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 CROSSREF_MAILTO=your-email@example.com bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
An email address for
CROSSREF_MAILTOis optional but recommended — Crossref's polite pool grants priority access to clients that identify themselves. No account or token is required.
Installation
Clone the repository:
git clone https://github.com/cyanheads/crossref-mcp-server.gitNavigate into the directory:
cd crossref-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and optionally set CROSSREF_MAILTO for polite-pool accessConfiguration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts.
Variable | Description | Default |
| Email address embedded in the polite-pool | — |
| Crossref API base URL. Override for testing against a local proxy. |
|
| Per-request timeout in milliseconds. Also the worst-case wait against an unresponsive upstream — a request that hits the deadline is not retried. |
|
| Transport: |
|
| HTTP session mode: |
|
| Port for the HTTP server. |
|
| Auth mode: |
|
| Log level (RFC 5424). |
|
| Directory for log files (Node.js only). |
|
| Enable OpenTelemetry instrumentation. |
|
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:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| CrossrefService — HTTP client, polite-pool header, retry, pagination helpers. |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools via the barrel in
src/mcp-server/tools/definitions/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields (abstracts, reference lists, and affiliations are frequently absent in Crossref records)
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
Search 150M+ academic works, journals, and funders via Crossref API.
Crossref MCP — wraps the Crossref REST API (academic papers, free, no auth)
Free scholarly paper and DOI discovery via Crossref. Self-service registration; no invitation.
Multi-engine scholarly research server for search, traversal, full text, and reading lists.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables retrieval of academic literature metadata via DOI or search using the Crossref REST API.2MIT
- AlicenseNot gradedqualityDmaintenanceSearches and retrieves scholarly metadata from the CrossRef REST API, covering over 150 million records across all disciplines, without requiring an API key.23 PyPIMIT
- FlicenseAqualityBmaintenanceEnables searching and retrieving scholarly metadata from Crossref's public REST API, including works, journals, funders, and members, without requiring an API key.101-
- AlicenseAqualityCmaintenanceEnables scholarly metadata lookups from the Crossref REST API, including works, members, journals, funders, types, licenses, and prefixes, as tools for LLM clients.1825 PyPIMIT