Zenodo Mcp Server

io.github.cyanheadsPublished by GitHub account cyanheadsv0.2.0Updated Oct 29, 2026

Search and resolve Zenodo datasets, software, and publications by DOI; trace versions and funding.

VerifiedStreamable HTTPWeb executableWeb Search & ScrapingKnowledge & Memory

Overview

AI-generated overview

Searches and resolves Zenodo research deposits — datasets, software and publications — by keyword, DOI or record id, with versions, files and funding metadata.

What you can do
  • Search deposits by keyword with filters for resource type, community, funder, grant, ORCID, file type, license, access and dates, with facet counts
  • Resolve a record from an id, Zenodo DOI, concept DOI or URL to full metadata, creators, funding, rights and an optional citation
  • List every version in a deposit's version series, newest first
  • Page a deposit's file manifest or list the members of a .zip archive
  • Read a byte-capped UTF-8 excerpt of one text file or archive member
  • Resolve community, funder, award, license and resource-type names to the ids search filters accept
When to use it
Useful when an assistant needs to find or verify citable research outputs, trace which release of a dataset or software deposit to cite, follow a deposit's version history, or check funding and licensing details before reusing data.
Requirements
Either the hosted HTTP endpoint or a local process via npx (Node.js; Bun v1.4.0+ or Node.js v24+ for a source build). No account is required for anonymous reads; an optional Zenodo personal access token in ZENODO_ACCESS_TOKEN raises rate limits. Network access to Zenodo is needed.
Before you install
The hosted instance shares Zenodo's anonymous rate limit across all callers, so tools can fail with rate_limited. A token authenticates as the account that created it and can expose restricted records that account owns, so use a dedicated account on shared servers. Titles, descriptions and file contents are depositor-supplied and should be treated as untrusted data.

Installation

In SourceWeft

  1. Open Zenodo Mcp Server in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/io.github.cyanheads%2Fzenodo-mcp-server/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "zenodo": {
      "type": "http",
      "url": "https://zenodo.caseyjhand.com/mcp"
    }
  }
}

README

@cyanheads/zenodo-mcp-server

Search and resolve Zenodo datasets, software, and publications by DOI; trace versions and funding, list files, and preview text files via MCP. STDIO or Streamable HTTP.

6 Tools

Public Hosted Server: https://zenodo.caseyjhand.com/mcp


Overview

Datasets, software releases, and publications from Zenodo, CERN's open research repository. Search deposits with filters for funder, grant, community, license, and file type; resolve any Zenodo DOI, concept DOI, or URL to its record; walk a deposit's versions; and list, open, and preview its files, including members of .zip archives.

Tools

The published zenodo-guide covers selecting a cited release, resolving funding identities, and checking inspection coverage. Plugin installs include it; MCP clients can discover it through the Skills extension and its skill resources.

ToolDescription
zenodo_search_recordsSearch deposits by keyword plus type, community, funder, grant, ORCID, file type, license, access, and date filters, with facet counts
zenodo_get_recordResolve one deposit from a record id, DOI, concept DOI, or URL to its full metadata, first 25 files, and an optional citation
zenodo_list_versionsList every version of a deposit's version series, newest first
zenodo_list_filesPage a deposit's file manifest, or list the members of one of its .zip files
zenodo_read_fileRead a byte-capped text excerpt of one file or .zip member
zenodo_lookup_vocabularyResolve community, funder, grant, license, and resource-type names to the ids search filters take

Capability reference

