crossref-mcp-server

v0.3.9 pre-1.0

Resolve DOIs, search ~155M scholarly works, fetch references, and look up publishers via the Crossref REST API. STDIO or Streamable HTTP.

crossref.caseyjhand.com/mcp
claude mcp add --transport http crossref-mcp-server https://crossref.caseyjhand.com/mcp
codex mcp add crossref-mcp-server --url https://crossref.caseyjhand.com/mcp
{
  "mcpServers": {
    "crossref-mcp-server": {
      "url": "https://crossref.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http crossref-mcp-server https://crossref.caseyjhand.com/mcp
{
  "mcpServers": {
    "crossref-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://crossref.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "crossref-mcp-server": {
      "type": "http",
      "url": "https://crossref.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://crossref.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

7

crossref_get_work

Resolves a DOI to its full Crossref metadata record: title, authors, affiliations, abstract (when deposited), journal or container, 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. 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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_get_work",
    "arguments": {
      "doi": "<doi>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "doi": {
      "type": "string",
      "pattern": "^10\\.\\d{4,9}\\/\\S+$",
      "description": "DOI in the format \"10.NNNN/suffix\", e.g. \"10.1038/nature12373\". Must start with \"10.\" followed by 4–9 digits and a slash."
    },
    "offset": {
      "default": 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.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of authors to return in one page (1–500, default 25). Ordinary records fit in a single page; large-collaboration papers in particle physics and genomics deposit thousands.",
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    }
  },
  "required": [
    "doi",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

crossref_get_references

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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_get_references",
    "arguments": {
      "doi": "<doi>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "doi": {
      "type": "string",
      "pattern": "^10\\.\\d{4,9}\\/\\S+$",
      "description": "DOI in the format \"10.NNNN/suffix\", e.g. \"10.1038/nature12373\". Must start with \"10.\" followed by 4–9 digits and a slash."
    },
    "offset": {
      "default": 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.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 100,
      "description": "Maximum number of references to return in one page (1–500, default 100). Most works fit in a single page; bibliography records can carry tens of thousands.",
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    }
  },
  "required": [
    "doi",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

crossref_search_works

open-world

Searches the Crossref works index (~155M 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. 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. 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. 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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_search_works",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "description": "Free-text search query, e.g. \"CRISPR gene editing\" or \"climate change adaptation\"",
      "type": "string"
    },
    "queryBibliographic": {
      "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\".",
      "type": "string"
    },
    "queryTitle": {
      "description": "Match against work titles only, e.g. \"Array programming with NumPy\".",
      "type": "string"
    },
    "queryAuthor": {
      "description": "Match against author names only, e.g. \"Charles R. Harris\".",
      "type": "string"
    },
    "queryContainerTitle": {
      "description": "Match against the container title (journal or book name) only, e.g. \"Nature\".",
      "type": "string"
    },
    "filter": {
      "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\",\"directory\":\"DOAJ\"}",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string"
      }
    },
    "fields": {
      "description": "Fields to return (reduces payload). Names are case-sensitive. Useful set: DOI, title, author, published, type, is-referenced-by-count, abstract, container-title, publisher, score. DOI is always returned whether or not it is listed here, so every result stays resolvable by crossref_get_work.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "rows": {
      "default": 20,
      "description": "Number of results to return per page (1–100, default 20)",
      "type": "number",
      "minimum": 1,
      "maximum": 100
    },
    "authorLimit": {
      "default": 25,
      "description": "Maximum number of authors to return per work (1–500, default 25). 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.",
      "type": "integer",
      "minimum": 1,
      "maximum": 500
    },
    "offset": {
      "description": "Zero-based result offset for offset-based paging. Cannot be used with cursor. Capped at ~10K; use cursor for deeper paging.",
      "type": "number",
      "minimum": 0
    },
    "cursor": {
      "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. Cannot be combined with offset.",
      "type": "string"
    },
    "sort": {
      "description": "Sort field",
      "type": "string",
      "enum": [
        "relevance",
        "score",
        "is-referenced-by-count",
        "published",
        "published-print",
        "published-online",
        "deposited",
        "indexed",
        "created",
        "updated",
        "references-count"
      ]
    },
    "order": {
      "description": "Sort direction (asc or desc)",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    }
  },
  "required": [
    "rows",
    "authorLimit"
  ],
  "additionalProperties": false
}
view source ↗

crossref_search_journals

open-world

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 by publication date; that list pages two ways. works_offset is the simple one and is capped ten times lower at works_offset + rows = 10000. works_cursor has no ceiling and reaches the whole works list: 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 newest work — it cannot resume from an offset. Returns journal metadata: title, publisher, ISSN-L, subject areas, and total DOI count.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_search_journals",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "description": "Journal title search query, e.g. \"Nature\" or \"Journal of Machine Learning Research\"",
      "type": "string"
    },
    "issn": {
      "description": "ISSN for exact single-journal lookup (print or electronic, with or without hyphen). Example: \"1234-5678\".",
      "type": "string",
      "pattern": "^\\d{4}-?\\d{3}[\\dX]$"
    },
    "include_works": {
      "default": false,
      "description": "When true, also return a page of the journal's most recent works by publication date. Requires an unambiguous journal — pass issn when a title query matches more than one. A journal with no ISSN registered has no addressable works list; the works lookup is then skipped and the notice enrichment says so.",
      "type": "boolean"
    },
    "rows": {
      "default": 10,
      "description": "Maximum number of journals to return for title queries, or works when include_works is true (1–100, default 10)",
      "type": "number",
      "minimum": 1,
      "maximum": 100
    },
    "offset": {
      "default": 0,
      "description": "Zero-based offset into the title-query journal list. Pass the nextOffset value from the previous response to continue. Ignored when issn is set, which resolves exactly one record.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "works_offset": {
      "default": 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; use works_cursor to read the whole list. Cannot be combined with works_cursor.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "works_cursor": {
      "description": "Cursor token for deep paging of the journal works list when include_works is true. Pass \"*\" to start the walk at the newest work, then pass the nextWorksCursor value from each response. Has no offset ceiling and cannot be combined with works_offset. Each token runs about 1500 characters and is returned on both result surfaces, a fixed cost per page — raise rows to spread it across more works on a long walk.",
      "type": "string"
    }
  },
  "required": [
    "include_works",
    "rows",
    "offset",
    "works_offset"
  ],
  "additionalProperties": false
}
view source ↗

