Skip to content

How to resolve a single citation

You have a single persistent-identifier key and want the metadata for it. Two ways to ask — pick by where your agent or shell tool lives.

The stdout mode of resolve writes CSL JSON straight to the pipe. Hand it one or more keys as arguments:

Terminal window
quartobot resolve --output - doi:10.21105/joss.01686 | jq '.[0].title'
"Welcome to the Tidyverse"

The JSON shape is CSL JSON — the same structure pandoc-citeproc reads. --output - skips the cache write, the human-readable summary line goes to stderr, and exit is non-zero if any key fails to resolve.

The resolver handles every persistent-identifier prefix manubot supports. Each call below resolves a single key against its source registrar:

Terminal window
# Journal DOI (Crossref) — "Welcome to the Tidyverse"
quartobot resolve --output - doi:10.21105/joss.01686 | jq '.[0].title'
# PubMed ID (PubMed) — GTEx Consortium pilot analysis
quartobot resolve --output - pmid:23715323 | jq '.[0].title'
# arXiv preprint (arXiv) — "Attention Is All You Need"
quartobot resolve --output - arxiv:1706.03762 | jq '.[0].title'
# bioRxiv preprint via DOI (Crossref-routed to bioRxiv) — Seurat v3
quartobot resolve --output - doi:10.1101/460147 | jq '.[0].title'

Bare DOIs (no doi: prefix) work too. url:, wikidata:, isbn:, and pmc: are also valid. See CLI reference: resolve for the full flag surface (--from-scan, --id-mode, --cache, --dry-run).

The same lookup, exposed to an agent that speaks MCP. After the server is wired into your authoring client (Claude Desktop, Codex, Gemini Code Assist, Cursor), the agent calls one tool:

resolve_citation(cite_key="doi:10.21105/joss.01686")

What comes back is the CSL JSON object for that key — same payload as one element of the CLI’s array. The agent can read the title, author list, year, container, DOI, and drop a grounded citation into the draft instead of guessing.

A leading @ or trailing pandoc-terminator punctuation (/, ., ,) is normalized away, so the tool is forgiving about the exact shape the agent extracts from prose.

See MCP server for the full per-client config (Claude Desktop, Codex, Gemini Code Assist, Cursor).

What an unresolvable key looks like on each surface:

CLI — non-zero exit, stderr message:

$ quartobot resolve --output - doi:not.a.real.doi/x
✗ doi:not.a.real.doi/x — could not resolve
0 resolved, 1 failed.
$ echo $?
1

Stdout stays empty (or carries [] if other keys in the same call resolved). A pipeline downstream sees the non-zero exit; a shell set -e catches it.

MCP — the tool returns a dict rather than raising:

{
"error": "could not resolve cite key: doi:not.a.real.doi/x",
"cite_key": "doi:not.a.real.doi/x"
}

The MCP server stays up. The agent sees a usable response and can explain the failure to the author or try a different key.

MCP when your authoring client speaks it natively (Claude Desktop, Codex, Gemini Code Assist, Cursor). CLI stdout when your agent shells out, or when you’re in a terminal piping through jq. Both reach the same manubot.cite.citekey_to_csl_item underneath — eight years of source-API quirks behind one call.