zenodo_search_records tool

  • Keyword query (terms OR-ed unless joined with AND, quoted phrases, field syntax such as metadata.title:"…") plus resource_type, community, funder, award, creator_orcid, file_type, license, access_status, and published_from / published_to
  • Up to 25 hits per page; only the first 10,000 matches are reachable (result_window_exceeded past that); latest versions only unless all_versions is true
  • sort: bestmatch, newest, oldest, mostviewed, mostdownloaded, updated-desc, updated-asc (default bestmatch with a query, newest without)
  • Each hit carries record and concept ids and DOIs, type, version, creators, license ids, access, file totals, and unique views and downloads; facets count resource types, access statuses, file types, subjects, and years over the full match set
  • A bare DOI or record URL as query fails as query_is_identifier; a community or funder Zenodo doesn't know fails as unknown_community / unknown_funder
  • Invalid search syntax fails as query_syntax, naming the offending token:
    • With a filter other than community or all_versions set, any invalid syntax fails (climate AND AND model, metadata.title: with no value, 'single quotes', an unclosed [a TO b]), because Zenodo would search it as plain text and drop the filters. Without such a filter it runs as plain text and the notice says so
    • Constructs Zenodo cannot run fail with or without a filter, unless the query already runs as plain text: an unclosed / (escape a literal one as \/), a trailing ! (climate\!), a range bound ending in a backslash (double-quote a bound holding a space, ], or }: [a TO "b]"]), and a boost, fuzziness, or proximity value Zenodo cannot convert (climate^1.2.3, "x y"~0.5; write ^2, ~0.5, "x y"~2)
    • An unclosed " inside a word (12" vinyl; escape it as 12\") fails without a filter; with one, the phrase is closed at the end of the query and the closed query is checked instead

zenodo_get_record tool

  • id takes a record id, a Zenodo DOI, a concept DOI or concept record id (resolves to the latest version), another DOI registered to a Zenodo record, or a zenodo.org / doi.org URL; input_kind and resolved_from report how it resolved
  • Returns the description as plain text (up to 4,000 characters), creators and contributors with ORCIDs and ROR affiliations, rights, access and embargo, funding, related identifiers, communities, version position with latest_recid, usage counts, and the first 25 files
  • Every capped field reports its total, and each cut except the file list (paged with zenodo_list_files) is listed in continuations with a cursor and the record id it is bound to; pass both back to get that field's next window (8,000 characters of text, or the next page of entries)
  • citation_style: bibtex, csl-json, apa, chicago-author-date, harvard-cite-them-right, ieee, modern-language-association, or nature
  • A miss returns found: false with miss_kind (not_found, deleted, restricted, not_on_zenodo) and guidance; a deleted record adds its removal tombstone

zenodo_list_versions tool

  • Takes the same id forms as zenodo_get_record; a concept DOI and any version's DOI list the same series
  • Newest first, up to 25 per page; reports total_versions, latest_recid, and the concept record id and DOI
  • Each version carries its record id, DOI, version label, publication date, index, is_latest, file totals, and unique views and downloads
  • Misses return found: false with the same miss_kind, guidance, and tombstone as zenodo_get_record

zenodo_list_files tool

  • Manifest entries carry key, size, MIME type, MD5, download URL, previewable, and listable (a .zip); up to 200 per page via offset / limit
  • archive_key lists the members of one .zip; Zenodo lists at most 1,000 files and directories per archive, flagged by upstream_truncated
  • key_contains filters keys or member paths case-insensitively before paging
  • Restricted and embargoed files return the access status and embargo date with no entries; unknown and deleted records fail as record_not_found / record_deleted

zenodo_read_file tool

  • key from zenodo_list_files, plus archive_member to read inside a .zip without downloading it
  • max_bytes from 256 (default 16,384), at most 65,536 per call (larger values return 65,536); continue a top-level file from next_offset; .zip members read from byte 0 only
  • status is text, not_text, restricted, or empty; excerpts end on a line or character boundary
  • Extensionless files typed application/octet-stream (LICENSE, Makefile) are returned only when their content is UTF-8 text
  • Every result carries the deposit's rights and the download_url

zenodo_lookup_vocabulary tool

  • vocabulary: communities, funders, awards, licenses, or resource_types; query by name, acronym, or keyword, or omit it to browse
  • Each entry's filter_param and filter_value name the zenodo_search_records filter and the id to pass it
  • funder scopes awards to one funder, as a ROR id, ROR URL, or Crossref Funder DOI
  • Up to 25 entries per page; only the first 10,000 entries of a list are reachable, reported as reachable beside total

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.

Zenodo-specific:

  • Accepts a record id, Zenodo DOI, concept DOI, external DOI, or zenodo.org / doi.org URL in every tool that takes id; URLs are parsed locally and never fetched
  • Search filters compose into Zenodo's query, so facet counts agree with total; community and funder values are checked before the search runs
  • Separate request pacers for Zenodo's search and general rate-limit buckets, backed by its X-RateLimit-* headers, plus a process-local cache (records 5 min, searches 60 s, vocabularies 1 h)
  • Byte-range file reads and .zip member reads, with binary detection before any text is returned