crossref_search_funders

open-world

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 works funded by the matched funder; that list pages two ways. works_offset is the simple one and is capped ten times lower at works_offset + rows = 10000. works_cursor has no ceiling and reaches the whole funded-works list: 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 newest 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. Returns funder name, registry ID, country, and alternate names. 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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_search_funders",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "query": {
      "description": "Funder name search query, e.g. \"National Science Foundation\" or \"Wellcome Trust\"",
      "type": "string"
    },
    "funder_doi": {
      "description": "Funder DOI for exact lookup — the full DOI \"10.13039/100000001\" (NSF) or the bare registry ID \"100000001\". Supersedes query when provided.",
      "type": "string",
      "pattern": "^(?:(?:https?:\\/\\/(?:dx\\.)?doi\\.org\\/|doi:)?10\\.13039\\/)?\\d+$"
    },
    "include_works": {
      "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.",
      "type": "boolean"
    },
    "rows": {
      "default": 10,
      "description": "Maximum funders to return for name queries, or works when include_works is true (1–100, default 10)",
      "type": "number",
      "minimum": 1,
      "maximum": 100
    },
    "offset": {
      "default": 0,
      "description": "Zero-based offset into the name-query funder list. Pass the nextOffset value from the previous response to continue. Ignored when funder_doi is set, which resolves exactly one record.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "works_offset": {
      "default": 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; use works_cursor to read the whole list. Cannot be combined with works_cursor.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "works_cursor": {
      "description": "Cursor token for deep paging of the funded-works list when include_works is true. Pass \"*\" to start the walk at the newest work, then pass the nextWorksCursor value from each response. Has no offset ceiling and cannot be combined with works_offset. Each token runs about 1500 characters and is returned on both result surfaces, a fixed cost per page — raise rows to spread it across more works on a long walk.",
      "type": "string"
    }
  },
  "required": [
    "include_works",
    "rows",
    "offset",
    "works_offset"
  ],
  "additionalProperties": false
}
view source ↗

crossref_get_member

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?" Resolve a DOI prefix (e.g. "10.1038") to its member ID with crossref_get_prefix, then pass that ID here.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_get_member",
    "arguments": {
      "member_id": "<member_id>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "member_id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Crossref member ID — a positive integer, e.g. 297 (Springer) or 340 (PLOS). Resolve a DOI prefix to a member ID first with crossref_get_prefix."
    }
  },
  "required": [
    "member_id"
  ],
  "additionalProperties": false
}
view source ↗

crossref_get_prefix

Resolves a DOI prefix — the registrant portion of a DOI, e.g. "10.1038" — to its owning Crossref member: the publisher name and numeric member ID. Answers "who publishes DOIs starting with 10.1038?" 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.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "crossref_get_prefix",
    "arguments": {
      "prefix": "<prefix>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "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\"."
    }
  },
  "required": [
    "prefix"
  ],
  "additionalProperties": false
}
view source ↗