crossref-mcp-server
An MCP server that resolves DOIs to full metadata records, searches the Crossref works index, fetches outgoing reference lists, and looks up journals, funders, and publishers via the Crossref REST API.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://crossref.caseyjhand.com/mcpWhat its tools do
7 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Resolve a DOI to its full metadata record
- Search scholarly works by free text or filters
- Fetch outgoing reference lists for a paper
- Look up publisher records by member ID
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- crossref-mcp-server 0.5.1
- MCP protocol negotiated in check
- 2025-11-25
- Tools
- 7 advertised
- Resources
- 1 advertised
- Resource templates
- 1 advertised
- Prompts
- 0 advertised
{
"tools": {
"listChanged": true
},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"extensions": {
"io.modelcontextprotocol/skills": {
"directoryRead": true
}
}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
7 advertised
crossref_get_work — Get Work by DOI
Resolves a DOI to its full Crossref metadata record: title, authors, editors, affiliations, abstract (when deposited), journal or container with the volume, issue, pages, and article number that locate the work in it, ISSNs and ISBNs, publication date, type, license, full-text links, and funder acknowledgements. The author list is paged: authorCount is the full deposited total, offset and limit select the page (25 authors by default), and when authors remain the response carries a nextOffset to pass back as offset — large-collaboration papers deposit thousands. Post-publication updates are relayed as Crossref records them: updatedBy names each correction, retraction, expression of concern, or new version issued against this work, with its notice DOI and whether the publisher or Retraction Watch recorded it, and updateTo names the works this record is itself a notice for. An absent updatedBy does not mean the work was never updated — coverage depends on those deposits. relations lists related identifiers, such as a preprint and its published version, grouped by relation type; only the Crossref-registered DOIs among them resolve through crossref_get_work. memberId is the Crossref member that holds the DOI now — pass it to crossref_get_member for the publisher record and its deposit coverage; the prefix of the DOI names the member that registered the prefix, which for a transferred title is a former publisher. Outgoing references are reported as a count in referencesCount; the reference entries themselves come from crossref_get_references. The isReferencedByCount field reports the total incoming citation count from Crossref; the citing works themselves are not available through Crossref — use OpenAlex for citation graphs.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"doi"
],
"properties": {
"doi": {
"type": "string",
"pattern": "^(?:https?:\\/\\/(?:dx\\.)?doi\\.org\\/|doi:)?10\\.\\d{4,9}\\/\\S+$",
"description": "DOI in the format \"10.NNNN/suffix\", e.g. \"10.1038/nature12373\". A resolver-wrapped form — \"https://doi.org/10.1038/nature12373\", \"https://dx.doi.org/…\", \"doi:10.1038/nature12373\" — is accepted and unwrapped."
},
"limit": {
"type": "integer",
"default": 25,
"maximum": 100000,
"minimum": 1,
"description": "Maximum number of authors to return in one page (1–10000 per call, default 25). A larger value, up to 100000, returns 10000 and says so in notice; nextOffset continues after the authors returned. Ordinary records fit in a single page; large-collaboration papers in particle physics and genomics deposit thousands."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based index of the first author to return. Pass the nextOffset value from the previous response to continue through a long author list. Only the author list is paged; every other field of the record is returned in full on every page."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"doi"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The limit that was applied to this page of authors."
},
"doi": {
"type": "string",
"description": "Canonical DOI"
},
"url": {
"type": "string",
"description": "DOI resolution URL"
},
"isbn": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISBN(s) as deposited — for a book chapter, those of the containing book. Omitted when the record deposits none."
},
"issn": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISSN(s) of the containing journal"
},
"page": {
"type": "string",
"description": "Page range as deposited, e.g. \"357-362\""
},
"type": {
"type": "string",
"description": "Work type (e.g. journal-article, book-chapter, posted-content)"
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"doi_not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `doi_not_found`: Valid DOI format but no Crossref record exists. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"issue": {
"type": "string",
"description": "Issue of the container the work appears in"
},
"links": {
"type": "array",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "Full-text URL"
},
"contentType": {
"type": "string",
"description": "MIME type of linked content"
},
"contentVersion": {
"type": "string",
"description": "Content version of the linked manuscript as deposited (e.g. vor, am)"
},
"intendedApplication": {
"type": "string",
"description": "Intended use (text-mining, similarity-checking, etc.)"
}
},
"description": "Registered full-text link",
"additionalProperties": false
},
"description": "Registered full-text links"
},
"shown": {
"type": "number",
"description": "Number of authors returned in this page."
},
"title": {
"type": "string",
"description": "Work title"
},
"notice": {
"type": "string",
"description": "When updatedBy is present, the update types Crossref records for this work with the source of each, and where the notice is read. Then, when limit was above its per-call cap, the value requested and the cap applied. Then which authors this page covers and the offset that reaches the next ones, or an explanation when the requested offset is past the end of the author list. All share this one string when more than one applies. Absent when the page holds the whole author list, no update is recorded, and limit was within its cap."
},
"offset": {
"type": "number",
"description": "Zero-based index of the first returned author within the deposited list. Omitted alongside authors when the record deposits no author field."
},
"volume": {
"type": "string",
"description": "Volume of the container the work appears in"
},
"authors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name when no given/family split is available"
},
"given": {
"type": "string",
"description": "Given (first) name"
},
"orcid": {
"type": "string",
"description": "ORCID identifier URI"
},
"family": {
"type": "string",
"description": "Family (last) name"
},
"sequence": {
"type": "string",
"description": "Author order role (first, additional)"
},
"affiliation": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ror": {
"type": "string",
"description": "ROR identifier for the affiliated organization, when the deposit carries one. It is the whole identity of an affiliation deposited without a name."
},
"name": {
"type": "string",
"description": "Affiliation name as deposited. Absent when the publisher asserted the organization by identifier alone, where ror carries it instead."
}
},
"description": "Affiliation",
"additionalProperties": false
},
"description": "Institutional affiliations"
}
},
"description": "Author or contributor",
"additionalProperties": false
},
"description": "Page of the author and contributor list, bounded by limit. Omitted when the record deposits no author field at all."
},
"editors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name when no given/family split is available"
},
"given": {
"type": "string",
"description": "Given (first) name"
},
"orcid": {
"type": "string",
"description": "ORCID identifier URI"
},
"family": {
"type": "string",
"description": "Family (last) name"
},
"sequence": {
"type": "string",
"description": "Author order role (first, additional)"
},
"affiliation": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ror": {
"type": "string",
"description": "ROR identifier for the affiliated organization, when the deposit carries one. It is the whole identity of an affiliation deposited without a name."
},
"name": {
"type": "string",
"description": "Affiliation name as deposited. Absent when the publisher asserted the organization by identifier alone, where ror carries it instead."
}
},
"description": "Affiliation",
"additionalProperties": false
},
"description": "Institutional affiliations"
}
},
"description": "Author or contributor",
"additionalProperties": false
},
"description": "Editors of the work, or of the book or proceedings containing it, in the entry shape authors use. Returned whole — never paged by offset and limit, and never counted in authorCount. Omitted when the record deposits none."
},
"funders": {
"type": "array",
"items": {
"type": "object",
"properties": {
"doi": {
"type": "string",
"description": "Funder DOI — the Crossref Funder Registry entry this assertion resolved to"
},
"ror": {
"type": "string",
"description": "ROR identifier for the funding organization, when the deposit carries one. It is the whole identity of an assertion deposited without a name."
},
"name": {
"type": "string",
"description": "Funder name as deposited. Absent when the publisher asserted the funder by identifier alone, where ror carries it instead."
},
"award": {
"type": "array",
"items": {
"type": "string"
},
"description": "Grant or award numbers"
}
},
"description": "Funding assertion",
"additionalProperties": false
},
"description": "Funding acknowledgements"
},
"subject": {
"type": "array",
"items": {
"type": "string"
},
"description": "Subject classification terms"
},
"abstract": {
"type": "string",
"description": "Abstract when deposited by the publisher. Many records lack abstracts. Publishers deposit it as JATS XML, so this is the text of that deposit with markup removed and character references decoded; numeric JATS superscripts retain caret notation (10^{16}, GeV^2), a link keeps its tag only where its href holds an address the text it wraps does not already carry, and each formula appears once — MathML as the TeX annotation it carries, otherwise written out linearly (x_i, A^{−1}, √(m), (a+b)/c), and TeX deposited beside MathML in whichever notation comes first."
},
"language": {
"type": "string",
"description": "Language code (ISO 639)"
},
"licenses": {
"type": "array",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "License URL"
},
"start": {
"type": "string",
"description": "License start date-time as deposited by the publisher"
},
"delayInDays": {
"type": "number",
"description": "Embargo delay in days from publication date"
},
"contentVersion": {
"type": "string",
"description": "Content version (vor, am, tdm, unspecified)"
}
},
"description": "License entry",
"additionalProperties": false
},
"description": "License terms"
},
"memberId": {
"type": "number",
"description": "Crossref member ID of the publisher that holds this DOI now — pass to crossref_get_member for its record. The DOI's prefix names the member that registered the prefix, which for a transferred title is a former publisher. Omitted when the record deposits no member."
},
"subtitle": {
"type": "string",
"description": "Subtitle when present"
},
"updateTo": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi",
"type",
"source"
],
"properties": {
"doi": {
"type": "string",
"description": "DOI on the other side of the link: the notice, in updatedBy; the updated work, in updateTo. It is this work's own DOI when the update was made to this record in place."
},
"type": {
"type": "string",
"description": "Crossref update type as deposited: correction, erratum, corrigendum, addendum, retraction, withdrawal, expression_of_concern, new_version, new_edition, or another code"
},
"source": {
"type": "string",
"description": "Who recorded the link: publisher, or retraction-watch for an entry Crossref carries from the Retraction Watch database"
},
"updated": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day of month"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month (1–12)"
}
},
"description": "Date the update was issued",
"additionalProperties": false
},
"recordId": {
"type": "string",
"description": "Retraction Watch record ID. Present only on retraction-watch entries."
}
},
"description": "Update link",
"additionalProperties": false
},
"description": "Works this record is an update notice for, in the entry shape updatedBy uses and in deposited order. Omitted when the record is not registered as a notice."
},
"published": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day of month"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month (1–12)"
}
},
"description": "Publication date — the first of published, published-print, published-online, and issued that names one. A component Crossref records as unknown is omitted, and so is every component below it.",
"additionalProperties": false
},
"publisher": {
"type": "string",
"description": "Publisher name"
},
"relations": {
"type": "array",
"items": {
"type": "object",
"required": [
"type",
"idType",
"assertedBy",
"ids"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Related identifiers in deposited order. A DOI resolves through crossref_get_work only when Crossref registered it; one registered with another agency, such as DataCite, returns doi_not_found there."
},
"type": {
"type": "string",
"description": "Relation type as deposited, read from this work toward ids: is-preprint-of, has-preprint, is-version-of, has-version, has-review, is-supplemented-by, references, has-part, and others"
},
"idType": {
"type": "string",
"description": "Identifier scheme of ids: doi, uri, pmid, arxiv, accession, issn, isbn, or other"
},
"assertedBy": {
"type": "string",
"description": "Whose deposit asserts the relation: subject (this work's depositor) or object (the related record's depositor, whose assertion Crossref shows here inverted)"
}
},
"description": "Related identifiers sharing one relation type, identifier type, and asserting party",
"additionalProperties": false
},
"description": "Related identifiers — preprints and published versions, other versions, reviews, supplements, parts — grouped by relation type, identifier type, and asserting party, groups in Crossref's order and ids in deposited order. Returned whole. Omitted when the record deposits none."
},
"truncated": {
"type": "boolean",
"description": "True when authors remain beyond this page. Absent when the page is the last."
},
"updatedBy": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi",
"type",
"source"
],
"properties": {
"doi": {
"type": "string",
"description": "DOI on the other side of the link: the notice, in updatedBy; the updated work, in updateTo. It is this work's own DOI when the update was made to this record in place."
},
"type": {
"type": "string",
"description": "Crossref update type as deposited: correction, erratum, corrigendum, addendum, retraction, withdrawal, expression_of_concern, new_version, new_edition, or another code"
},
"source": {
"type": "string",
"description": "Who recorded the link: publisher, or retraction-watch for an entry Crossref carries from the Retraction Watch database"
},
"updated": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day of month"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month (1–12)"
}
},
"description": "Date the update was issued",
"additionalProperties": false
},
"recordId": {
"type": "string",
"description": "Retraction Watch record ID. Present only on retraction-watch entries."
}
},
"description": "Update link",
"additionalProperties": false
},
"description": "Update notices Crossref records against this work — corrections, errata, retractions, expressions of concern, withdrawals, new versions — in deposited order. Entries are never merged: the same notice DOI can appear once per source, typed differently by each. Omitted when none is recorded, which does not mean the work was never updated: coverage depends on publisher and Retraction Watch deposits."
},
"nextOffset": {
"type": "number",
"description": "Offset to pass in the next call to retrieve the following page of authors. Absent when this page reaches the end of the author list."
},
"authorCount": {
"type": "number",
"description": "Total number of authors in the deposited list, before offset and limit were applied. Omitted alongside authors when the record deposits no author field."
},
"articleNumber": {
"type": "string",
"description": "Article number, deposited by journals that number articles instead of paging them"
},
"containerTitle": {
"type": "string",
"description": "Journal, book, or proceedings name containing this work — the first entry of containerTitles"
},
"containerTitles": {
"type": "array",
"items": {
"type": "string"
},
"description": "Every container title the record deposits, in deposited order. A book chapter or proceedings paper often deposits two: the series, then the book or volume. Omitted when the record deposits none."
},
"referencesCount": {
"type": "number",
"description": "Number of outgoing references (works cited by this paper)"
},
"isReferencedByCount": {
"type": "number",
"description": "Incoming citation count from Crossref — the count of works citing this DOI"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"idempotentHint": true
}crossref_get_references — Get Reference List
Returns the outgoing reference list for a DOI — the works cited by this paper. Each reference includes the deposited citation string and, where Crossref has resolved it, a DOI you can look up with crossref_get_work. Results are paged: referenceCount is the full deposited total, and when more remain the response carries a nextOffset to pass back as offset. Reference list coverage varies by publisher; many older works and non-participating publishers have no indexed references. Incoming citations — the works that cite this paper — are not available through Crossref; use OpenAlex for that.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"doi"
],
"properties": {
"doi": {
"type": "string",
"pattern": "^(?:https?:\\/\\/(?:dx\\.)?doi\\.org\\/|doi:)?10\\.\\d{4,9}\\/\\S+$",
"description": "DOI in the format \"10.NNNN/suffix\", e.g. \"10.1038/nature12373\". A resolver-wrapped form — \"https://doi.org/10.1038/nature12373\", \"https://dx.doi.org/…\", \"doi:10.1038/nature12373\" — is accepted and unwrapped."
},
"limit": {
"type": "integer",
"default": 100,
"maximum": 100000,
"minimum": 1,
"description": "Maximum number of references to return in one page (1–2500 per call, default 100). A larger value, up to 100000, returns 2500 and says so in notice; nextOffset continues after the references returned. Most works fit in a single page; bibliography records can carry tens of thousands."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based index of the first reference to return. Pass the nextOffset value from the previous response to continue through a long reference list."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"doi",
"referenceCount",
"offset",
"references"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The limit that was applied to this page."
},
"doi": {
"type": "string",
"description": "DOI of the citing work"
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"doi_not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `doi_not_found`: Valid DOI format but no Crossref record exists. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of references returned in this page."
},
"notice": {
"type": "string",
"description": "Coverage guidance when no references are indexed, a range explanation when the requested offset is past the end of the list, or which references this page covers and the offset that reaches the next ones. Leads with the value requested and the cap applied when limit was above its per-call cap. Absent on a page that holds the whole list with limit within its cap."
},
"offset": {
"type": "number",
"description": "Zero-based index of the first returned reference within the deposited list"
},
"truncated": {
"type": "boolean",
"description": "True when references remain beyond this page. Absent when the page is the last."
},
"nextOffset": {
"type": "number",
"description": "Offset to pass in the next call to retrieve the following page. Absent when this page reaches the end of the reference list."
},
"references": {
"type": "array",
"items": {
"type": "object",
"properties": {
"doi": {
"type": "string",
"description": "Resolved DOI for this reference, when available"
},
"key": {
"type": "string",
"description": "Reference key as deposited"
},
"issn": {
"type": "string",
"description": "ISSN of the referenced journal"
},
"year": {
"type": "string",
"description": "Publication year of the referenced work"
},
"issue": {
"type": "string",
"description": "Issue"
},
"author": {
"type": "string",
"description": "Author field from the reference entry, with inline formatting markup removed"
},
"volume": {
"type": "string",
"description": "Volume"
},
"edition": {
"type": "string",
"description": "Edition of the cited book, as deposited"
},
"firstPage": {
"type": "string",
"description": "First page"
},
"seriesTitle": {
"type": "string",
"description": "Title of the series the cited work belongs to, with inline formatting markup removed"
},
"volumeTitle": {
"type": "string",
"description": "Title of the cited book or proceedings volume, with inline formatting markup removed. The only title on many book and chapter citations."
},
"articleTitle": {
"type": "string",
"description": "Article title of the referenced work, with inline formatting markup removed"
},
"journalTitle": {
"type": "string",
"description": "Journal title of the referenced work, with inline formatting markup removed"
},
"unstructured": {
"type": "string",
"description": "Citation string as deposited by the publisher, with formatting markup and structured-citation tags removed. Angle-bracketed text that is not recognizable as markup — a cited URL, a Miller index, a DOI fragment — is left exactly as deposited, and so is a link whose href holds an address the text it wraps does not already carry."
},
"doiAssertedBy": {
"type": "string",
"description": "Who supplied the DOI: \"publisher\" when the citing work's publisher deposited it, \"crossref\" when Crossref matched the citation to it."
}
},
"description": "Reference entry",
"additionalProperties": false
},
"description": "Page of the outgoing reference list"
},
"referenceCount": {
"type": "number",
"description": "Total number of references in the deposited list"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"idempotentHint": true
}crossref_search_works — Search Works
Searches the Crossref works index (~188M records) by free text and/or structured filters. The generic query matches loosely across all fields; scope precisely with the field-specific parameters queryTitle, queryAuthor, and queryContainerTitle, or resolve a known citation to its DOI with queryBibliographic — all combine with each other and with query. Use the filter parameter for structured filtering (object with hyphen-separated Crossref keys). Sort options: relevance, score, is-referenced-by-count, published, deposited, indexed, created. Each work returns at most authorLimit authors (25 by default) with authorCount reporting the full deposited total, since a single page of large-collaboration papers can carry tens of thousands of author entries; crossref_get_work pages the whole author list for any DOI whose list was cut. Post-publication updates ride each work as Crossref records them: updatedBy names each correction, retraction, expression of concern, or new version issued against it, and updateTo the works it is itself a notice for — an absent updatedBy does not mean the work was never updated. Offset-based paging is capped at ~10K results; use cursor="*" to start cursor-based deep paging, then pass the nextCursor value from each response to continue. A walk with a query term and no sort runs by relevance, one with filters only in Crossref registration order. The walk ends on the page where nextCursor is absent — that page also carries a notice saying the list is exhausted. Cursor and offset cannot be combined.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"rows": {
"type": "integer",
"default": 20,
"maximum": 1000,
"minimum": 1,
"description": "Number of results to return per page (1–250 per call, default 20). A larger value, up to 1000, returns 250 and says so in notice — on an offset page the notice also names the offset that continues after the records returned."
},
"sort": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"enum": [
"relevance",
"score",
"is-referenced-by-count",
"published",
"published-print",
"published-online",
"deposited",
"indexed",
"created",
"updated",
"references-count"
],
"type": "string",
"description": "Crossref sort field"
}
],
"description": "Sort field. Omitted, an offset page with a query term is ranked by relevance, and so is a cursor walk with one (sent as score). The publication-date sorts (published, published-print, published-online) work with offset paging only — Crossref refuses them alongside cursor; every other sort works with either."
},
"order": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort direction"
}
],
"description": "Sort direction (asc or desc). Applies only alongside sort; without one it has no effect."
},
"query": {
"type": "string",
"description": "Free-text search query, e.g. \"CRISPR gene editing\" or \"climate change adaptation\""
},
"cursor": {
"type": "string",
"description": "Cursor token for deep paging. Pass \"*\" to start cursor-based paging (required past ~10K results), then pass the nextCursor value from each response until a response omits it, which means the list is exhausted. A walk with a query term runs by relevance unless sort is set; a walk with filters only runs in Crossref registration order. Relevance scores shift slightly between identical requests, so a relevance walk can repeat or skip a record at a page boundary — sort:\"created\" walks every match without repeats. Cannot be combined with offset, or with a publication-date sort (published, published-print, published-online), which Crossref does not walk by cursor."
},
"fields": {
"type": "array",
"items": {
"enum": [
"DOI",
"title",
"type",
"author",
"published",
"published-print",
"published-online",
"container-title",
"publisher",
"member",
"is-referenced-by-count",
"score",
"abstract",
"volume",
"issue",
"page",
"article-number",
"ISSN",
"updated-by",
"update-to"
],
"type": "string",
"description": "Crossref select name"
},
"description": "Fields to return (reduces payload). Names are case-sensitive, and each fills one output field: DOI → doi, title → title, type → type, author → authors and authorCount, published / published-print / published-online → published, container-title → containerTitle and containerTitles, publisher → publisher, member → memberId, is-referenced-by-count → isReferencedByCount, score → score, abstract → abstract, volume → volume, issue → issue, page → page, article-number → articleNumber, ISSN → issn, updated-by → updatedBy, update-to → updateTo. A selected field absent from a work means the record does not deposit it. DOI is always returned whether or not it is listed here, so every result stays resolvable by crossref_get_work, which also returns the fields this list does not cover (license, funder, references, and the rest of the record)."
},
"filter": {
"type": "object",
"description": "Structured filter object using Crossref hyphen-separated keys. All values must be strings. Boolean flag keys (has-abstract, has-references, has-full-text) require string values \"true\" or \"false\". Example: {\"type\":\"journal-article\",\"has-abstract\":\"true\",\"from-pub-date\":\"2023-01-01\"}",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
}
},
"offset": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based result offset for offset-based paging. Cannot be used with cursor. Capped at ~10K: offset + rows cannot pass it, so a page that would returns the rows that fit and says so in notice, and an offset at or past it is refused. Use cursor for deeper paging."
},
"queryTitle": {
"type": "string",
"description": "Match against work titles only, e.g. \"Array programming with NumPy\"."
},
"authorLimit": {
"type": "integer",
"default": 25,
"maximum": 100000,
"minimum": 1,
"description": "Maximum number of authors to return per work (1–500 per call, default 25; a larger value, up to 100000, applies 500 and says so in notice). Ordinary records fit under the default; large-collaboration papers deposit thousands, and a page of them is large enough to exhaust a client context. Each work reports its full deposited total as authorCount — call crossref_get_work with that work doi to page the authors this cap left out."
},
"queryAuthor": {
"type": "string",
"description": "Match against author names only, e.g. \"Charles R. Harris\"."
},
"queryBibliographic": {
"type": "string",
"description": "Whole-citation match to resolve a known reference to its DOI. Combine title, author, year, and container into one string, e.g. \"Watson Crick molecular structure of nucleic acids Nature 1953\"."
},
"queryContainerTitle": {
"type": "string",
"description": "Match against the container title (journal or book name) only, e.g. \"Nature\"."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"works",
"totalResults",
"returned"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The per-work author cap applied to this page. Absent when no list was cut."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"unknown_filter",
"sort_cursor_conflict",
"invalid_cursor",
"invalid_parameter",
"cursor_offset_conflict",
"offset_too_large"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `unknown_filter`: Crossref rejected a filter key it does not recognize (filter-not-available). `sort_cursor_conflict`: A publication-date sort (published, published-print, published-online) was combined with cursor paging, which Crossref refuses. `invalid_cursor`: Crossref did not recognize the cursor token (HTTP 404 cursor-invalid). `invalid_parameter`: Crossref rejected a parameter value (a filter value of the wrong type or form), or answered HTTP 400 with a body that did not parse. `cursor_offset_conflict`: Both cursor and offset were supplied in the same request. `offset_too_large`: The requested offset is at or past the ~10K limit Crossref puts on offset + rows, so no page of any size starts there. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"works": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi"
],
"properties": {
"doi": {
"type": "string",
"description": "Canonical DOI"
},
"issn": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISSN(s) of the containing journal"
},
"page": {
"type": "string",
"description": "Page range as deposited, e.g. \"357-362\""
},
"type": {
"type": "string",
"description": "Work type"
},
"issue": {
"type": "string",
"description": "Issue of the container the work appears in"
},
"score": {
"type": "number",
"description": "Relevance score assigned by Crossref"
},
"title": {
"type": "string",
"description": "Work title"
},
"volume": {
"type": "string",
"description": "Volume of the container the work appears in"
},
"authors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name when no given/family split is available"
},
"given": {
"type": "string",
"description": "Given name"
},
"family": {
"type": "string",
"description": "Family name"
}
},
"description": "Author",
"additionalProperties": false
},
"description": "Author list for this work, capped at authorLimit. Omitted when the record deposits no author field."
},
"abstract": {
"type": "string",
"description": "Abstract when present in the indexed record — the text of the publisher’s JATS deposit, with markup removed and character references decoded; numeric JATS superscripts retain caret notation (10^{16}, GeV^2), a link keeps its tag only where its href holds an address the text it wraps does not already carry, and each formula appears once — MathML as the TeX annotation it carries, otherwise written out linearly (x_i, A^{−1}, √(m), (a+b)/c), and TeX deposited beside MathML in whichever notation comes first"
},
"memberId": {
"type": "number",
"description": "Crossref member ID of the publisher that holds this DOI now — pass to crossref_get_member for its record. The DOI's prefix names the member that registered the prefix, which for a transferred title is a former publisher. Omitted when the record deposits no member."
},
"updateTo": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi",
"type",
"source"
],
"properties": {
"doi": {
"type": "string",
"description": "DOI on the other side of the link: the notice, in updatedBy; the updated work, in updateTo. It is this work's own DOI when the update was made to this record in place."
},
"type": {
"type": "string",
"description": "Crossref update type as deposited: correction, erratum, corrigendum, addendum, retraction, withdrawal, expression_of_concern, new_version, new_edition, or another code"
},
"source": {
"type": "string",
"description": "Who recorded the link: publisher, or retraction-watch for an entry Crossref carries from the Retraction Watch database"
},
"updated": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day of month"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month (1–12)"
}
},
"description": "Date the update was issued",
"additionalProperties": false
},
"recordId": {
"type": "string",
"description": "Retraction Watch record ID. Present only on retraction-watch entries."
}
},
"description": "Update link",
"additionalProperties": false
},
"description": "Works this record is an update notice for, in the entry shape updatedBy uses and in deposited order. Omitted when the record is not registered as a notice."
},
"published": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month"
}
},
"description": "Publication date — the first of published, published-print, and published-online that names one. A component Crossref records as unknown is omitted, and so is every component below it.",
"additionalProperties": false
},
"publisher": {
"type": "string",
"description": "Publisher name"
},
"updatedBy": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi",
"type",
"source"
],
"properties": {
"doi": {
"type": "string",
"description": "DOI on the other side of the link: the notice, in updatedBy; the updated work, in updateTo. It is this work's own DOI when the update was made to this record in place."
},
"type": {
"type": "string",
"description": "Crossref update type as deposited: correction, erratum, corrigendum, addendum, retraction, withdrawal, expression_of_concern, new_version, new_edition, or another code"
},
"source": {
"type": "string",
"description": "Who recorded the link: publisher, or retraction-watch for an entry Crossref carries from the Retraction Watch database"
},
"updated": {
"type": "object",
"properties": {
"day": {
"type": "number",
"description": "Day of month"
},
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month (1–12)"
}
},
"description": "Date the update was issued",
"additionalProperties": false
},
"recordId": {
"type": "string",
"description": "Retraction Watch record ID. Present only on retraction-watch entries."
}
},
"description": "Update link",
"additionalProperties": false
},
"description": "Update notices Crossref records against this work — corrections, errata, retractions, expressions of concern, withdrawals, new versions — in deposited order. Entries are never merged: the same notice DOI can appear once per source, typed differently by each. Omitted when none is recorded, which does not mean the work was never updated: coverage depends on publisher and Retraction Watch deposits."
},
"authorCount": {
"type": "number",
"description": "Total number of authors deposited for this work, before the authorLimit cap. Greater than the length of authors when the cap cut the list; pass the doi of this work to crossref_get_work to page the whole list."
},
"articleNumber": {
"type": "string",
"description": "Article number, deposited by journals that number articles instead of paging them"
},
"containerTitle": {
"type": "string",
"description": "Journal or container name — the first entry of containerTitles"
},
"containerTitles": {
"type": "array",
"items": {
"type": "string"
},
"description": "Every container title the record deposits, in deposited order. A book chapter or proceedings paper often deposits two: the series, then the book or volume. Omitted when the record deposits none."
},
"isReferencedByCount": {
"type": "number",
"description": "Incoming citation count"
}
},
"description": "Work summary",
"additionalProperties": false
},
"description": "Matching works. Empty when nothing matched the query, when an offset runs past the end of the results, or on the page that ends a cursor walk — the notice enrichment says which."
},
"notice": {
"type": "string",
"description": "Guidance on an empty page, naming which of its three causes applies: a query nothing matched, an offset past the end of a list that did match, or a cursor walk that has reached the end of the list. On a page carrying records, present when authorLimit cut at least one work list, naming how many and the route to the rest, or when every query term and filter value was supplied blank, so the page lists the whole index unfiltered. On any page, present when rows or authorLimit was above its per-call cap, naming the value requested and the cap applied — on an offset page with records remaining, also the offset that continues after the records returned. On an offset page where offset + rows would pass the ~10K offset ceiling, present naming the rows requested and the rows that fit and were applied, and, when records remain past the ceiling, cursor as the route to them. A page needing more than one caveat carries them all in this one string."
},
"returned": {
"type": "number",
"description": "Number of records returned in this response"
},
"truncated": {
"type": "boolean",
"description": "True when at least one work on this page had its author list cut by authorLimit. Absent when every work on the page carries its full deposited author list."
},
"nextCursor": {
"type": "string",
"description": "Cursor token to pass as cursor on the next call to continue a cursor walk. Present only on a page requested with cursor, and absent once the walk reaches the end of the list."
},
"totalResults": {
"type": "number",
"description": "Total matching records in Crossref"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}crossref_search_journals — Search Journals
Finds Crossref journal records by ISSN or title query. Provide issn for an exact single-journal lookup, or query for title-based search returning up to rows results. Title-query results page with offset — the nextOffset enrichment carries the value for the following page, up to offset + rows = 100000. Set include_works to true to also return a page of the matched journal's most recent works — the works deposited under any of its ISSNs, whichever one you passed; that list pages two ways, in two orders. works_offset is the simple one: newest published first, capped ten times lower at works_offset + rows = 10000. works_cursor has no ceiling and reaches the whole works list, newest registered first — ordered by the date each DOI was registered with Crossref, since Crossref does not walk a publication-date sort by cursor: pass works_cursor="*" on the first call, then chain the nextWorksCursor token from each response. The two cannot be combined, and a cursor walk always starts at the most recently registered work — it cannot resume from an offset. Returns journal metadata: title, publisher, ISSNs with their print or electronic form, subject areas, DOI counts (total, current, backfile), and per-category metadata deposit coverage — how completely the journal deposits abstracts, references, ORCIDs, funders, licenses, and similar metadata. An issn lookup also returns DOI counts by issued year.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"issn": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"type": "string",
"pattern": "^\\d{4}-?\\d{3}[\\dX]$",
"description": "ISSN, e.g. \"1234-5678\""
}
],
"description": "ISSN for exact single-journal lookup (print or electronic, with or without hyphen). Example: \"1234-5678\"."
},
"rows": {
"type": "integer",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of journals to return for title queries, or works when include_works is true (1–500 per call, default 10). A larger value, up to 1000, returns 500 and says so in notice; nextOffset and nextWorksOffset continue after the records returned."
},
"query": {
"type": "string",
"description": "Journal title search query, e.g. \"Nature\" or \"Journal of Machine Learning Research\""
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based offset into the title-query journal list. Pass the nextOffset value from the previous response to continue. Capped at offset + rows = 100000: a page that would pass it returns the journals that fit and says so in notice, and an offset at or past it is refused. Ignored when issn is set, which resolves exactly one record."
},
"works_cursor": {
"type": "string",
"description": "Cursor token for deep paging of the journal works list when include_works is true. Pass \"*\" to start the walk at the most recently registered work, then pass the nextWorksCursor value from each response. A walk runs by the date each DOI was registered with Crossref, newest first, not by publication date — Crossref does not walk a publication-date sort by cursor. Has no offset ceiling and cannot be combined with works_offset."
},
"works_offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based offset into the journal works list when include_works is true. Pass the nextWorksOffset value from the previous response to continue. Capped at works_offset + rows = 10000: a page that would pass it returns the works that fit and says so in notice, and a works_offset at or past it is refused; use works_cursor to read the whole list. Cannot be combined with works_cursor."
},
"include_works": {
"type": "boolean",
"default": false,
"description": "When true, also return a page of the journal's most recent works — newest published first on an offset page, newest registered first on a works_cursor walk. Requires an unambiguous journal — pass issn when a title query matches more than one. A journal with no well-formed ISSN registered has no addressable works list; the works lookup is then skipped and the notice enrichment says so."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"journals",
"journalCount",
"journalsTotal"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"invalid_cursor",
"invalid_parameter",
"issn_not_found",
"ambiguous_journal",
"offset_too_large",
"works_offset_too_large",
"works_cursor_offset_conflict"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `invalid_cursor`: Crossref did not recognize the cursor token (HTTP 404 cursor-invalid). `invalid_parameter`: Crossref rejected a parameter value (a filter value of the wrong type or form), or answered HTTP 400 with a body that did not parse. `issn_not_found`: ISSN lookup returned 404 — ISSN is not registered in Crossref. `ambiguous_journal`: include_works is true but the title query matched more than one journal, making the target ambiguous. `offset_too_large`: offset is at or past the 100000-record ceiling Crossref puts on offset + rows on journal title search, so no journal page starts there. `works_offset_too_large`: works_offset is at or past the 10000-record ceiling Crossref puts on works_offset + rows on a journal works list, so no works page starts there. `works_cursor_offset_conflict`: include_works is true and works_cursor was supplied alongside a works_offset above zero. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Guidance on a page that needs a caveat: a query nothing matched, an offset or works_offset past the end of a list that did match, a page whose offset + rows would pass one of the route offset ceilings, cut to the rows that fit (naming the rows requested and applied), a page that reaches one of those ceilings with records still unretrieved (naming the route to them), an include_works request that was skipped because the matched journal has no well-formed registered ISSN to address its works list by, a query or issn supplied blank with nothing else to search by, so the page lists every journal unfiltered, or a rows value above its per-call cap, naming the value requested and the cap applied. Absent otherwise. A page needing more than one caveat carries them all in this one string."
},
"journals": {
"type": "array",
"items": {
"type": "object",
"properties": {
"issn": {
"type": "array",
"items": {
"type": "string"
},
"description": "All ISSN variants for this journal"
},
"issnL": {
"type": "string",
"description": "Linking ISSN (ISSN-L). Crossref journal records do not carry one, so this is normally absent; issnTypes says which ISSN is print and which electronic."
},
"title": {
"type": "string",
"description": "Journal title"
},
"coverage": {
"type": "array",
"items": {
"type": "object",
"required": [
"category"
],
"properties": {
"current": {
"type": "number",
"description": "Coverage fraction (0–1) among current (recent) DOIs"
},
"backfile": {
"type": "number",
"description": "Coverage fraction (0–1) among backfile (older) DOIs"
},
"category": {
"type": "string",
"description": "Metadata deposit category, e.g. \"references\", \"abstracts\", \"orcids\", \"funders\", \"licenses\", \"affiliations\""
}
},
"description": "Metadata deposit coverage for one category",
"additionalProperties": false
},
"description": "Per-category metadata deposit coverage for this journal — each a 0–1 fraction split into current and backfile DOIs. Signals how completely the journal deposits abstracts, references, ORCIDs, funders, licenses, and similar metadata. A window with no DOIs reports every fraction as 0, so read each fraction beside currentDois and backfileDois."
},
"subjects": {
"type": "array",
"items": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Subject area name"
}
},
"description": "Subject",
"additionalProperties": false
},
"description": "Subject classifications"
},
"issnTypes": {
"type": "array",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"type": {
"type": "string",
"description": "ISSN form, e.g. \"print\" or \"electronic\". Absent when Crossref records no form for this ISSN."
},
"value": {
"type": "string",
"description": "ISSN"
}
},
"description": "One ISSN and its form",
"additionalProperties": false
},
"description": "Each ISSN with its form — print or electronic"
},
"publisher": {
"type": "string",
"description": "Publisher name"
},
"totalDois": {
"type": "number",
"description": "Total registered DOIs in this journal"
},
"doisByYear": {
"type": "array",
"items": {
"type": "object",
"required": [
"year",
"count"
],
"properties": {
"year": {
"type": "number",
"description": "Issued year"
},
"count": {
"type": "number",
"description": "DOIs issued that year"
}
},
"description": "DOI count for one issued year",
"additionalProperties": false
},
"description": "Registered DOIs per issued year, ascending by year; a year with no DOIs has no entry. Returned on an issn lookup only — title-query results omit it."
},
"currentDois": {
"type": "number",
"description": "DOIs issued in the current window — this year and the two before it"
},
"backfileDois": {
"type": "number",
"description": "DOIs issued before the current window"
}
},
"description": "Journal record",
"additionalProperties": false
},
"description": "Matching journal records"
},
"nextOffset": {
"type": "number",
"description": "Value to pass as offset on the next call for the following page of journals. Absent when this page ends the matches or reaches the 100000-record offset ceiling."
},
"worksTotal": {
"type": "number",
"description": "Total works deposited under any of the journal's ISSNs, when include_works is true"
},
"recentWorks": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi"
],
"properties": {
"doi": {
"type": "string",
"description": "Work DOI"
},
"type": {
"type": "string",
"description": "Work type"
},
"title": {
"type": "string",
"description": "Work title"
},
"published": {
"type": "object",
"properties": {
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month"
}
},
"description": "Publication date — the first of published, published-print, and published-online that names one. A component Crossref records as unknown is omitted, and so is every component below it.",
"additionalProperties": false
},
"isReferencedByCount": {
"type": "number",
"description": "Incoming citation count"
}
},
"description": "Work summary",
"additionalProperties": false
},
"description": "Page of works from the matched journal, newest first — by publication date on an offset page, by Crossref registration date on a works_cursor page. Requires include_works, and absent even then when the matched journal has no well-formed registered ISSN — the works list is addressable by ISSN alone, and the notice enrichment says so."
},
"journalCount": {
"type": "number",
"description": "Number of journal records returned in this page"
},
"journalsTotal": {
"type": "number",
"description": "Total journal records matching the query in Crossref"
},
"nextWorksCursor": {
"type": "string",
"description": "Value to pass as works_cursor on the next call for the following page of works. Present only on a page requested with works_cursor, and absent once the walk reaches the end of the works list."
},
"nextWorksOffset": {
"type": "number",
"description": "Value to pass as works_offset on the next call for the following page of works. Absent when this page ends the works list, reaches the 10000-record offset ceiling, or was requested with works_cursor."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}crossref_search_funders — Search Funders
Finds funders registered in the Crossref Funder Registry by name or funder DOI. Provide funder_doi for an exact single-funder lookup — the full DOI ("10.13039/100000001"), the bare registry ID ("100000001"), or either behind a doi: or https://doi.org/ prefix — or query for name-based search. Name-query results page with offset — the nextOffset enrichment carries the value for the following page, up to offset + rows = 100000. Set include_works to true to also return a page of the most recent works funded by the matched funder; that list pages two ways, in two orders. works_offset is the simple one: newest published first, capped ten times lower at works_offset + rows = 10000. works_cursor has no ceiling and reaches the whole funded-works list, newest registered first — ordered by the date each DOI was registered with Crossref, since Crossref does not walk a publication-date sort by cursor: pass works_cursor="*" on the first call, then chain the nextWorksCursor token from each response. The two cannot be combined, and a cursor walk always starts at the most recently registered work — it cannot resume from an offset. This list also counts works funded by the funder's registry descendants, which a crossref_search_works filter on {"funder": "10.13039/<id>"} does not; a funder_doi lookup returns those descendants' registry IDs as descendants, and the funder's own ID OR'd with each of them in the funder filter ({"funder": "<id>,funder:<descendant>,…"}) counts the same scope with date, type, or other filters. Returns funder name, registry ID, country, and alternate names, plus the work count of the ID alone on a funder_doi lookup. The Funder Registry supersedes entries, and a deprecated one answers to the same names as its successor while carrying only a fraction of its works: such a record carries replacedBy with the superseding registry ID and the response carries a notice naming it. The replacement is never followed automatically — re-run with funder_doi set to that ID to get the current entry.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"rows": {
"type": "integer",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum funders to return for name queries, or works when include_works is true (1–1000, default 10)."
},
"query": {
"type": "string",
"description": "Funder name search query, e.g. \"National Science Foundation\" or \"Wellcome Trust\""
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based offset into the name-query funder list. Pass the nextOffset value from the previous response to continue. Capped at offset + rows = 100000: a page that would pass it returns the funders that fit and says so in notice, and an offset at or past it is refused. Ignored when funder_doi is set, which resolves exactly one record."
},
"funder_doi": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"type": "string",
"pattern": "^(?:(?:https?:\\/\\/(?:dx\\.)?doi\\.org\\/|doi:)?10\\.13039\\/)?\\d+$",
"description": "Funder registry ID or funder DOI, e.g. \"100000001\""
}
],
"description": "Funder DOI for exact lookup — the full DOI \"10.13039/100000001\" (NSF) or the bare registry ID \"100000001\". Supersedes query when provided."
},
"works_cursor": {
"type": "string",
"description": "Cursor token for deep paging of the funded-works list when include_works is true. Pass \"*\" to start the walk at the most recently registered work, then pass the nextWorksCursor value from each response. A walk runs by the date each DOI was registered with Crossref, newest first, not by publication date — Crossref does not walk a publication-date sort by cursor. Has no offset ceiling and cannot be combined with works_offset."
},
"works_offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based offset into the funded-works list when include_works is true. Pass the nextWorksOffset value from the previous response to continue. Capped at works_offset + rows = 10000: a page that would pass it returns the works that fit and says so in notice, and a works_offset at or past it is refused; use works_cursor to read the whole list. Cannot be combined with works_cursor."
},
"include_works": {
"type": "boolean",
"default": false,
"description": "When true, also return a page of works funded by the matched funder. Requires an unambiguous funder — pass funder_doi when a name query matches more than one."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"funders",
"funderCount",
"fundersTotal"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"invalid_cursor",
"invalid_parameter",
"funder_not_found",
"ambiguous_funder",
"offset_too_large",
"works_offset_too_large",
"works_cursor_offset_conflict"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `invalid_cursor`: Crossref did not recognize the cursor token (HTTP 404 cursor-invalid). `invalid_parameter`: Crossref rejected a parameter value (a filter value of the wrong type or form), or answered HTTP 400 with a body that did not parse. `funder_not_found`: Funder DOI lookup returned 404 — funder is not in the Crossref Funder Registry. `ambiguous_funder`: include_works is true but the name query matched more than one funder, making the target ambiguous. `offset_too_large`: offset is at or past the 100000-record ceiling Crossref puts on offset + rows on funder name search, so no funder page starts there. `works_offset_too_large`: works_offset is at or past the 10000-record ceiling Crossref puts on works_offset + rows on a funded-works list, so no works page starts there. `works_cursor_offset_conflict`: include_works is true and works_cursor was supplied alongside a works_offset above zero. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Guidance on a page that needs a caveat: a query nothing matched, an offset or works_offset past the end of a list that did match, a page whose offset + rows would pass one of the route offset ceilings, cut to the rows that fit (naming the rows requested and applied), a page that reaches one of those ceilings with records still unretrieved (naming the route to them), a returned funder that the Funder Registry has deprecated in favor of another entry, or a query or funder_doi supplied blank with nothing else to search by, so the page lists every funder unfiltered. Absent otherwise. A page needing more than one caveat carries them all in this one string."
},
"funders": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Funder registry ID"
},
"uri": {
"type": "string",
"description": "Funder registry URI"
},
"name": {
"type": "string",
"description": "Funder canonical name"
},
"country": {
"type": "string",
"description": "Free-text place the registry records for this funder. Usually a country name, but supranational entries carry values like \"European Union\" — there is no machine-readable country code on the record."
},
"altNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "Alternate names for this funder"
},
"replaces": {
"type": "array",
"items": {
"type": "string"
},
"description": "Registry IDs this funder supersedes. Present only when the funder has absorbed a deprecated entry; works registered against those IDs are not counted here."
},
"replacedBy": {
"type": "array",
"items": {
"type": "string"
},
"description": "Registry IDs that supersede this funder. Present only on a deprecated entry — a deprecated funder carries a fraction of its successor's works, so re-run with funder_doi set to one of these IDs to reach the current entry. Pass a bare ID (\"501100004543\") or the full DOI."
},
"worksCount": {
"type": "number",
"description": "Works asserting this registry ID alone — the total a crossref_search_works funder filter on this ID returns, not counting the funder's registry descendants. Returned on a funder_doi lookup; name-query rows omit it."
},
"descendants": {
"type": "array",
"items": {
"type": "string"
},
"description": "Registry IDs of every funder below this one in the Funder Registry hierarchy, at every depth, bare as Crossref lists them (\"100000054\"). Present on a funder_doi lookup of a funder that has any; name-query rows never carry it. The funded-works list counts their works; to count that scope with filters, OR this funder's ID and each of these in a crossref_search_works funder filter: {\"funder\": \"<id>,funder:<descendant>,…\"}."
}
},
"description": "Funder record",
"additionalProperties": false
},
"description": "Matching funder records"
},
"nextOffset": {
"type": "number",
"description": "Value to pass as offset on the next call for the following page of funders. Absent when this page ends the matches or reaches the 100000-record offset ceiling."
},
"fundedWorks": {
"type": "array",
"items": {
"type": "object",
"required": [
"doi"
],
"properties": {
"doi": {
"type": "string",
"description": "Work DOI"
},
"type": {
"type": "string",
"description": "Work type"
},
"title": {
"type": "string",
"description": "Work title"
},
"published": {
"type": "object",
"properties": {
"year": {
"type": "number",
"description": "Year"
},
"month": {
"type": "number",
"description": "Month"
}
},
"description": "Publication date — the first of published, published-print, and published-online that names one. A component Crossref records as unknown is omitted, and so is every component below it.",
"additionalProperties": false
},
"isReferencedByCount": {
"type": "number",
"description": "Incoming citation count"
}
},
"description": "Work summary",
"additionalProperties": false
},
"description": "Page of works funded by the matched funder, newest first — by publication date on an offset page, by Crossref registration date on a works_cursor page. Only present when include_works is true."
},
"funderCount": {
"type": "number",
"description": "Number of funder records returned in this page"
},
"fundersTotal": {
"type": "number",
"description": "Total funder records matching the query in Crossref"
},
"nextWorksCursor": {
"type": "string",
"description": "Value to pass as works_cursor on the next call for the following page of funded works. Present only on a page requested with works_cursor, and absent once the walk reaches the end of the works list."
},
"nextWorksOffset": {
"type": "number",
"description": "Value to pass as works_offset on the next call for the following page of funded works. Absent when this page ends the works list, reaches the 10000-record offset ceiling, or was requested with works_cursor."
},
"fundedWorksTotal": {
"type": "number",
"description": "Total count of funded works for the matched funder, including works funded by its registry descendants, when include_works is true"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}crossref_get_member — Get Member by ID
Resolves a Crossref member ID to its publisher/organization record: primary name, alternate imprint names, owned DOI prefixes, registered DOI counts, a per-work-type breakdown, and per-category metadata deposit coverage. Members are the organizations that register DOIs with Crossref, so this answers "what does this publisher publish, and how completely do they deposit metadata?" The member that holds a DOI now is the memberId crossref_get_work or crossref_search_works returns for it — pass that ID here. crossref_get_prefix returns the member a DOI prefix record names, the prefix's registrant, which for a transferred title is a former publisher rather than the DOI's current one.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"member_id"
],
"properties": {
"member_id": {
"type": "integer",
"maximum": 9007199254740991,
"description": "Crossref member ID — a positive integer, e.g. 297 (Springer) or 340 (PLOS). A DOI's current member is the memberId crossref_get_work returns for it.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"id"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"id": {
"type": "number",
"description": "Crossref member ID"
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"member_not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `member_not_found`: No Crossref member exists for the given ID. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"names": {
"type": "array",
"items": {
"type": "string"
},
"description": "Alternate and imprint names registered under this member"
},
"counts": {
"type": "object",
"properties": {
"totalDois": {
"type": "number",
"description": "Total DOIs registered by this member (current + backfile)"
},
"currentDois": {
"type": "number",
"description": "DOIs registered in the current (recent) window"
},
"backfileDois": {
"type": "number",
"description": "DOIs registered in the backfile (older) window"
}
},
"description": "Registered DOI counts",
"additionalProperties": false
},
"coverage": {
"type": "array",
"items": {
"type": "object",
"required": [
"category"
],
"properties": {
"current": {
"type": "number",
"description": "Coverage fraction (0–1) among current (recent) DOIs"
},
"backfile": {
"type": "number",
"description": "Coverage fraction (0–1) among backfile (older) DOIs"
},
"category": {
"type": "string",
"description": "Metadata deposit category, e.g. \"references\", \"abstracts\", \"orcids\", \"funders\", \"licenses\", \"affiliations\""
}
},
"description": "Metadata deposit coverage for one category",
"additionalProperties": false
},
"description": "Per-category metadata deposit coverage — each a 0–1 fraction split into current (recent) and backfile (older) DOIs. Signals how completely this publisher deposits references, abstracts, ORCIDs, funders, licenses, and similar metadata."
},
"deposits": {
"type": "boolean",
"description": "Whether the member deposits any metadata with Crossref"
},
"location": {
"type": "string",
"description": "Publisher location (city, region, country)"
},
"prefixes": {
"type": "array",
"items": {
"type": "string"
},
"description": "DOI prefixes owned by this member, e.g. \"10.1038\""
},
"primaryName": {
"type": "string",
"description": "Primary publisher/organization name"
},
"worksByType": {
"type": "array",
"items": {
"type": "object",
"required": [
"type",
"count"
],
"properties": {
"type": {
"type": "string",
"description": "Crossref work type, e.g. \"journal-article\", \"book-chapter\", \"posted-content\""
},
"count": {
"type": "number",
"description": "Number of DOIs of this type registered by the member"
}
},
"description": "DOI count for one work type",
"additionalProperties": false
},
"description": "DOI counts broken down by work type (all DOIs), sorted by count descending"
},
"depositsArticles": {
"type": "boolean",
"description": "Whether the member deposits journal-article metadata"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"idempotentHint": true
}crossref_get_prefix — Get Prefix Owner
Resolves a DOI prefix — the registrant portion of a DOI, e.g. "10.1038" — to the Crossref member its prefix record names: the publisher name and numeric member ID. That member is the prefix's registrant, so for a title transferred to another publisher it is not the member holding the DOI now — crossref_get_work's memberId names that one. The Crossref prefix record carries only these three facts (no counts, coverage, or flags); the returned memberId chains directly into crossref_get_member for the full publisher record.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"prefix"
],
"properties": {
"prefix": {
"type": "string",
"pattern": "^10\\.\\d+$",
"description": "DOI prefix in the format \"10.NNNN\" — the registrant portion of a DOI with no \"/suffix\", e.g. \"10.1038\" or \"10.1371\"."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"prefix"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_unavailable",
"malformed_response",
"request_timeout",
"prefix_not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Crossref answered HTTP 429 and the limit did not clear inside the retry budget. `upstream_unavailable`: Crossref was unreachable, returned a 5xx status, or served an HTML error page instead of JSON. `malformed_response`: Crossref returned HTTP 200 with a body that is not valid JSON. `request_timeout`: Crossref did not respond within CROSSREF_TIMEOUT_MS, or answered HTTP 408/504. `prefix_not_found`: The DOI prefix is not registered with Crossref. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"prefix": {
"type": "string",
"description": "The DOI prefix that was resolved, e.g. \"10.1038\""
},
"memberId": {
"type": "number",
"description": "Numeric Crossref member ID the prefix record names — pass to crossref_get_member for the full publisher record. For a transferred title it is a former publisher; a DOI's current member is the memberId crossref_get_work returns."
},
"ownerName": {
"type": "string",
"description": "Name of the member (publisher) the prefix record names — its registrant"
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"idempotentHint": true
}Prompts
0 advertised
Resources
1 advertised
crossref-guide
Judgment for the crossref_* tools — confirming a citation match, choosing between the journal/funder works lists and a filtered crossref_search_works, Crossref filter syntax (OR on one key, date bounds, types), DOI prefix versus current publisher, and deposit-dependent coverage. Use when resolving references to DOIs, listing a journal's, funder's, or publisher's works, counting works, or screening for retractions.
skill://crossref-guide/SKILL.mdio.modelcontextprotocol/skills
skill://{+path}90-day uptime
A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.
- 2026-11-07: 2 of 2 checks answered
- 2026-11-09: 1 of 1 checks answered
- 2026-11-10: 1 of 1 checks answered
30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 698 ms
- 95th percentile latency
- 698 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 698 ms · metadata changed · 7 tool(s), 1 resource(s), 1 resource template(s), 0 prompt(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http crossref-mcp-server-caseyjhand https://crossref.caseyjhand.com/mcpCodex
codex mcp add crossref-mcp-server-caseyjhand --url https://crossref.caseyjhand.com/mcpCursor
{
"mcpServers": {
"crossref-mcp-server-caseyjhand": {
"url": "https://crossref.caseyjhand.com/mcp"
}
}
}Cline
{
"mcpServers": {
"crossref-mcp-server-caseyjhand": {
"type": "streamableHttp",
"url": "https://crossref.caseyjhand.com/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"crossref-mcp-server-caseyjhand": {
"type": "http",
"url": "https://crossref.caseyjhand.com/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http crossref-mcp-server-caseyjhand https://crossref.caseyjhand.com/mcpClaude Desktop
https://crossref.caseyjhand.com/mcpWindsurf
{
"mcpServers": {
"crossref-mcp-server-caseyjhand": {
"serverUrl": "https://crossref.caseyjhand.com/mcp"
}
}
}Let your agent find its tools
Find servers like crossref-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.