Agent-friendly output:

  • Misses as data: zenodo_get_record and zenodo_list_versions return found: false with a typed miss_kind, next-step guidance, and a deleted record's tombstone
  • Depositor-supplied text is labeled untrusted: descriptions render as quoted blocks, file content in a code fence, and HTML descriptions are converted to plain text
  • Search responses echo the effectiveQuery sent to Zenodo and the appliedSort, and every paged tool reports totals with a next page or offset
  • Fields Zenodo omits stay absent rather than defaulting to 0, '', or false

Getting started

Connect to the hosted instance, no install:

sh
claude mcp add --transport http zenodo-mcp-server https://zenodo.caseyjhand.com/mcp

Or run it locally:

sh
claude mcp add zenodo-mcp-server -- npx -y @cyanheads/zenodo-mcp-server

For other clients, add this to the MCP config file:

json
{  "mcpServers": {    "zenodo-mcp-server": {      "command": "npx",      "args": ["-y", "@cyanheads/zenodo-mcp-server"]    }  }}

The hosted instance shares Zenodo's anonymous rate limit across callers. An optional Zenodo personal access token in ZENODO_ACCESS_TOKEN raises the rate limit of your own instance (-e ZENODO_ACCESS_TOKEN=your-token after the Claude Code server name, or an env entry). For Docker or a source build, see Running the server.

Configuration

VariableDescriptionDefault
ZENODO_ACCESS_TOKENZenodo personal access token, created with no scopes. Raises Zenodo's rate limit; tool behavior and page sizes are unchanged.none
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOGS_DIRDirectory for log files (Node.js only).<app-root>/logs
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable OpenTelemetry.false

See .env.example for the full list of optional overrides.

Rate limits

Zenodo rate-limits anonymous clients per IP address: 60 requests per minute and 2,000 per hour overall, and 30 per minute on record search. The server paces its own requests below those limits: 25 searches per minute, and 55 other requests per minute and 1,900 per hour. Every caller of one server process shares that budget. When it runs out, tools fail with rate_limited and a retryAfter in seconds.

With ZENODO_ACCESS_TOKEN set, Zenodo allows 100 requests per minute and 5,000 per hour, and the server paces other requests at 90 per minute and 4,800 per hour. Search stays at 25 per minute.

The token authenticates as the account that created it, so reads can see what that account can see, including restricted records it owns. On a shared or hosted server, create the token on a dedicated account that owns no restricted records.

Running the server

Local development

Requires Bun v1.4.0 or higher (or Node.js v24+).

  • Clone and install:

    sh
    git clone https://github.com/cyanheads/zenodo-mcp-server.gitcd zenodo-mcp-server && bun installcp .env.example .env   # optionally set ZENODO_ACCESS_TOKEN
  • Build and run the production version:

    sh
    # One-time buildbun run rebuild
    # Run the built serverbun run start:http# orbun run start:stdio
  • Run checks and tests:

    sh
    bun run devcheck  # Lints, formats, type-checks, and morebun run test      # Runs the test suite

Docker

sh
docker run --rm -p 3010:3010 ghcr.io/cyanheads/zenodo-mcp-server:latest

The image defaults to HTTP transport, stateless session mode, and logs to /var/log/zenodo-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/zenodo-mcp-server:latest"]. To build locally, docker build -t zenodo-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point: server instructions, tool registration, service setup and teardown.
src/configServer-specific environment variable parsing and validation with Zod.
src/mcp-server/toolsTool definitions (*.tool.ts) plus shared render, schema, and record-miss helpers.
src/services/zenodoZenodo service: HTTP boundary and pacers, cache, identifier parsing, query building, normalization, text previews.
tests/Unit, service, tool, and fuzz tests against recorded Zenodo fixtures.
docs/design.mdTool surface design, verified upstream behavior, and decisions log.
skills/zenodo-guide/Published research workflow guide and query reference, also served over MCP.

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; Zenodo responses are cached process-wide in the service, not in ctx.state
  • Register new tools in src/mcp-server/tools/definitions/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:

