data-aggregator-mcp
One MCP server that searches 17+ research-data sources behind a single normalized DataResource model, then resolves, verifies, fetches, inspects, and cross-links what it finds.
search— one query fanned out across archives (Zenodo, DataCite/Dryad/Figshare/Dataverse/OSF/OpenNeuro, DataONE, HuggingFace, OpenML, RCSB PDB, UniProt, GBIF, data.gov, NASA CMR), omics (GEO/SRA/BioProject, OmicsDI, BioStudies, DANDI, CELLxGENE, GWAS Catalog) and literature (PubMed, OpenAIRE); DOI-deduped, with per-source failures surfaced inerrors{}. Filters:sources,kind,size,published_after/before,cursor. Ontology expansion viaorganism(NCBI Taxonomy),disease(MeSH),tissue(UBERON),chemical(ChEBI),assay(EDAM), each echoed back; unmatched terms reported inunresolved{}. Opt-ins:rank=semantic,understand=true/multi_query=true(LLM rewriting, needLLM_API_BASE),collapse_mirrors,provenanceRO-Crate.resolve— full record +files[]manifest,links[]paper→data bridges, normalizedidentifiers(pmid/pmcid/doi) and OA full text. Opt-ins:cite=(bibtex/ris/csl-json/any CSL style),trust=true(retraction via Crossref),fair=true(RDA FAIRness score + gaps),use=(ALLOW/REVIEW/DENY licence advisory),format=croissant / ro-crate / provenance dossier.fetch— download to disk (or managed cache) with md5/SHA-256 verification where the source publishes one,max_bytesguard, optional path-traversal-safe archiveextract, and a.dataresource.jsonsidecar; Dryad, NASA CMR, BioProject and the GWAS Catalog are discovery-only and fail loud.operate— readschema,preview,head, run a read-onlysqlSELECT, orpeekper-column profiles against a remote Parquet/CSV/TSV without downloading it (requires the[operate]extra).relate— given 2–10 ids, returns metadata-level join hints: shared accession, shared identifier, explicit link, or version lineage, each with the literal shared value as evidence.list_sources— wired sources with capabilities, auth and rate limits, plus an optional livecheck_healthprobe five sources.Prompts —
find_data,data_behind_paper,search_resolve_fetchfor guided workflows.Deployment — stdio by default or streamable HTTP with DNS-rebinding protection; optional keys for NCBI, data.gov, Unpaywall, embeddings and LLM endpoints.
Integrates with Figshare via DataCite for discovering and downloading research outputs, including md5 checksum verification.
Provides discovery of Mendeley records through DataCite integration, though fetching is not supported.
Supports search and file retrieval from OSF via DataCite, with md5 verification on downloads.
Enables searching PubMed articles and resolving them to open-access full text, with citation and cross-links to related data.
Enables searching for datasets and files on Zenodo, with support for fetching and checksum verification.
🔎 data-aggregator-mcp
One MCP server to find and fetch research data across archives, omics registries, and literature — behind a single normalized model.
search one query across 17 sources — Zenodo, DataCite (Dryad /
Figshare / Dataverse / OSF / OpenNeuro / Mendeley), NCBI omics
(GEO / SRA / BioProject), BioStudies (EBI, incl. ArrayExpress),
literature (PubMed / OpenAIRE), HuggingFace datasets, DataONE
(eco / environmental), OmicsDI (proteomics / metabolomics), DANDI
(neurophysiology), CZ CELLxGENE (single-cell), OpenML (ML datasets),
RCSB PDB (structures), UniProtKB (proteins), the GWAS Catalog,
GBIF (biodiversity), data.gov (US federal open data), and NASA CMR
(Earth science) — deduplicated, normalized, and cross-linked. resolve any hit to its file
manifest, citation, trust signals, and the data it points at. fetch it to
disk, checksum-verified where the source publishes a checksum.
mcp-name: io.github.musharna/data-aggregator-mcp
✨ Why this
Many research-data MCP servers wrap one source each. This one unifies many
behind six tools and one DataResource model, so an agent searches once and gets
back comparable records:
Multi-domain, one model — generalist archives + raw omics + literature, deduplicated by DOI (the fetchable record wins over bare metadata).
Taxonomy synonym expansion —
organism="Orobanche aegyptiaca"also matchesPhelipanche aegyptiaca(NCBI Taxonomy), so a species rename doesn't cost you results.Paper → data bridge — resolve a paper and get links to the GEO / SRA / BioProject / DataCite records it produced.
Checked fetch — streams to disk with md5 / sha-256 verification where the source publishes a checksum (a mismatch raises), and optional archive unpacking. Many sources publish no checksum (see the Checksum column below); those downloads are not verified, and the only content check is an HTML sniff on files declared as PDF or XML, which rejects a paywall page served as a "PDF".
Citations, access & full text — render a citation in any CSL style, get normalized access/license, and pull open-access full text — all in one
resolve.Trust signals — usage
metrics(citations / views / downloads / likes), version status (is_latest/superseded_by), andlast_updatedfreshness, surfaced wherever the source exposes them.Interop exports —
resolve(format="croissant")or"ro-crate"hands a dataset to an ML or research-packaging pipeline as standard JSON-LD.Operate on data in place —
operatereads the schema, previews rows, or runs a read-only SQLSELECTagainst a remote Parquet/CSV/TSV without downloading it (Parquet footer + DuckDB httpfs range reads). Optional[operate]extra; base install is unchanged.Relate across records —
relatetakes a handful of resolved ids and reports how they connect — shared accession, shared cross-identifier, an explicit link, or version lineage — naming the literal shared value as evidence. Metadata hints only: it never reads files or executes a join.
→ Full rationale and a comparison vs. single-source servers, breadth gateways, and ML-dataset tools: docs/POSITIONING.md.
Related MCP server: Academic MCP
⚡ Quickstart
Run with no install:
uvx data-aggregator-mcpRegister with Claude Code:
claude mcp add data-aggregator -- uvx data-aggregator-mcpA typical agent flow:
search("drought stress RNA-seq", organism="Sorghum bicolor")
→ [ geo:GSE..., sra:SRX..., zenodo:..., pubmed:... ] # deduped, taxa-normalized
resolve("sra:SRX079566")
→ DataResource{ files: [ENA FASTQ urls…], access: "open", taxa: [...] }
fetch("sra:SRX079566", dest="./data")
→ ["./data/SRX079566_1.fastq.gz", …] # md5-verifiedpip install data-aggregator-mcp
data-aggregator-mcp # or: python -m data_aggregator_mcpTo use the operate tool (query remote tabular files in place), install the
optional extra:
pip install "data-aggregator-mcp[operate]"Add to a client's MCP config (e.g. Claude Desktop claude_desktop_config.json):
{
"mcpServers": {
"data-aggregator": {
"command": "uvx",
"args": ["data-aggregator-mcp"],
"env": { "NCBI_API_KEY": "your-optional-key" }
}
}
}🌐 Transports
stdio (default) — the server runs as a child of the client, so fetch()
writes to your own disk. Nothing to configure; every command above uses it.
Streamable HTTP — the same six tools, prompts, and resources over HTTP:
data-aggregator-mcp --transport http # → http://127.0.0.1:8000/mcp/flag | default | notes |
|
| |
|
| this machine only; any non-loopback value requires |
|
| |
| auto on loopback | permitted |
| derived | permitted browser |
| off | fresh transport per request, no session affinity |
| off | plain JSON responses instead of SSE streams |
The endpoint is served at /mcp/ — with the trailing slash. /mcp answers
307 redirecting there, which is fine for any client that follows redirects (a
307 preserves the POST body); point one that doesn't straight at /mcp/. In
stateful mode, sessions idle for 30 minutes are reaped.
DNS-rebinding protection is always on. A loopback bind derives its own
host/origin allowlist, so the default needs no configuration. A non-loopback bind
(--host 0.0.0.0, a LAN address, a container interface) refuses to start
without at least one explicit --allow-host — guessing an allowlist there is
precisely the hole the protection exists to close, so it fails loud instead of
open:
data-aggregator-mcp --transport http --host 0.0.0.0 \
--allow-host data.example.org:8000Once running, a request whose Host header is outside the allowlist is refused
with 421 Invalid Host header.
⚠️
fetch(dest=…)writes to the server's filesystem, not the client's. Over stdio those are the same disk; over HTTP they may be different machines, and the caller gets back paths it cannot read. Treatdeston an HTTP deployment as server-side staging, or use stdio when you need the bytes locally.search,resolve,operate,relate, andlist_sourcesare unaffected — they return data, not paths.
🗂️ Sources
Source | Discover | Fetch | Checksum |
Zenodo | ✅ | ✅ | md5 |
DataCite → Figshare | ✅ | ✅ | md5 |
DataCite → Dataverse | ✅ | ✅ | md5 |
DataCite → OSF | ✅ | ✅ | md5 |
DataCite → Dryad | ✅ | manifest only¹ | sha-256 (listed) |
DataCite → Mendeley & others | ✅ | — | — |
NCBI SRA | ✅ | ✅ (ENA FASTQ) | md5 |
NCBI GEO | ✅ | ✅ ( | none² |
NCBI BioProject | ✅ | → SRA links | — |
PubMed / OpenAIRE | ✅ | ✅ (OA full text) | none³ |
HuggingFace datasets | ✅ | ✅ (resolve URL) | none² |
DataONE (eco/env) | ✅ | ✅ (Member Node) | md5 / sha-256 |
OmicsDI → PRIDE | ✅ | ✅ (HTTPS FTP) | none² |
OmicsDI → MetaboLights | ✅ | ✅ (HTTPS FTP) | sha-256 |
OmicsDI → other MS repos | ✅ | — | — |
DataCite → OpenNeuro | ✅ | ✅ (snapshot) | none² |
DANDI (neurophysiology) | ✅ | ✅ (302→S3) | sha-256 |
CZ CELLxGENE (single-cell) | ✅ | ✅ (H5AD/RDS) | none² |
OpenML (ML datasets) | ✅ | ✅ (ARFF) | md5 |
RCSB PDB (structures) | ✅ | ✅ (.cif/.pdb) | none² |
UniProtKB (proteins) | ✅ | ✅ (FASTA) | none² |
BioStudies (EBI) | ✅ | ✅ (study files) | none² |
GBIF (biodiversity) | ✅ | ✅ (Darwin Core)⁴ | none² |
data.gov (DCAT-US) | ✅ | ✅ (file URL)⁴ | none³ |
NASA CMR (Earth science) | ✅ | —⁵ | — |
GWAS Catalog | ✅ | → PMID bridge | — |
¹ Dryad downloads are token / bot-challenge gated, so fetch fails loud;
resolve still lists the files.
² No upstream checksum, so fetch does not verify these bytes. It still fails
loud on an HTTP error or when the download exceeds max_bytes.
³ No upstream checksum. Files declared as PDF or XML (literature full text, and
data.gov distributions with that mediaType) get an HTML sniff: an HTML login or
paywall page served in their place fails loud. Other files are not checked.
⁴ Only records that carry a downloadable file (a GBIF Darwin Core Archive, a
data.gov distribution URL); metadata-only records are discovery-only.
⁵ Discovery-only: granule downloads need an Earthdata login, which is not wired.
resolve returns the DOI and a data-access portal link.
🛠️ Tools
search(query?, size?, sources?, organism?, disease?, tissue?, chemical?, assay?, kind?, published_after?, published_before?, rank?, cursor?, collapse_mirrors?, understand?, multi_query?, provenance?)
Fan out across all wired sources in parallel and return compact DataResource
records, deduped by DOI. Per-source failures land in errors{} — never silently
dropped.
organism— expand the query with NCBI-Taxonomy synonyms; the expansion is echoed intaxon_expansion, and results carry normalizedtaxa[]({taxid, name}) plus adescribed_inlink to plant-genomics-mcp for plant taxa.sources— restrict the fan-out, e.g.["omics"].size— max results (1–50).kind— keep onlydataset/sequencing_run/study/publication/software. A record whose upstream type none of these covers (a Zenodo image, a DataCiteAudiovisual, an untyped record) is kindotherand matches no filter.published_after/published_before— filter by publication year.rank—relevance(default) orsemantic(re-rank the fetched page by embedding similarity to the query; needsEMBEDDING_API_BASE, degrades to relevance order otherwise).understand— opt into LLM query understanding (default false). A free-text query is normalized into a focused keyword query: conversational fluff ("I'm looking for…","where can I find…") is stripped while the scientific and entity terms are kept so they still match by text. The LLM also detects structured entities (organism/disease/tissue/chemical/assay, kind) — these are echoed inquery_understanding.extractedfor transparency but not auto-applied, because ANDing LLM-inferred facets across free-text keyword upstreams over-constrains and hurts recall. Only the cleanedkeyword_coreand explicityearscopes are applied; the ontology resolvers still run on the facets you pass (the LLM proposes, you dispose). Needs an LLM endpoint (LLM_API_BASE); with none configured the search runs unchanged and notes it inerrors['understand']. Effectiveness is query- and model-dependent — opt-in / default-off; validate the recall lift on your own corpus and LLM (see the eval harness below).understand=was measured once (v0.38.0, 2026-06-11) on a 5-query verified gold set: mean recall@20 lift −0.10 against the plain query, with 4 of the 5 queries neutral or better.multi_query=has not been measured.multi_query— opt into diverse multi-query recall expansion (default false). An LLM generates up to a few deliberately-diverse reformulations of your query (different facets/synonyms/framings, not paraphrases), each is fanned out across every source, and the deduped union is re-ranked against your original query, aiming to reach records a single keyword query would miss. Bounded atMAX_QUERY_VARIANTS(4, incl. the original), so it costs at most N× the upstream calls. The original query's results are always among the candidates, but only the topsizeof the re-ranked union are returned, so a result the plain query would have returned can be displaced by one from a variant. Composes withunderstand=(which structures variant 0). The variants used are echoed inquery_expansion. Needs an LLM endpoint (LLM_API_BASE); with none configured the search runs as a normal single query and notes it inerrors['multi_query'].cursor— opaque token from a prior result'snext_cursor; pages forward across every source. Incursormode the other params are read from the token, soqueryis optional.
resolve(id, cite?, format?, trust?, fair?, use?)
Full record + files manifest. Routes by id shape — zenodo:7654321, a bare DOI,
datacite:10.5061/dryad.x, an omics id (sra:SRX079566, geo:GSE332789,
bioproject:PRJNA1468572), a literature id (pubmed:34320281, openaire:<id>),
a HuggingFace id (hf:owner/name), a DataONE id (dataone:doi:10.5063/F1HT2M7Q),
or an OmicsDI id (omicsdi:pride:PXD000001). Attaches, where available:
files[]— ENA FASTQ manifest (SRA), GEOsuppl/, or the host repo's native manifest (Figshare / Dataverse / OSF / Dryad).links[]— paper → data:pubmed:→sra:/geo:/bioproject:(NCBI elink);openaire:→datacite:(ScholeXplorer Scholix).access/license— normalized status (open/embargoed/restricted/closed/unknown) and license where the source exposes it.identifiers— normalized{pmid, pmcid, doi}, plus an open-access full-textFileEntry(EuropePMC XML, or an Unpaywall PDF fallback) for papers.citation— passcite=<format>:bibtex,ris,csl-json, or any CSL style name (apa,mla,vancouver, …). DOI records use content negotiation; others render CSL-JSON from metadata. Off by default; failures degrade quietly.trust signals —
metrics(citations / views / downloads / likes),is_latest/superseded_by(derived from version links), andlast_updatedfreshness, where the source provides them.errors—{step: message}when an enrichment step failed on this record (e.g.taxonomyduring an NCBI rate limit); the rest of the record stands. Such a record is not cached, so the next resolve retries the step.truncated—{field: note}when a list on this record is deliberately partial, e.g. a BioProject'slinkspast 100 SRA runs:first 100 of 891 SRA runs; …. Empty when every list is complete.trust=true— attach retraction status (via Crossref) undertrust{}. One extra Crossref call; meaningful for DOI-bearing records only.fair=true— attach an RDA-grounded FAIRness score (0–100 + F/A/I/R sub-scores + actionable gaps) computed from the record metadata underfair{}. Pure/local — no extra network call.use=<intent>— attach a licence-compatibility advisory underlicense_compat{}for the intended use (commercial/redistribute/modify/ml-training). Returns ALLOW/REVIEW/DENY with the governing clause. Metadata-derived advisory, not legal advice; an absent/unrecognized licence yields REVIEW.format— passformat="croissant"(file-level Croissant JSON-LD),"ro-crate"(minimal RO-Crate 1.1), or"provenance"(one-call RO-Crate 1.1 data-availability dossier bundling version-currency, licence+SPDX, FAIR score, and retraction status) to attach a standard manifest under the matching field.
fetch(id, dest?, files?, max_bytes?, force?, extract?)
Download files to disk and return their paths. Streams under a max_bytes guard
(force to override) with md5 / sha-256 verification wherever the source
publishes a checksum.
files— restrict to a subset of the resolved manifest.extract— unpack downloaded zip / tar archives in place, guarded against path traversal and runaway extracted size. Off by default.Sources without a checksum are downloaded unverified. The one content check there is an HTML sniff on files declared as PDF or XML (literature full text, some data.gov distributions): it fails loud if the body is actually an HTML page.
Checksum-verified: Zenodo, SRA (ENA FASTQ), DataONE (Member-Node objects), DataCite-hosted Figshare / Dataverse / OSF, OpenML (ARFF), MetaboLights (via OmicsDI; sha-256 from the study's
HASHES/) and DANDI (sha-256; an asset whose hash DANDI has not computed yet is unverified).Fetchable but unverified: GEO
suppl/, HuggingFace datasets, PRIDE (via OmicsDI), DataCite-hosted OpenNeuro, CZ CELLxGENE, RCSB PDB, UniProtKB, BioStudies, GBIF (Darwin Core Archives), data.gov distributions, and literature open-access full text.Dryad, other DataCite repos, other OmicsDI repos (MassIVE / GNPS / ...), BioProject, NASA CMR, and the GWAS Catalog are discovery-only and raise
FetchNotSupportedError.
list_sources()
Wired sources with their capabilities — layer, kinds, supported filters,
fetchability, operable flag, id examples, auth, and rate limits.
operate(op, id, file?, query?, n?, columns?)
Inspect or query a remote tabular file (Parquet / CSV / TSV) without
downloading it. Addresses a file by catalog id + file name (defaults to the
first tabular file on the resolved record). Ops:
schema— column names + types (reads the Parquet footer / sniffs the CSV header; no full load).preview— a small sample of rows.head— the firstnrows (default 20), optionally restricted tocolumns.sql— a read-onlySELECT(the file is the viewdata), e.g.SELECT col, count(*) FROM data GROUP BY 1.peek— per-column profile via DuckDBSUMMARIZE(type, null-rate, approximate distinct count, min/max, numeric quartiles) without downloading the file. Likehead/sql, reads the whole file and honors the source-size ceiling.
Backed by the Parquet footer reader + DuckDB httpfs range reads. sql runs in
a locked-down DuckDB (read-only, local filesystem disabled, single-SELECT
validation, row / wall-clock caps). Requires the optional [operate] extra
(pip install data-aggregator-mcp[operate]); without it, operate returns a
clear install-the-extra message and the other five tools are unaffected.
Any HuggingFace dataset with a datasets-server converted view is operable
(schema / preview / head / sql): resolve surfaces the auto-converted
Parquet files (source="hf-datasets-server") even for datasets stored as
JSON/JSONL/arrow, so pass file=<config>/<split>/...parquet to pick a split when
there are several. A split HF converted only in part (its first 5 GB) is named
<config>/partial-<split>/..., so a query on it covers that part, not the whole split.
relate(ids)
Cross-resource join/harmonization hints. Given 2–10 resource ids, relate resolves
each (TTL-cached) and reports how they relate and on what key they could be joined:
shared_accession— same BioProject/SRA/GEO accession on ≥2 records → joinable key.shared_identifier— same doi/pmid/pmcid across records → same work / paper↔data link.explicit_link— one record'slinks[]points at another input record.version_lineage— one record supersedes another (dedupe, don't join, those).
Hints only. relate never reads file columns, fetches files, or executes a
join/merge/conversion — every hint names the shared value as evidence. Per-id resolve
failures are reported in errors, not fatal; an empty result carries an explanatory
note.
Prompts
Three workflow prompts surface in clients (e.g. /mcp__data_aggregator__* in
Claude Code):
find_data— find datasets for a topic, optionally scoped to an organism.data_behind_paper— find the datasets / accessions behind a paper.search_resolve_fetch— walk the end-to-end search → resolve → fetch flow.
⚙️ Configuration
All optional, set via environment variables:
NCBI_API_KEY— raises the NCBI E-utilities rate limit (3 → 10 req/s) used by the omics, literature, and taxonomy lookups.DATA_GOV_API_KEY— optional; data.gov works without it through the keyless catalog API (catalog.data.gov). With a free api.data.gov key set, data.gov requests go through the api.data.gov gateway instead (1,000 requests/hour per key).UNPAYWALL_EMAIL— enables the Unpaywall fallback leg of literature full-text retrieval (the EuropePMC leg works without it).NCBI_EMAIL— contact address sent to NCBI's ID converter; falls back toUNPAYWALL_EMAILwhen unset.DATAVERSE_BASE_URL— resolve Dataverse DOIs against a different installation (defaulthttps://dataverse.harvard.edu).CACHE_TTL_SECONDS— resolve-cache lifetime in seconds (default3600; an unparseable value falls back to that default).EMBEDDING_API_BASE/EMBEDDING_API_KEY/EMBEDDING_MODEL— an OpenAI-compatible embeddings endpoint enablingrank=semantic. Absent ⇒ semantic re-rank degrades to relevance order. Key is optional (keyless local servers supported); model defaults totext-embedding-3-small.LLM_API_BASE/LLM_API_KEY/LLM_MODEL— an OpenAI-compatible/chat/completionsendpoint enablingsearch(understand=true)(NL→structured query rewriting) andsearch(multi_query=true)(diverse multi-query recall expansion). Absent ⇒ both run the raw query unchanged and note it inerrors['understand']/errors['multi_query']. Key is optional (keyless local servers supported); model defaults togpt-4o-mini(a passthrough string — set it to whatever your endpoint serves).multi_queryfans out at mostMAX_QUERY_VARIANTS(4, incl. the original) variants, bounding the N× cost.
To measure the recall lift of understand=true / multi_query=true on a small
labeled set, run the gated eval harnesses (need a live LLM endpoint):
DATA_AGGREGATOR_MCP_LIVE=1 LLM_API_BASE=... python scripts/eval_understand.py
DATA_AGGREGATOR_MCP_LIVE=1 LLM_API_BASE=... python scripts/eval_multi_query.pyThey print per-query and mean recall@20 (understand / multi-query off vs. on). See
the fixtures at scripts/eval_understand_fixture.json and
scripts/eval_multi_query_fixture.json.
🧪 Develop
uv venv && uv pip install -e ".[dev]"
uv run pytest -q
uv run ruff check src tests
DATA_AGGREGATOR_MCP_LIVE=1 uv run pytest -k live -q # real-API probesThe README demo (examples/assets/demo.svg) is recorded network-free from
examples/_demo_stdio.py — see the header of that file to re-record.
License
MIT — see LICENSE.
Available Tools
6 toolsfetchA
Download a resource's files to local disk and return the PATHS (never the file contents). Fetchable backends: Zenodo (md5-verified); SRA via ENA FASTQ (md5-verified); GEO supplementary files (unverified); DataCite sub-repos — Figshare/Dataverse/OSF (md5-verified), OpenNeuro (snapshot manifest, unverified), Dryad is manifest-only (resolve lists files, fetch fails loud), Mendeley + other DataCite repos fail loud; PubMed/OpenAIRE open-access full text (EuropePMC XML / Unpaywall PDF, unverified); HuggingFace Hub (unverified); DataONE Member-Node objects (md5/SHA-256-verified); OmicsDI — PRIDE (unverified) + MetaboLights (sha-256-verified) only, MassIVE/GNPS/PeptideAtlas/Metabolomics Workbench fail loud; DANDI dandisets (302→S3, sha-256-verified); CZ CELLxGENE H5AD/RDS assets (unverified); OpenML ARFF (md5-verified); RCSB PDB .cif/.pdb structure files (unverified); UniProtKB FASTA (unverified); BioStudies study files (unverified); GBIF Darwin Core Archives (unverified); data.gov dataset distributions (unverified). Fails loud if selected files exceed max_bytes unless force=true. Verifies md5/SHA-256 where the source publishes one; files marked unverified get no integrity check. Writes a .dataresource.json sidecar.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Source-prefixed id or bare Zenodo id | |
| dest | No | Destination dir (default managed cache) | |
| files | No | Glob over file names (default all) | |
| force | No | Override max_bytes | |
| extract | No | Unpack downloaded zip/tar archives into the destination (default false). Path-traversal-guarded; counts against max_bytes. | |
| max_bytes | No | Byte ceiling before failing loud |
Output Schema
| Name | Required | Description |
|---|---|---|
| bytes | No | |
| paths | No | |
| resumed | No | |
| skipped | No | |
| unverified | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the provided annotations: it discloses per-backend integrity verification (md5/SHA-256 vs unverified), failure conditions ('fails loud if selected files exceed max_bytes unless force=true'), the writing of a .dataresource.json sidecar, and the fact that it returns paths rather than contents.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the tool's core action and return behavior, but the long run-on sentence enumerating every backend is dense and could be more structured (e.g., a table or bullet list). It is information-rich but not concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complex multi-backend nature of the tool, existing output schema, and safety annotations, the description is complete enough: it covers supported sources, verification behavior, failure modes, side effects (sidecar), and return semantics without needing to re-explain outputs or safety hints.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all six parameters. The description restates the interaction between max_bytes and force but adds no new semantic detail about the other parameters (id, dest, files, extract), so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Download a resource's files to local disk and return the PATHS (never the file contents).' It also implicitly distinguishes itself from the sibling 'resolve' by noting that Dryad is manifest-only and that resolve lists files while fetch fails loud.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for when the tool is applicable by listing all fetchable backends and their verification/failure behavior. It also provides a specific alternative for Dryad ('resolve lists files, fetch fails loud'), but it lacks general routing guidance like 'use search to find an id first' or 'use this instead of resolve when you need the actual files.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_sourcesARead-only
List wired data sources and their capabilities (layer, kinds, supported filters, auth requirement, rate limit, status).
| Name | Required | Description | Default |
|---|---|---|---|
| check_health | No | When true, probe 5 sources (zenodo, datacite, omics, literature, huggingface) and attach a 'health' field ({status: up|down, latency_ms, detail}) to those entries; every other source gets health: null. Default false: returns the static catalog with no network. |
Output Schema
| Name | Required | Description |
|---|---|---|
| sources | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
readOnlyHint=true already tells the agent this is a safe read. The description adds the shape of the catalog entries, which is mild value, but it does not disclose the default no-network behavior or the targeted health-probe side effects — that context lives only in the parameter schema. With annotations and an output schema present, the bar is lower and a 3 fits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence that front-loads the action and resource, then lists the returned fields parenthetically. No filler, no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Annotations cover the safety profile, the output schema covers the return shape, and the sole parameter is fully described, so the description need not repeat them. It is essentially complete for a read-only lister, missing only the positioning relative to sibling tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
schema_description_coverage is 100% and the single check_health parameter is fully documented in-schema, including which five sources get probed and what the health field contains. The description contributes nothing about the parameter, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("List") and resource ("wired data sources") and enumerates exactly what each entry contains (layer, kinds, supported filters, auth requirement, rate limit, status). No sibling (search, resolve, fetch, operate, relate) performs catalog enumeration, so the tool is clearly distinguishable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent can infer this is the discovery step that should precede search/fetch, but the description never says so, nor does it state when not to call it (e.g., prefer search when you already know the source). No alternative routing is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
operateARead-only
Inspect or query a remote tabular file (Parquet/CSV/TSV) WITHOUT downloading it. op='schema' returns columns+types; 'preview' a small sample; 'head' the first n rows; 'sql' a read-only SELECT against the file (exposed as the view 'data', e.g. "SELECT * FROM data WHERE x > 1"). op='peek' profiles every column WITHOUT downloading — type, null-rate, approximate distinct count, min/max, and numeric quartiles (a DuckDB SUMMARIZE; like head/sql it reads the whole file, so it honors the source-size ceiling). Addresses a file by catalog id + file name (resolve the id first to see files[] and access_modes). Requires the [operate] extra; fails loud if the file is not an operable tabular file.
| Name | Required | Description | Default |
|---|---|---|---|
| n | No | Row count for head/preview | |
| id | Yes | DataResource id (e.g. 'zenodo:7654321') | |
| op | Yes | ||
| file | No | File name within the record; optional when exactly one operable file is present. | |
| query | No | Read-only SELECT for op='sql'. | |
| columns | No | Optional column projection for head. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=true, and the description adds substantial behavior beyond that: SQL is restricted to read-only SELECT against the view 'data', peek/head/sql read the whole file and therefore honor a source-size ceiling, the tool requires the [operate] extra, and it fails loud on non-operable files. This is exactly the extra context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but every clause carries information; the no-download constraint and op list are front-loaded before the addressing/permission details. Slightly long as a single paragraph, but nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description does the work of describing what each op returns, and covers the addressing model, extra requirement, and failure mode. A couple of minor gaps remain (e.g. default n behavior and pagination/limits on sql results) but the tool is callable correctly as written.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 83%, but the description adds real meaning the schema lacks: it defines each op value's semantics and output (columns+types, small sample, first n rows, read-only SELECT, full profiling with null-rate/distinct/min-max/quartiles). It also explains the SQL view alias 'data' and its example, which the schema does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource ('Inspect or query a remote tabular file') plus the key differentiator ('WITHOUT downloading it') and enumerates the five operations with their distinct return shapes. An agent can distinguish it from fetch (which downloads) and from search/resolve without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational routing: resolve the id first to see files[] and access_modes, notes the [operate] extra requirement, and notes file is optional when exactly one operable file exists. It stops short of explicitly naming the sibling alternative (e.g. fetch) that would be used when download is desired, so it is clear context but not full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
relateARead-only
Given 2-10 resource ids, return metadata-level join/harmonization HINTS: how the datasets relate and on what key they could be joined. Detects shared accessions (BioProject/SRA/GEO), shared cross-identifiers (doi/pmid/pmcid), explicit links between the inputs, and version lineage. HINTS ONLY — it does not read file columns, fetch files, or execute any join/merge/conversion; each hint names the shared value as evidence. Resolve ids first if you only have a search result. Per-id resolve failures are reported, not fatal.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | 2-10 source-prefixed resource ids to relate. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| hints | No | |
| errors | No | |
| resolved | Yes | |
| input_ids | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond readOnlyHint=true by disclosing that output is hints only, that no file columns are read or files fetched, that each hint cites the shared value as evidence, and that per-id resolve failures are reported rather than fatal. These are exactly the behavioral traits an agent needs to set expectations for a non-authoritative, non-mutating analysis call.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and return type, then constraints and failure behavior in descending priority. Dense but almost every clause carries distinct information; the enumeration of detection categories is slightly listy but justified by the tool's discovery purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-format explanation is unnecessary, and the description still covers scope boundaries, prerequisites, evidence semantics, and partial-failure behavior. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the description restates the same facts (2-10 ids, source-prefixed) without adding syntax or format guidance beyond the schema. With a single fully documented parameter, this is the expected baseline rather than added value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb (relate) and resource (2-10 resource ids) and states precisely what is returned: metadata-level join/harmonization hints, with enumerated detection categories (shared accessions, cross-identifiers, explicit links, version lineage). It is clearly distinguishable from resolve/search/fetch because it explicitly says it does not read columns, fetch files, or execute joins.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite ('Resolve ids first if you only have a search result') and a clear when-not-to-use boundary (it does not execute joins/merges, so it is not a substitute for an operate-style action). It does not, however, name the sibling tool an agent should reach for when it actually wants to execute a join, leaving that routing implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolveARead-only
Fetch the full DataResource for a known id (e.g. 'zenodo:7654321', 'datacite:10.5061/dryad.x', 'hf:owner/name', a bare Zenodo record id, or a DOI), including the complete files[] manifest. Publication resolve also attaches normalized identifiers (pmid/pmcid/doi) and, when open access, a full-text file. Pass cite= to render a citation onto the result (citation field); omitted means no citation. Pass trust=true to attach retraction status (via Crossref) under trust{}. Pass fair=true to attach an RDA-grounded FAIRness score (0–100 + F/A/I/R sub-scores + actionable gaps) computed from the record under fair{}. Pass use= (commercial/redistribute/modify/ml-training) to attach a licence-compatibility advisory (ALLOW/REVIEW/DENY, not legal advice) under license_compat{}. Pass format=provenance for a one-call RO-Crate 1.1 data-availability dossier (under provenance{}) composing version-currency, licence+SPDX, FAIR score, retraction status, and the source/DOI/ID chain — it auto-attaches fair + trust.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Source-prefixed id, bare Zenodo id, or DOI | |
| use | No | When set, attach a licence-compatibility advisory under license_compat{} for an intended use of the record. Supported intents: 'commercial', 'redistribute', 'modify', 'ml-training' (training = a derivative+commercial use, our stated interpretation). The verdict is ALLOW/REVIEW/DENY computed from a bundled choosealicense.com licence matrix keyed on the normalized SPDX id, naming the governing clause — a metadata-derived advisory, NOT legal advice. An unrecognized or absent licence yields REVIEW (never a fabricated ALLOW/DENY); an unknown intent is an error. | |
| cite | No | Optional citation format to render onto the result: 'bibtex', 'ris', 'csl-json', or any CSL style name ('apa', 'mla', 'vancouver', ...). DOI-bearing records render via DOI content negotiation; non-DOI records support 'csl-json' only. Omitted = no citation. Failures degrade quietly (citation stays null). | |
| fair | No | When true, attach an RDA-grounded FAIRness assessment under fair{}: a 0–100 overall score plus findable/accessible/interoperable/reusable sub-scores, the count of indicators evaluated, and actionable gaps each naming its RDA FAIR Data Maturity Model indicator id. Pure/local — no network call. Only the machine-evaluable subset is scored (never fabricates what the metadata cannot show). | |
| trust | No | When true, attach trust signals (retraction status via Crossref) to the result under trust{}. One extra Crossref call; only meaningful for DOI-bearing records (a DataCite data DOI Crossref does not register leaves retracted=null = unknown, never a false clean claim). | |
| format | No | Optional export to render onto the result. 'croissant' attaches a file-level Croissant JSON-LD manifest (croissant field); 'ro-crate' attaches a minimal RO-Crate 1.1 manifest (ro_crate field); 'provenance' attaches a one-call RO-Crate 1.1 data-availability dossier (provenance field) bundling version-currency, licence+SPDX, FAIR score, retraction status, and the source/DOI/ID chain — it auto-attaches fair{} and trust{} so the dossier is complete in one call (unknown signals are reported as unknown, never as a clean claim). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| doi | No | |
| fair | No | |
| kind | Yes | |
| taxa | No | |
| year | No | |
| files | No | |
| links | No | |
| title | Yes | |
| trust | No | |
| access | No | |
| errors | No | |
| source | Yes | |
| funding | No | |
| license | No | |
| metrics | No | |
| mirrors | No | |
| citation | No | |
| creators | No | |
| organism | No | |
| ro_crate | No | |
| subjects | No | |
| croissant | No | |
| is_latest | No | |
| truncated | No | |
| accessions | No | |
| provenance | No | |
| description | No | |
| identifiers | No | |
| access_modes | No | |
| last_updated | No | |
| superseded_by | No | |
| license_compat | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond readOnlyHint=true: it discloses cost/network behavior ('pure/local — no network call' for fair, 'one extra Crossref call' for trust), graceful degradation (citation failures stay null, retraction is null=unknown rather than a false clean claim), and legal/scope caveats (advisory 'NOT legal advice', unrecognized licence yields REVIEW). Unknowns are explicitly reported as unknown, never fabricated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose (fetch + files[] manifest) before enumerating optional flags, one sentence per flag, no filler. It is on the long side and duplicates schema parameter docs, which costs a point but not clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter tool with an output schema and only readOnlyHint annotations, the description carries the behavioral burden well: auto-attach relationships, error/degradation behavior, and advisory framing are all covered. The remaining gap is sibling routing (when to prefer search or fetch instead), which no sentence addresses.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description mostly restates what the schema already documents (citation formats, fair/trust/use semantics, format enum, provenance auto-attaching fair+trust); its only additive value is the concrete id-format examples that the schema's brief 'Source-prefixed id' note lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — fetch the full DataResource for a known id — and names the distinguishing payload ('the complete files[] manifest'). The id-format examples show exactly what a queryable 'known id' looks like, so an agent can tell this apart from search/fetch without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The precondition is clear: this is for a KNOWN id, and format=provenance is framed as a 'one-call' alternative to assembling the pieces separately. However, it never names the sibling tools (search, fetch, relate) or states the when-not condition (e.g. 'use search when you do not have an id'), leaving routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchARead-only
Search public research-data archives, omics registries, and the literature for datasets, software, publications, and sequencing data. Fans out across Zenodo, DataCite (Dryad, Figshare, Dataverse, OSF, Mendeley, OpenNeuro), NCBI omics (GEO, SRA, BioProject), literature (PubMed + OpenAIRE), HuggingFace Hub (datasets), DataONE (eco/environmental federation), OmicsDI (proteomics/metabolomics), RCSB PDB (macromolecular structures), GWAS Catalog (genotype-phenotype studies), OpenML (ML datasets), DANDI (neurophysiology dandisets), CZ CELLxGENE (single-cell datasets), GBIF (biodiversity datasets), data.gov (US government open data), NASA CMR (Earth-science collections), UniProt (protein entries), and BioStudies (EMBL-EBI study records). Returns compact DataResource records; per-source failures are reported in errors{}. Use resolve for the full record (SRA resolve attaches the ENA FASTQ manifest; publication resolve attaches links[] to datasets/accessions, normalized identifiers (pmid/pmcid/doi), and — when open access — a full-text file), then fetch to download files. Pass organism= to expand the query with NCBI-Taxonomy synonyms; results carry normalized taxa[] + plant cross-links. Pass disease= to expand the query with MeSH descriptor synonyms (e.g. 'breast cancer' also matches 'Breast Neoplasms'); the expansion is echoed in mesh_expansion. Pass tissue= to expand the query with UBERON synonyms (e.g. 'liver' also matches 'iecur'/'jecur'); the expansion is echoed in tissue_expansion. Pass chemical= to expand the query with ChEBI compound synonyms (e.g. 'caffeine' also matches '1,3,7-trimethylxanthine'); the expansion is echoed in chemical_expansion. Pass assay= to expand the query with EDAM assay/method synonyms (e.g. 'ChIP-seq' also matches 'ChIP-sequencing'); echoed in assay_expansion. Pass collapse_mirrors=true to opt into conservative cross-repo mirror collapse: same-dataset copies under different/no DOIs are folded into one record, with the folded copies annotated under mirrors[]. An ontology param that matches no term in its registry (e.g. organism='yeast' — NCBI Taxonomy indexes no such common name) is reported in unresolved[] and the search runs WITHOUT that expansion, so a dropped filter is never silent. Clients that support form elicitation are asked for a replacement term before the search runs.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Keep only results of this kind. | |
| rank | No | Result ordering. 'relevance' (default) = upstream/merged order. 'semantic' re-ranks the fetched page by embedding similarity to the query (needs EMBEDDING_API_BASE; degrades to relevance order with an errors['semantic'] note if unconfigured). In semantic mode pagination is window-based (each page consumes its full fetched window). | relevance |
| size | No | Max results (1-50, default 10) | |
| assay | No | Optional assay/method name. Resolved via EDAM topics (EBI OLS); the query is expanded with the canonical name + exact synonyms (e.g. 'ChIP-seq' also matches 'ChIP-sequencing'/'ChIP-exo'). An unknown term yields no expansion; an OLS failure surfaces in errors. The expansion is echoed in assay_expansion. | |
| query | No | Free-text search query | |
| cursor | No | Opaque pagination token from a prior search's next_cursor. When set, all other search params are read from the cursor. | |
| tissue | No | Optional tissue/anatomy name. Resolved via UBERON (EBI OLS); the query is expanded with the canonical term + exact synonyms (e.g. 'liver' also matches 'iecur'/'jecur'). The expansion is echoed in tissue_expansion. | |
| disease | No | Optional disease/phenotype name. Resolved via MeSH (NCBI E-utilities); the query is expanded with the canonical descriptor + entry-term synonyms (e.g. 'breast cancer' also matches 'Breast Neoplasms'). The expansion is echoed in mesh_expansion. | |
| sources | No | Restrict fan-out to these sources (default: all). Available: zenodo, dataone, gbif, cellxgene, datacite, dandi, omics, literature, huggingface, datagov, nasacmr, omicsdi, openml, pdb, uniprot, gwas, biostudies | |
| chemical | No | Optional chemical/compound name. Resolved via ChEBI (EBI OLS); the query is expanded with the canonical name + exact synonyms (e.g. 'caffeine' also matches '1,3,7-trimethylxanthine'), capped to a bounded number of synonyms. An unknown term yields no expansion; an OLS failure surfaces in errors. The expansion is echoed in chemical_expansion. | |
| organism | No | Optional organism name. Resolved via NCBI Taxonomy; the query is expanded with the canonical name + synonyms (e.g. 'Orobanche aegyptiaca' also matches 'Phelipanche aegyptiaca'). The expansion is echoed in taxon_expansion. | |
| provenance | No | Opt into a whole-search RO-Crate 1.1 Run Crate (default false). Attaches provenance_crate{} — a machine-readable manifest documenting this search: the query, the sources queried, the ontology expansions that fired, the per-source errors (a partial search is disclosed), and per-hit provenance for every result (version-currency, licence + normalized SPDX, FAIR score). Per-hit RETRACTION is omitted — it would need one Crossref call per hit; use per-record resolve(format=provenance) for that. Covers THIS search page only (intra-page; each page of a paginated search gets its own crate). | |
| understand | No | Opt into LLM query understanding: a free-text query is rewritten into a keyword core + structured params (organism/disease/tissue/chemical/assay, kind, year) before fan-out; extracted entities are validated by the same ontology resolvers (a hallucinated entity that doesn't resolve is simply dropped), explicit params you pass always win, and the interpretation is echoed in query_understanding. Requires an LLM endpoint (LLM_API_BASE); with none configured the search runs unchanged and notes it in errors['understand']. | |
| multi_query | No | Opt into diverse multi-query recall expansion: an LLM generates up to a few deliberately-diverse reformulations of your query, each is fanned out across all sources, and the deduped union is re-ranked against your original query — surfacing relevant records a single keyword query would miss. Costs N× the upstream calls (bounded). Requires an LLM endpoint (LLM_API_BASE); with none configured the search runs as a normal single query and notes it in errors['multi_query']. The variants used are echoed in query_expansion. Composes with understand=. NOTE: multi_query=true ALWAYS applies semantic re-ranking of the window internally regardless of rank=; the rank= param has no effect in this mode. | |
| published_after | No | Keep results with year >= this. | |
| collapse_mirrors | No | Opt into conservative cross-repo content dedup (default false). On top of the always-on exact-DOI dedup, folds records that are the SAME dataset deposited under different (or no) DOIs — e.g. a Zenodo mirror of a figshare deposit, GEO<->ArrayExpress — into one record, annotating the survivor with the folded copies under mirrors[]. Conservative: a merge needs a shared file checksum OR identical (normalized-title, first-author name, year); title-only or partial matches never merge. Intra-page / best-effort only (a mirror on a different page is not collapsed), so a page may return fewer than size items; pagination is unaffected. | |
| published_before | No | Keep results with year <= this. |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| query | Yes | |
| total | Yes | |
| errors | No | |
| results | No | |
| unresolved | No | |
| next_cursor | No | |
| mesh_expansion | No | |
| assay_expansion | No | |
| query_expansion | No | |
| taxon_expansion | No | |
| provenance_crate | No | |
| tissue_expansion | No | |
| chemical_expansion | No | |
| query_understanding | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only readOnlyHint=true in annotations, the description carries the behavioral load and does it well: per-source failures surface in errors{}, ontology terms that fail to resolve land in unresolved[] with the search still running (a dropped filter is never silent), pagination/window behavior under semantic rank, and multi_query overriding rank= entirely. These are non-obvious traits an agent needs before invoking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the resolve/fetch routing, which is good, but it is a single enormous paragraph that exhaustively re-lists every source (already in the sources enum) and restates several schema descriptions. The high-value behavior notes are buried mid-paragraph rather than separated out.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 17-parameter federated search with an output schema, the description covers scope, follow-up tools, optional-parameter semantics, failure disclosure, and mode interactions. Return-value detail is correctly left to the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents each parameter in detail; the description largely echoes that. It does add cross-parameter synthesis (expansions echoed per registry, ranking implications of multi_query, cursor overriding other params, elicitation of a replacement term for unresolved ontologies) that the flat schema does not stitch together.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search public research-data archives, omics registries, and the literature for datasets, software, publications, and sequencing data') and enumerates the federated backends. It also distinguishes itself from siblings by naming resolve and fetch as the follow-up steps for full records and file downloads.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear routing guidance to siblings ('Use resolve for the full record ... then fetch to download files') and explains when each optional ontology parameter and collapse_mirrors applies. It stops short of an explicit when-not-to-use-this condition or a comparison against list_sources/relate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.54.14- Changed
search1 field changed- changed
Input schema / properties / collapse_mirrors / descriptionPrevious value: -"Opt into conservative cross-repo content dedup (default false). On top of the always-on exact-DOI dedup, folds records that are the SAME dataset deposited under different (or no) DOIs — e.g. a Zenodo mirror of a figshare deposit, GEO<->ArrayExpress — into one record, annotating the survivor with the folded copies under mirrors[]. Conservative: a merge needs a shared file checksum OR identical (normalized-title, first-author-surname, year); title-only or partial matches never merge. Intra-page / best-effort only (a mirror on a different page is not collapsed), so a page may return fewer than size items; pagination is unaffected."New value: +"Opt into conservative cross-repo content dedup (default false). On top of the always-on exact-DOI dedup, folds records that are the SAME dataset deposited under different (or no) DOIs — e.g. a Zenodo mirror of a figshare deposit, GEO<->ArrayExpress — into one record, annotating the survivor with the folded copies under mirrors[]. Conservative: a merge needs a shared file checksum OR identical (normalized-title, first-author name, year); title-only or partial matches never merge. Intra-page / best-effort only (a mirror on a different page is not collapsed), so a page may return fewer than size items; pagination is unaffected."
6 tool updates
v0.54.1- Changed
fetch1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
list_sources1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
operate1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
relate1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
resolve3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Output schema / properties / errorsAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "title": "Errors", + "type": "object" +} - added
Output schema / properties / truncatedAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "title": "Truncated", + "type": "object" +}
- Changed
search3 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Output schema / $defs / DataResource / properties / errorsAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "title": "Errors", + "type": "object" +} - added
Output schema / $defs / DataResource / properties / truncatedAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "title": "Truncated", + "type": "object" +}
1 tool update
v0.46.0- Changed
operate1 field changed- added
Input schema / properties / n / minimumAdded value: +1
1 tool update
v0.45.3- Changed
fetch1 field changed- added
Output schema / properties / unverifiedAdded value: +{ + "items": { + "type": "string" + }, + "title": "Unverified", + "type": "array" +}
2 tool updates
v0.45.1- Added
resolve - Added
search
3 tool updates
v0.43.0- Added
relate - Removed
resolve - Removed
search
3 tool updates
v0.42.0- Changed
list_sources1 field changed- changed
Input schema / properties / check_health / descriptionPrevious value: -"When true, probe 5 sources (zenodo, datacite, omics, literature, huggingface) and attach a 'health' field ({status: up|down, latency_ms, detail}) to those entries; the remaining 7 sources get health: null. Default false: returns the static catalog with no network."New value: +"When true, probe 5 sources (zenodo, datacite, omics, literature, huggingface) and attach a 'health' field ({status: up|down, latency_ms, detail}) to those entries; every other source gets health: null. Default false: returns the static catalog with no network."
- Removed
relate - Changed
search4 fields changed- changed
Input schema / properties / sources / descriptionPrevious value: -"Restrict fan-out to these sources (default: all). Available: zenodo, dataone, cellxgene, datacite, dandi, omics, literature, huggingface, omicsdi, openml, pdb, uniprot, gwas"New value: +"Restrict fan-out to these sources (default: all). Available: zenodo, dataone, gbif, cellxgene, datacite, dandi, omics, literature, huggingface, datagov, nasacmr, omicsdi, openml, pdb, uniprot, gwas, biostudies" - added
Output schema / $defs / QueryUnderstanding / properties / confidenceAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confidence" +} - added
Output schema / $defs / UnresolvedEntityAdded value: +{ + "description": "Echo of an ontology-typed search param that was supplied but matched NO term,\nso the query ran WITHOUT that expansion.\n\nDistinct from ``SearchResult.errors``: an entry there means the ontology LOOKUP\nFAILED (HTTP/parse) and the two are mutually exclusive per field. An entry here\nmeans the lookup SUCCEEDED and legitimately returned no match — the common case\nfor a common name the registry does not index (NCBI Taxonomy has no ``yeast``,\n``oak`` or ``cedar``; UBERON has no bare ``root``).\n\nWithout this echo the response for a silently-dropped param is byte-identical to\none where the param was never passed, so the caller cannot tell that the filter\nthey asked for was not applied.", + "properties": { + "field": { + "title": "Field", + "type": "string" + }, + "input": { + "title": "Input", + "type": "string" + }, + "note": { + "title": "Note", + "type": "string" + }, + "ontology": { + "title": "Ontology", + "type": "string" + } + }, + "required": [ + "field", + "input", + "ontology", + "note" + ], + "title": "UnresolvedEntity", + "type": "object" +} - added
Output schema / properties / unresolvedAdded value: +{ + "items": { + "$ref": "#/$defs/UnresolvedEntity" + }, + "title": "Unresolved", + "type": "array" +}
1 tool update
v0.41.1- Changed
search1 field changed- changed
Input schema / properties / sources / descriptionPrevious value: -"Restrict fan-out to these sources (default: all). Available: zenodo, dataone, cellxgene, datacite, dandi, omics, literature, huggingface, omicsdi, openml, pdb, gwas"New value: +"Restrict fan-out to these sources (default: all). Available: zenodo, dataone, cellxgene, datacite, dandi, omics, literature, huggingface, omicsdi, openml, pdb, uniprot, gwas"
5 tool updates
v0.40.0- Changed
list_sources1 field changed- changed
Input schema / properties / check_health / descriptionPrevious value: -"When true, probe each source's base endpoint and attach a 'health' field ({status: up|down, latency_ms, detail}) to each source. Default false: returns the static catalog with no network."New value: +"When true, probe 5 sources (zenodo, datacite, omics, literature, huggingface) and attach a 'health' field ({status: up|down, latency_ms, detail}) to those entries; the remaining 7 sources get health: null. Default false: returns the static catalog with no network."
- Changed
operate1 field changed- changed
Input schema / properties / op / enumPrevious value: -[ - "schema", - "preview", - "head", - "sql" -]New value: +[ + "schema", + "preview", + "head", + "sql", + "peek" +]
- Added
relate - Changed
resolve14 fields changed- added
Input schema / properties / fairAdded value: +{ + "description": "When true, attach an RDA-grounded FAIRness assessment under fair{}: a 0–100 overall score plus findable/accessible/interoperable/reusable sub-scores, the count of indicators evaluated, and actionable gaps each naming its RDA FAIR Data Maturity Model indicator id. Pure/local — no network call. Only the machine-evaluable subset is scored (never fabricates what the metadata cannot show).", + "type": "boolean" +} - changed
Input schema / properties / format / descriptionPrevious value: -"Optional export to render onto the result. 'croissant' attaches a file-level Croissant JSON-LD manifest (croissant field); 'ro-crate' attaches a minimal RO-Crate 1.1 manifest (ro_crate field)."New value: +"Optional export to render onto the result. 'croissant' attaches a file-level Croissant JSON-LD manifest (croissant field); 'ro-crate' attaches a minimal RO-Crate 1.1 manifest (ro_crate field); 'provenance' attaches a one-call RO-Crate 1.1 data-availability dossier (provenance field) bundling version-currency, licence+SPDX, FAIR score, retraction status, and the source/DOI/ID chain — it auto-attaches fair{} and trust{} so the dossier is complete in one call (unknown signals are reported as unknown, never as a clean claim)." - changed
Input schema / properties / format / enumPrevious value: -[ - "croissant", - "ro-crate" -]New value: +[ + "croissant", + "ro-crate", + "provenance" +] - added
Input schema / properties / trustAdded value: +{ + "description": "When true, attach trust signals (retraction status via Crossref) to the result under trust{}. One extra Crossref call; only meaningful for DOI-bearing records (a DataCite data DOI Crossref does not register leaves retracted=null = unknown, never a false clean claim).", + "type": "boolean" +} - added
Input schema / properties / useAdded value: +{ + "description": "When set, attach a licence-compatibility advisory under license_compat{} for an intended use of the record. Supported intents: 'commercial', 'redistribute', 'modify', 'ml-training' (training = a derivative+commercial use, our stated interpretation). The verdict is ALLOW/REVIEW/DENY computed from a bundled choosealicense.com licence matrix keyed on the normalized SPDX id, naming the governing clause — a metadata-derived advisory, NOT legal advice. An unrecognized or absent licence yields REVIEW (never a fabricated ALLOW/DENY); an unknown intent is an error.", + "type": "string" +} - added
Output schema / $defs / FairAssessmentAdded value: +{ + "description": "FAIRness assessment attached on resolve(fair=True). PURE-function output:\na 0–100 overall score plus 0–100 per-dimension sub-scores, grounded in the\nmachine-evaluable subset of the RDA FAIR Data Maturity Model. ``assessed`` is\nthe count of indicators actually evaluated (transparency — we never score what\nthe metadata can't show). ``gaps`` are failed-indicator reasons, each naming its\nRDA indicator id and framed as a metadata-exposure gap, not a value judgement.", + "properties": { + "accessible": { + "title": "Accessible", + "type": "integer" + }, + "assessed": { + "title": "Assessed", + "type": "integer" + }, + "findable": { + "title": "Findable", + "type": "integer" + }, + "gaps": { + "items": { + "type": "string" + }, + "title": "Gaps", + "type": "array" + }, + "interoperable": { + "title": "Interoperable", + "type": "integer" + }, + "reusable": { + "title": "Reusable", + "type": "integer" + }, + "score": { + "title": "Score", + "type": "integer" + } + }, + "required": [ + "score", + "findable", + "accessible", + "interoperable", + "reusable", + "assessed" + ], + "title": "FairAssessment", + "type": "object" +} - added
Output schema / $defs / LicenseVerdictAdded value: +{ + "description": "Licence-compatibility advisory attached on resolve(use=<intent>). PURE-function\noutput: an ALLOW / REVIEW / DENY verdict for an intended use of the resolved record,\ncomputed from a bundled licence matrix (choosealicense.com flag vocabulary) keyed on\nthe normalized SPDX id. ``spdx_id`` is None exactly when the licence was unrecognized\nor absent (→ REVIEW, never a fabricated ALLOW/DENY). ``reason`` names the governing\nclause; ``disclaimer`` states this is a metadata-derived advisory, not legal advice.", + "properties": { + "disclaimer": { + "title": "Disclaimer", + "type": "string" + }, + "license_raw": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "License Raw" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "spdx_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Spdx Id" + }, + "use": { + "title": "Use", + "type": "string" + }, + "verdict": { + "enum": [ + "ALLOW", + "REVIEW", + "DENY" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "use", + "verdict", + "spdx_id", + "license_raw", + "reason", + "disclaimer" + ], + "title": "LicenseVerdict", + "type": "object" +} - added
Output schema / $defs / MirrorAdded value: +{ + "description": "A same-dataset copy folded into this record by content dedup (resolve the\nmirror's id to reach the original deposit). Only populated when a search ran\nwith the opt-in ``collapse_mirrors`` flag.", + "properties": { + "doi": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Doi" + }, + "id": { + "title": "Id", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "title": "Mirror", + "type": "object" +} - added
Output schema / $defs / TrustSignalsAdded value: +{ + "description": "Integrity/provenance signals attached on resolve(trust=True). All nullable:\nNone = not checked or not determinable (e.g. a DOI Crossref doesn't register) —\nNEVER a negative claim. A *found* Crossref work yields definitive booleans.", + "properties": { + "concern": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Concern" + }, + "retracted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Retracted" + }, + "retraction_doi": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Retraction Doi" + } + }, + "title": "TrustSignals", + "type": "object" +} - added
Output schema / properties / fairAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/FairAssessment" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / license_compatAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/LicenseVerdict" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / mirrorsAdded value: +{ + "items": { + "$ref": "#/$defs/Mirror" + }, + "title": "Mirrors", + "type": "array" +} - added
Output schema / properties / provenanceAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provenance" +} - added
Output schema / properties / trustAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TrustSignals" + }, + { + "type": "null" + } + ], + "default": null +}
- Changed
search31 fields changed- added
Input schema / properties / assayAdded value: +{ + "description": "Optional assay/method name. Resolved via EDAM topics (EBI OLS); the query is expanded with the canonical name + exact synonyms (e.g. 'ChIP-seq' also matches 'ChIP-sequencing'/'ChIP-exo'). An unknown term yields no expansion; an OLS failure surfaces in errors. The expansion is echoed in assay_expansion.", + "type": "string" +} - added
Input schema / properties / chemicalAdded value: +{ + "description": "Optional chemical/compound name. Resolved via ChEBI (EBI OLS); the query is expanded with the canonical name + exact synonyms (e.g. 'caffeine' also matches '1,3,7-trimethylxanthine'), capped to a bounded number of synonyms. An unknown term yields no expansion; an OLS failure surfaces in errors. The expansion is echoed in chemical_expansion.", + "type": "string" +} - added
Input schema / properties / collapse_mirrorsAdded value: +{ + "default": false, + "description": "Opt into conservative cross-repo content dedup (default false). On top of the always-on exact-DOI dedup, folds records that are the SAME dataset deposited under different (or no) DOIs — e.g. a Zenodo mirror of a figshare deposit, GEO<->ArrayExpress — into one record, annotating the survivor with the folded copies under mirrors[]. Conservative: a merge needs a shared file checksum OR identical (normalized-title, first-author-surname, year); title-only or partial matches never merge. Intra-page / best-effort only (a mirror on a different page is not collapsed), so a page may return fewer than size items; pagination is unaffected.", + "type": "boolean" +} - added
Input schema / properties / diseaseAdded value: +{ + "description": "Optional disease/phenotype name. Resolved via MeSH (NCBI E-utilities); the query is expanded with the canonical descriptor + entry-term synonyms (e.g. 'breast cancer' also matches 'Breast Neoplasms'). The expansion is echoed in mesh_expansion.", + "type": "string" +} - added
Input schema / properties / multi_queryAdded value: +{ + "default": false, + "description": "Opt into diverse multi-query recall expansion: an LLM generates up to a few deliberately-diverse reformulations of your query, each is fanned out across all sources, and the deduped union is re-ranked against your original query — surfacing relevant records a single keyword query would miss. Costs N× the upstream calls (bounded). Requires an LLM endpoint (LLM_API_BASE); with none configured the search runs as a normal single query and notes it in errors['multi_query']. The variants used are echoed in query_expansion. Composes with understand=. NOTE: multi_query=true ALWAYS applies semantic re-ranking of the window internally regardless of rank=; the rank= param has no effect in this mode.", + "type": "boolean" +} - added
Input schema / properties / provenanceAdded value: +{ + "default": false, + "description": "Opt into a whole-search RO-Crate 1.1 Run Crate (default false). Attaches provenance_crate{} — a machine-readable manifest documenting this search: the query, the sources queried, the ontology expansions that fired, the per-source errors (a partial search is disclosed), and per-hit provenance for every result (version-currency, licence + normalized SPDX, FAIR score). Per-hit RETRACTION is omitted — it would need one Crossref call per hit; use per-record resolve(format=provenance) for that. Covers THIS search page only (intra-page; each page of a paginated search gets its own crate).", + "type": "boolean" +} - changed
Input schema / properties / sources / descriptionPrevious value: -"Restrict fan-out to these sources (default: all). Available: zenodo, datacite, omics, literature, huggingface, dataone, omicsdi"New value: +"Restrict fan-out to these sources (default: all). Available: zenodo, dataone, cellxgene, datacite, dandi, omics, literature, huggingface, omicsdi, openml, pdb, gwas" - added
Input schema / properties / tissueAdded value: +{ + "description": "Optional tissue/anatomy name. Resolved via UBERON (EBI OLS); the query is expanded with the canonical term + exact synonyms (e.g. 'liver' also matches 'iecur'/'jecur'). The expansion is echoed in tissue_expansion.", + "type": "string" +} - added
Input schema / properties / understandAdded value: +{ + "default": false, + "description": "Opt into LLM query understanding: a free-text query is rewritten into a keyword core + structured params (organism/disease/tissue/chemical/assay, kind, year) before fan-out; extracted entities are validated by the same ontology resolvers (a hallucinated entity that doesn't resolve is simply dropped), explicit params you pass always win, and the interpretation is echoed in query_understanding. Requires an LLM endpoint (LLM_API_BASE); with none configured the search runs unchanged and notes it in errors['understand'].", + "type": "boolean" +} - added
Output schema / $defs / AssayExpansionAdded value: +{ + "description": "Echo of EDAM assay-synonym expansion that fired for a search (transparency).", + "properties": { + "canonical_name": { + "title": "Canonical Name", + "type": "string" + }, + "edam_id": { + "title": "Edam Id", + "type": "string" + }, + "input": { + "title": "Input", + "type": "string" + }, + "synonyms": { + "items": { + "type": "string" + }, + "title": "Synonyms", + "type": "array" + } + }, + "required": [ + "input", + "edam_id", + "canonical_name", + "synonyms" + ], + "title": "AssayExpansion", + "type": "object" +} - added
Output schema / $defs / ChemicalExpansionAdded value: +{ + "description": "Echo of ChEBI chemical-synonym expansion that fired for a search (transparency).", + "properties": { + "canonical_name": { + "title": "Canonical Name", + "type": "string" + }, + "chebi_id": { + "title": "Chebi Id", + "type": "string" + }, + "input": { + "title": "Input", + "type": "string" + }, + "synonyms": { + "items": { + "type": "string" + }, + "title": "Synonyms", + "type": "array" + } + }, + "required": [ + "input", + "chebi_id", + "canonical_name", + "synonyms" + ], + "title": "ChemicalExpansion", + "type": "object" +} - added
Output schema / $defs / DataResource / properties / fairAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/FairAssessment" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / $defs / DataResource / properties / license_compatAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/LicenseVerdict" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / $defs / DataResource / properties / mirrorsAdded value: +{ + "items": { + "$ref": "#/$defs/Mirror" + }, + "title": "Mirrors", + "type": "array" +} - added
Output schema / $defs / DataResource / properties / provenanceAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provenance" +} - added
Output schema / $defs / DataResource / properties / trustAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TrustSignals" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / $defs / FairAssessmentAdded value: +{ + "description": "FAIRness assessment attached on resolve(fair=True). PURE-function output:\na 0–100 overall score plus 0–100 per-dimension sub-scores, grounded in the\nmachine-evaluable subset of the RDA FAIR Data Maturity Model. ``assessed`` is\nthe count of indicators actually evaluated (transparency — we never score what\nthe metadata can't show). ``gaps`` are failed-indicator reasons, each naming its\nRDA indicator id and framed as a metadata-exposure gap, not a value judgement.", + "properties": { + "accessible": { + "title": "Accessible", + "type": "integer" + }, + "assessed": { + "title": "Assessed", + "type": "integer" + }, + "findable": { + "title": "Findable", + "type": "integer" + }, + "gaps": { + "items": { + "type": "string" + }, + "title": "Gaps", + "type": "array" + }, + "interoperable": { + "title": "Interoperable", + "type": "integer" + }, + "reusable": { + "title": "Reusable", + "type": "integer" + }, + "score": { + "title": "Score", + "type": "integer" + } + }, + "required": [ + "score", + "findable", + "accessible", + "interoperable", + "reusable", + "assessed" + ], + "title": "FairAssessment", + "type": "object" +} - added
Output schema / $defs / LicenseVerdictAdded value: +{ + "description": "Licence-compatibility advisory attached on resolve(use=<intent>). PURE-function\noutput: an ALLOW / REVIEW / DENY verdict for an intended use of the resolved record,\ncomputed from a bundled licence matrix (choosealicense.com flag vocabulary) keyed on\nthe normalized SPDX id. ``spdx_id`` is None exactly when the licence was unrecognized\nor absent (→ REVIEW, never a fabricated ALLOW/DENY). ``reason`` names the governing\nclause; ``disclaimer`` states this is a metadata-derived advisory, not legal advice.", + "properties": { + "disclaimer": { + "title": "Disclaimer", + "type": "string" + }, + "license_raw": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "License Raw" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "spdx_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Spdx Id" + }, + "use": { + "title": "Use", + "type": "string" + }, + "verdict": { + "enum": [ + "ALLOW", + "REVIEW", + "DENY" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "use", + "verdict", + "spdx_id", + "license_raw", + "reason", + "disclaimer" + ], + "title": "LicenseVerdict", + "type": "object" +} - added
Output schema / $defs / MeshExpansionAdded value: +{ + "description": "Echo of MeSH-synonym expansion that fired for a search (transparency).", + "properties": { + "canonical_name": { + "title": "Canonical Name", + "type": "string" + }, + "input": { + "title": "Input", + "type": "string" + }, + "mesh_ui": { + "title": "Mesh Ui", + "type": "string" + }, + "synonyms": { + "items": { + "type": "string" + }, + "title": "Synonyms", + "type": "array" + } + }, + "required": [ + "input", + "mesh_ui", + "canonical_name", + "synonyms" + ], + "title": "MeshExpansion", + "type": "object" +} - added
Output schema / $defs / MirrorAdded value: +{ + "description": "A same-dataset copy folded into this record by content dedup (resolve the\nmirror's id to reach the original deposit). Only populated when a search ran\nwith the opt-in ``collapse_mirrors`` flag.", + "properties": { + "doi": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Doi" + }, + "id": { + "title": "Id", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "title": "Mirror", + "type": "object" +} - added
Output schema / $defs / QueryExpansionAdded value: +{ + "description": "Transparency echo of A2.P2 multi-query recall expansion (search multi_query=true).\n\nWhen enabled and an LLM endpoint is configured, the LLM generates deliberately-diverse\nreformulations of the query; each variant is fanned out across all sources, and the\ndeduped union is re-ranked against the ORIGINAL query. ``variants`` lists the RAW variants\nactually fanned out, the original query first. Each variant received the same ontology\nexpansion (shown by the ``*_expansion`` echoes); results are the deduped union re-ranked\nagainst ``input``.", + "properties": { + "input": { + "title": "Input", + "type": "string" + }, + "variants": { + "items": { + "type": "string" + }, + "title": "Variants", + "type": "array" + } + }, + "required": [ + "input", + "variants" + ], + "title": "QueryExpansion", + "type": "object" +} - added
Output schema / $defs / QueryUnderstandingAdded value: +{ + "description": "Echo of the LLM query-understanding rewrite that fired (transparency, A2.P1).\n\nThe LLM proposes; explicit caller params win; the ontology resolvers then VALIDATE the\nproposed entities. ``applied`` lists the fields the caller left None that were FED into\nthis search as parameters — for ontology entities (organism/disease/tissue/chemical/\nassay) this means \"passed to the resolver\", NOT \"resolved\": whether it actually expanded\nis shown by the corresponding ``*_expansion`` echo (None there ⇒ the entity did not\nresolve, and was never silently treated as a match). ``overridden`` lists fields the LLM\nproposed but the caller had set explicitly (so the LLM's value was ignored).", + "properties": { + "applied": { + "additionalProperties": true, + "title": "Applied", + "type": "object" + }, + "extracted": { + "additionalProperties": true, + "title": "Extracted", + "type": "object" + }, + "input": { + "title": "Input", + "type": "string" + }, + "keyword_core": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Keyword Core" + }, + "overridden": { + "items": { + "type": "string" + }, + "title": "Overridden", + "type": "array" + } + }, + "required": [ + "input", + "keyword_core" + ], + "title": "QueryUnderstanding", + "type": "object" +} - added
Output schema / $defs / TissueExpansionAdded value: +{ + "description": "Echo of UBERON tissue-synonym expansion that fired for a search (transparency).", + "properties": { + "canonical_name": { + "title": "Canonical Name", + "type": "string" + }, + "input": { + "title": "Input", + "type": "string" + }, + "synonyms": { + "items": { + "type": "string" + }, + "title": "Synonyms", + "type": "array" + }, + "uberon_id": { + "title": "Uberon Id", + "type": "string" + } + }, + "required": [ + "input", + "uberon_id", + "canonical_name", + "synonyms" + ], + "title": "TissueExpansion", + "type": "object" +} - added
Output schema / $defs / TrustSignalsAdded value: +{ + "description": "Integrity/provenance signals attached on resolve(trust=True). All nullable:\nNone = not checked or not determinable (e.g. a DOI Crossref doesn't register) —\nNEVER a negative claim. A *found* Crossref work yields definitive booleans.", + "properties": { + "concern": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Concern" + }, + "retracted": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Retracted" + }, + "retraction_doi": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Retraction Doi" + } + }, + "title": "TrustSignals", + "type": "object" +} - added
Output schema / properties / assay_expansionAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/AssayExpansion" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / chemical_expansionAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/ChemicalExpansion" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / mesh_expansionAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/MeshExpansion" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / provenance_crateAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provenance Crate" +} - added
Output schema / properties / query_expansionAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/QueryExpansion" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / query_understandingAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/QueryUnderstanding" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / tissue_expansionAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/TissueExpansion" + }, + { + "type": "null" + } + ], + "default": null +}
3 tool updates
v0.20.0- Added
operate - Changed
resolve9 fields changed- added
Input schema / properties / formatAdded value: +{ + "description": "Optional export to render onto the result. 'croissant' attaches a file-level Croissant JSON-LD manifest (croissant field); 'ro-crate' attaches a minimal RO-Crate 1.1 manifest (ro_crate field).", + "enum": [ + "croissant", + "ro-crate" + ], + "type": "string" +} - added
Output schema / $defs / MetricsAdded value: +{ + "description": "Usage/impact signals, each a separate axis — NO blended score. All\nnullable: a source that does not expose an axis leaves it None.", + "properties": { + "citations": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Citations" + }, + "downloads": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Downloads" + }, + "likes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Likes" + }, + "views": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Views" + } + }, + "title": "Metrics", + "type": "object" +} - added
Output schema / properties / access_modesAdded value: +{ + "items": { + "type": "string" + }, + "title": "Access Modes", + "type": "array" +} - added
Output schema / properties / croissantAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Croissant" +} - added
Output schema / properties / is_latestAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Is Latest" +} - added
Output schema / properties / last_updatedAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Updated" +} - added
Output schema / properties / metricsAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/Metrics" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / properties / ro_crateAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ro Crate" +} - added
Output schema / properties / superseded_byAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Superseded By" +}
- Changed
search9 fields changed- changed
Input schema / properties / sources / descriptionPrevious value: -"Restrict fan-out to these sources (default: all). Available: zenodo, datacite, omics, literature, huggingface"New value: +"Restrict fan-out to these sources (default: all). Available: zenodo, datacite, omics, literature, huggingface, dataone, omicsdi" - added
Output schema / $defs / DataResource / properties / access_modesAdded value: +{ + "items": { + "type": "string" + }, + "title": "Access Modes", + "type": "array" +} - added
Output schema / $defs / DataResource / properties / croissantAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Croissant" +} - added
Output schema / $defs / DataResource / properties / is_latestAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Is Latest" +} - added
Output schema / $defs / DataResource / properties / last_updatedAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Updated" +} - added
Output schema / $defs / DataResource / properties / metricsAdded value: +{ + "anyOf": [ + { + "$ref": "#/$defs/Metrics" + }, + { + "type": "null" + } + ], + "default": null +} - added
Output schema / $defs / DataResource / properties / ro_crateAdded value: +{ + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ro Crate" +} - added
Output schema / $defs / DataResource / properties / superseded_byAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Superseded By" +} - added
Output schema / $defs / MetricsAdded value: +{ + "description": "Usage/impact signals, each a separate axis — NO blended score. All\nnullable: a source that does not expose an axis leaves it None.", + "properties": { + "citations": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Citations" + }, + "downloads": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Downloads" + }, + "likes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Likes" + }, + "views": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Views" + } + }, + "title": "Metrics", + "type": "object" +}
4 tool updates
v0.16.0- Changed
fetch1 field changed- added
Output schema / properties / resumedAdded value: +{ + "items": { + "type": "string" + }, + "title": "Resumed", + "type": "array" +}
- Changed
list_sources1 field changed- added
Input schema / properties / check_healthAdded value: +{ + "default": false, + "description": "When true, probe each source's base endpoint and attach a 'health' field ({status: up|down, latency_ms, detail}) to each source. Default false: returns the static catalog with no network.", + "type": "boolean" +}
- Changed
resolve5 fields changed- added
Output schema / $defs / CreatorAdded value: +{ + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "orcid": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Orcid" + } + }, + "required": [ + "name" + ], + "title": "Creator", + "type": "object" +} - added
Output schema / $defs / FundingRefAdded value: +{ + "properties": { + "award": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Award" + }, + "funder": { + "title": "Funder", + "type": "string" + } + }, + "required": [ + "funder" + ], + "title": "FundingRef", + "type": "object" +} - added
Output schema / properties / creators / items / $refAdded value: +"#/$defs/Creator" - removed
Output schema / properties / creators / items / typeRemoved value: -"string" - added
Output schema / properties / fundingAdded value: +{ + "items": { + "$ref": "#/$defs/FundingRef" + }, + "title": "Funding", + "type": "array" +}
- Changed
search12 fields changed- added
Input schema / properties / cursorAdded value: +{ + "description": "Opaque pagination token from a prior search's next_cursor. When set, all other search params are read from the cursor.", + "type": "string" +} - added
Input schema / properties / kindAdded value: +{ + "description": "Keep only results of this kind.", + "enum": [ + "dataset", + "sequencing_run", + "study", + "publication", + "software" + ], + "type": "string" +} - added
Input schema / properties / published_afterAdded value: +{ + "description": "Keep results with year >= this.", + "type": "integer" +} - added
Input schema / properties / published_beforeAdded value: +{ + "description": "Keep results with year <= this.", + "type": "integer" +} - added
Input schema / properties / rankAdded value: +{ + "default": "relevance", + "description": "Result ordering. 'relevance' (default) = upstream/merged order. 'semantic' re-ranks the fetched page by embedding similarity to the query (needs EMBEDDING_API_BASE; degrades to relevance order with an errors['semantic'] note if unconfigured). In semantic mode pagination is window-based (each page consumes its full fetched window).", + "enum": [ + "relevance", + "semantic" + ], + "type": "string" +} - changed
Input schema / properties / sources / descriptionPrevious value: -"Restrict fan-out to these sources (default: all). Available: zenodo, datacite, omics, literature"New value: +"Restrict fan-out to these sources (default: all). Available: zenodo, datacite, omics, literature, huggingface" - removed
Input schema / requiredRemoved value: -[ - "query" -] - added
Output schema / $defs / CreatorAdded value: +{ + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "orcid": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Orcid" + } + }, + "required": [ + "name" + ], + "title": "Creator", + "type": "object" +} - added
Output schema / $defs / DataResource / properties / creators / items / $refAdded value: +"#/$defs/Creator" - removed
Output schema / $defs / DataResource / properties / creators / items / typeRemoved value: -"string" - added
Output schema / $defs / DataResource / properties / fundingAdded value: +{ + "items": { + "$ref": "#/$defs/FundingRef" + }, + "title": "Funding", + "type": "array" +} - added
Output schema / $defs / FundingRefAdded value: +{ + "properties": { + "award": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Award" + }, + "funder": { + "title": "Funder", + "type": "string" + } + }, + "required": [ + "funder" + ], + "title": "FundingRef", + "type": "object" +}
4 tool updates
v0.11.0- First observed
fetch - First observed
list_sources - First observed
resolve - First observed
search
TDQS
Scored across 6 tools
Each tool occupies a distinct phase of the data lifecycle: list_sources (source metadata), search (fan-out discovery), resolve (full record), operate (remote tabular inspection), fetch (download), and relate (join hints). The descriptions explicitly state boundaries (e.g. resolve returns the full record, fetch returns paths, relate is HINTS ONLY), so an agent can pick the right tool without confusion.
All names are lowercase snake_case and verb-oriented (search, resolve, fetch, operate, relate), giving a predictable pattern. The only minor deviation is list_sources, a two-word verb_noun form among single verbs, but it remains readable and consistent in style.
Six tools is well-scoped for a multi-repo data aggregation server; each tool maps to a necessary capability (source listing, search, resolve, fetch, inspect, relate) with no redundant or filler entries. Nothing feels thin or bloated.
The surface covers the full workflow from source awareness (list_sources) through discovery (search), record retrieval (resolve), file download (fetch), remote inspection (operate), and cross-dataset linkage (relate). No obvious dead ends for the stated aggregation purpose; relate being hints-only is an explicit design choice rather than a gap.
Maintenance
Related MCP Connectors
Federated search of books and papers, BibTeX/RIS citations, open-access retrieval and reading.
Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms.
Search 150M+ academic works, journals, and funders via Crossref API.
Scholarly search: OpenAlex, Crossref, arXiv, OpenCitations and PubMed in one endpoint.
Related MCP Servers
- AlicenseBqualityAmaintenanceEnables searching and downloading academic papers from 14 platforms including arXiv, PubMed, Google Scholar, Web of Science, Springer, and Sci-Hub with unified data format and intelligent rate limiting.21747 npm186MIT
- AlicenseAqualityCmaintenanceEnables users to search, download, and read academic papers from multiple platforms including arXiv, PubMed, bioRxiv, Google Scholar, Semantic Scholar, and CrossRef through a unified interface.344MIT
- FlicenseNot gradedqualityAmaintenanceEnables searching for academic papers and preprints across multiple platforms including Semantic Scholar, arXiv, PubMed, and CrossRef. It provides access to research records, DOI lookups, and journal metadata through a unified interface deployed on Cloudflare Workers.-
- AlicenseAqualityAmaintenance▎ Provides 32 tools for plant-genomics locus lookup across 11 free public backends (Ensembl Plants, Phytozome, UniProtKB, Europe PMC, QuickGO, NCBI BLAST, Gramene, KEGG, STRING-DB, ATTED-II, BAR). Takes a TAIR-style locus plus optional organism and returns gene metadata, functional/pathway annotation, interactions, co-expression, and literature — in single-locus, batch, and cross-source synthesis.567MIT