@cyanheads/datacite-mcp-server
Search DataCite datasets and software, fetch DOI metadata, trace relations, format citations via MCP. STDIO or Streamable HTTP.
6 Tools
Overview
DOI metadata from DataCite for datasets, software, samples, workflows, and other research outputs that repositories deposit worldwide. Search it, open a record in full, trace a DOI's relations (versions, parts, supplements, citations), find the repositories that publish in a field, and format citations. No API key required.
Tools
Capability reference
datacite_search_works tool
text (literal words, with angle brackets removed; nonblank text that leaves no searchable clause fails as invalid_text) or query (OpenSearch query syntax), ANDed with filters: resource_types, creator (ORCID iD or name), affiliation (ROR ID or name), affiliation_country, funder (ROR ID, Crossref Funder ID, or name; include_child_funders with a ROR funder), subject, fields_of_science, repository_ids, provider_ids, licenses, language, place, published_from / published_to, and min_citations; list filters take up to 10 values, any of which match
- Positive integer
limit (default 20), applied at most 1,000 with a requested/applied notice; ranked pages reach the first 10,000 matches under sort (relevance, newest, oldest, recently_updated, most_cited, most_viewed, most_downloaded; default relevance with text or query, else newest), while cursor: "*" walks the whole result set in registration order through nextCursor and takes neither page nor sort. A ranked ceiling stop is distinguished from the last page
affiliation_country accepts current and formerly assigned ISO alpha-2 codes plus XK, in any case. UK maps to GB with a notice; unassigned codes fail as invalid_filter before searching
- Crossref Funder IDs resolve through one exact ROR FundRef mapping before searching
funded-by; an unavailable, incomplete, or ambiguous mapping keeps literal identifier matching and discloses reduced coverage. The cached, paced lookup takes at most five seconds without retry. A cursor walk keeps its initial resolved or literal filter on every continuation without another ROR lookup, including across server restarts. include_child_funders still requires an explicitly supplied ROR
- License ids are trimmed and lowercased, preserving internal spaces (
cc0 1.0 differs from cc0-1.0); reuse the licenses facet ids exactly. query also supports deposition timestamps, e.g. created:[2026-10-16 TO *], updated, and registered
include_facets adds top counts for resource types, years, repositories, providers, affiliations, fields of science, and licenses (2–8 s slower); every response reports totalCount, effectiveQuery, sortApplied, and appliedFilters
datacite_get_work tool
- One
doi, in any case: bare, with a doi: or info:doi/ prefix, as a doi.org URL, or %2F-encoded
- Titles, creators and contributors with ORCID iDs and ROR affiliations, dates, subjects, descriptions, funding, geolocations, rights, related identifiers and items, repository and provider, and
counts (citations, references, versions, parts, views, downloads); long lists are capped and their full sizes reported in truncatedLists and relatedIdentifierCounts
- Page one capped
list with a zero-based offset (default 0): creators, contributors, relatedIdentifiers, subjects, and fundingReferences return up to 100 entries, relatedItems 25, and geoLocations 50. Offsets index valid mapped entries in deposit order. listPage reports list, offset, total, and nextOffset while entries remain; other metadata keeps its normal caps. Default truncation notices give exact continuation inputs. An offset requires a list; past-end pages are empty
- Titles and search snippets are plain text, with recognized formatting tags removed and entities decoded once before snippet truncation, including all semicolon-terminated WHATWG named references such as
α and β. Structured descriptions keep their deposited text; rendered descriptions preserve paragraph and line boundaries
- Geolocations preserve points, boxes, ordered polygon vertices, and optional polygon interior points. Incomplete polygons are omitted with a notice; valid sibling geometry remains
- A DOI DataCite holds no public record for returns
found: false with missReason (other_agency, does_not_exist, not_public, unclassified), the registrationAgency when known, and guidance
datacite_trace_relations tool
- Any agency's
doi: a DataCite dataset or software DOI, or a journal article's DOI to find the DataCite data and software it cites or that cite, supplement, or derive from it
depth 1 (default) or 2 — the second hop expands at most 10 DataCite neighbours and runs only when the first hop leaves max_nodes room; positive integer max_nodes (default 50, root included), applied at most 100 with a notice, fills with own-metadata targets, then records pointing at the root, then Event Data endpoints, then the second hop; relation_types keeps only the listed relation types, read from the traced DOI's side (omitted: all); include_event_data (default true) adds Event Data citation links
- Edges keep
relationType exactly as asserted and list their sources (metadata, reverse_metadata, event_data); coverage states how much of each source was read (up to 100 reverse records per hop, only the first 10 when those are large, and up to 100 events per call), and rootCounts carries DataCite's own counts for comparison. An absent edge is not evidence that no relationship exists
- Reverse lookups inspect both
relatedIdentifiers and identified relatedItems, verify each assertion's target and type, and merge identical edges
event_offset (0–9,999, absolute and zero-based) selects Event-Data-only tracing, including when set to 0: metadata edges and depth-2 expansion are skipped. Continue with coverage.eventData.nextEventOffset and the same DOI and filters. Each call reads one upstream page and stops before an eligible endpoint that cannot fit max_nodes; root-only calls explain how to raise the cap. Coverage distinguishes exhaustion, a blocked classification lookup, unavailable/skipped reads, and the upstream 10,000-result ceiling. Upstream ordering can change between calls; continuations are not snapshots
datacite_search_repositories tool
query text over names and descriptions, plus field_of_science, repository_types, certificates, software, client_type, and provider_id; or repository_ids alone (up to 1,000 inputs) to look up the first 25 distinct normalized ids in input order. A notice names unqueried ids; missing-id notices cover queried ids only
- Positive integer
limit (default 20), applied at most 1,000 with a requested/applied notice, with page; name-ordered rows carry the repositoryId that datacite_search_works takes in repository_ids, plus providerId, types, certificates, software, subjects, homepage, and re3data link
- Search responses echo the composed
effectiveQuery (* without query clauses), including empty pages; ID lookups report appliedFilters.repository_ids without a query echo
datacite_get_citation tool
format: text (default), csl_json, bibtex, ris, datacite_json, datacite_xml, schema_org, codemeta, or jats
- For
text only: style takes a CSL style id (default apa; verified ids under datacite_list_reference topic citation_styles), and one DataCite would silently render as APA fails as unsupported_style; locale takes one of the 61 CSL locales DataCite renders (en-GB, de-DE, fr-FR, …) or a bare language code that expands to its primary dialect (de → de-DE), default en-US (list: topic citation_locales)
text returns the citation as plain text in citation and the upstream markup in citationHtml; text payloads over 100,000 UTF-16 code units are cut without splitting a surrogate pair. Machine formats return complete payloads inline up to that cap; larger documents omit citation and return downloadUrl for the full document and totalCharacters, with truncated: true, shown: 0, and cap: 100000. A DOI DataCite doesn't hold returns found: false as in datacite_get_work; a format DataCite can't render for that DOI fails as format_unavailable
- JATS stays verbatim. A notice reports empty name elements or a year that disagrees with its ISO date, including when the document is returned by download URL; use
csl_json, bibtex, ris, or datacite_json for another representation
datacite_list_reference tool
topic: resource_types, relation_types, identifier_types, date_types, contributor_types, fields_of_science, licenses, repository_types, certificates, software_platforms, client_types, sort_orders, query_syntax, citation_formats, citation_styles, citation_locales, identifier_formats, or coverage
- Returns
entries (value, label, group, inverse) and usage notes; makes no upstream request
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
The published datacite-guide skill covers attribution, identifier resolution, relation evidence, and complete inventories. It ships with the package and plugins and is available through the MCP Skills extension.
DataCite-specific:
- Keyless and read-only: the DataCite REST API (
/dois, /repositories, Event Data at /events, and DOI content negotiation for citations), plus the doi.org registration-agency lookup for DOIs DataCite doesn't hold
- Shared request budget: one paced request queue, an in-process response cache, and a 45 s deadline per tool call; a spent budget fails as
rate_limited with the wait in seconds. DATACITE_CONTACT_EMAIL moves requests to DataCite's identified tier, 1,000 requests per 5 minutes per IP instead of 500
- Forgiving identifiers: DOIs in any case, bare or prefixed or as doi.org URLs; ORCID iDs with their checksum verified; ROR IDs and Crossref Funder IDs bare or as URLs; vocabulary values in any case, with hyphens, underscores, or spaces
- DataCite DOI metadata is waived under CC0 1.0; the waiver covers the deposited metadata only, not the datasets, software, or papers it describes.
datacite_get_work reports metadataLicense separately from the work's own rights
Agent-friendly output:
- Provenance on every search:
effectiveQuery, appliedFilters as sent upstream, the sort or order applied, and totalCount; trace edges name their sources, and coverage says how much of each source was read
- Typed misses instead of errors: a DOI DataCite doesn't hold comes back
found: false with a missReason, the agency that holds it when known, and the next step
- Disclosed limits: capped lists report their full counts, and notices explain zero hits, the 10,000-match paging ceiling, an unexpanded second hop, and citations the returned edges don't account for
- In the text output, depositor-supplied text (titles, descriptions, names) is blockquoted or flattened and machine payloads are fenced, so deposited content can't pose as structure. Titles and snippets are normalized on both surfaces; structured descriptions and other deposited fields retain their original text
Getting started
Connect to the hosted instance, no install:
Every caller of the hosted instance shares one DataCite request budget of 800 requests per 5 minutes. For sustained use, run your own instance.
Or run it locally:
For other clients, add this to the MCP config file:
No API key is needed. Set the optional DATACITE_CONTACT_EMAIL to double DataCite's per-IP request allowance. For Docker or a source build, see Running the server.
Configuration
See .env.example for the full list of optional overrides.
Running the server
Requires Bun v1.4.0 or higher, or Node.js v24+. To build from source:
Set DATACITE_CONTACT_EMAIL in .env to use DataCite's identified tier.
Local development
Docker
Run over stdio:
Or expose Streamable HTTP at http://localhost:3010/mcp:
Project structure
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catch in tool logic
- Use
ctx.log for logging, ctx.state for storage
- Register new tools in the
createApp() arrays in src/index.ts
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.