sh
bun run devcheckbun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Source: README.md at commit 0ae79d9

Tools

6

zenodo_search_records

Search Zenodo records

Search Zenodo's open research repository — datasets, software releases, publications, and other deposits — by keyword query plus filters for resource type, community, funder, grant, creator ORCID, file type, license, access status, and publication date. Returns one summary per deposit (ids, DOIs, title, type, version, creators, license, access, file totals, usage counts) plus facet counts over the full match set, every filter applied. Query terms are OR-ed unless joined with AND, quoted phrases match exactly, and field syntax works on record fields (metadata.title:"…", metadata.subjects.subject:"…"). Only the latest version of each deposit is searched unless all_versions is true, and only the first 10,000 matches are reachable by paging. Resolve community, funder, and grant names to ids with zenodo_lookup_vocabulary first; open one deposit in full with zenodo_get_record.

Parameterspagesizesortawardqueryfunderlicensecommunityfile_typeall_versionspublished_toaccess_status+3 more

zenodo_get_record

Get a Zenodo record

Resolve one Zenodo deposit from a record id, a Zenodo DOI (10.5281/zenodo.N), a concept DOI or concept record id (resolves to the latest version), another DOI deposited on Zenodo, or a zenodo.org or doi.org URL, and return its full metadata: description, creators with ORCIDs and ROR affiliations, license, access and embargo, funding and grants, related identifiers, communities, version position, usage counts, and the first 25 files. Optionally includes a formatted citation. Long text and lists are cut at display caps with their totals reported; every cut except the file list (paged with zenodo_list_files) is listed in continuations with a cursor and the record id it is bound to, and passing both back returns the next window of that field (8,000 characters of text, or the next page of entries) in place of the record. A miss (an unknown id, a deleted record with its removal tombstone, restricted metadata, or a DOI not registered on Zenodo) returns found: false with guidance instead of an error.

Parametersidrequiredcursorcitation_style

zenodo_list_versions

List Zenodo record versions

List every version of a Zenodo deposit's version series, newest first, with each version's record id, DOI, version label, publication date, file totals, and usage counts. Accepts any identifier zenodo_get_record accepts; a concept DOI and any single version's DOI list the same series. Use it to find the version a paper cited or the current release. A miss (an unknown id, a deleted record with its removal tombstone, restricted metadata, or a DOI not registered on Zenodo) returns found: false with guidance instead of an error.

Parametersidrequiredpagesize

zenodo_list_files

List Zenodo record files

Page through a Zenodo deposit's file manifest (key, size, MIME type, MD5, download URL, previewability), optionally filtered by a substring of the file key, or list the members inside one of its .zip files by setting archive_key. Restricted and embargoed files return the access status and embargo date with no entries. Read a small text file or ZIP member with zenodo_read_file.

Parametersidrequiredlimitoffsetarchive_keykey_contains

zenodo_read_file

Read a Zenodo file excerpt

Read a bounded UTF-8 excerpt (up to 64 KiB per call) of one text file in a Zenodo deposit — a README, CITATION.cff, CSV head, notebook, or script — or of one member inside a .zip file, without downloading the archive. Continue a top-level file from next_offset. A file with a text MIME type or extension is read directly; an extensionless file Zenodo types application/octet-stream (LICENSE, COPYING, Makefile) is read and returned only when its content is UTF-8 text. Binary files, restricted files, and other types return metadata and the download URL without content. File content is depositor-supplied and carries the deposit’s license.

Parametersidrequiredkeyrequiredmax_bytesoffset_bytesarchive_member

zenodo_lookup_vocabulary

Look up Zenodo filter ids

Resolve names to the ids zenodo_search_records filters on, and browse Zenodo's reference vocabularies: communities (slug), funders (ROR id), awards and grants (award id), licenses (license id), and resource types (type id). Search by name, acronym, or keyword; awards can be scoped to one funder. Each entry names the zenodo_search_records parameter its id feeds.

Parameterspagesizequeryfundervocabularyrequired

Version history

5
  1. v0.2.0LatestOct 29, 2026
  2. v0.1.4Oct 25, 2026
  3. v0.1.3Oct 18, 2026
  4. v0.1.2Oct 9, 2026
  5. v0.1.1Sep 24, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts