The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Zotio listing page.
The trust-and-automation layer for Zotero
Docs: Get started · Commands · MCP server · Safe writes · Workflows
zotio gives humans, scripts, and MCP agents local-first search
and auditing, preview-first writes, and bounded, provenance-tagged context
for Zotero libraries.
Fast proof: check a bundled library before trying zotio on your own data.
That is the bundled library catching the retracted Wakefield 1998 MMR paper,
with no Zotero install and no API key (the check queries Crossref live).
zotio demo --reset removes it.
Zotero itself red-flags a retracted item when you open it, from the same
Retraction Watch data. zotio adds what the desktop app does not: corrections
and expressions of concern, a report across a whole library that scripts can
consume, and an exit code CI can fail on.
Point it at your real library:
Zotero's GUI is great for reading and citing. It is painful the moment you need to operate on a library at scale: find every article missing a PDF, catch duplicate \cite{} keys before a submission, export a week of highlights, keep an Obsidian vault in sync, or hand an AI agent trustworthy context. Existing CLIs and pyzotero give you raw API access — then you write the glue, and you own the risk.
zotio is the glue, hardened:
zotio never calls an LLM; it does the assembly and budgeting a model is bad at, then hands off.It is the tool you reach for when the GUI gets too manual: find the problems that bite downstream, fix them safely, ingest with review, and give agents a surface they can trust.
Reads stay on your machine. Writes split by intent: creating a new item (with its attachments/PDFs) prefers the local desktop connector (localhost:23119, no key — the same channel the browser "Save to Zotero" button uses), while everything else — field edits, deletes, enrichment, tag ops, moves, and collections create/update — routes to the Zotero Web API and needs a configured key. The connector path is a preference, not a guarantee: --via auto uses it only on a personal library with the desktop running, and falls back to the Web API otherwise (group libraries always go to the cloud). Either way it's preview-first, the version-read happens locally, and the applied change is replayed into your local mirror so a follow-up read sees it without another sync.
| Plane | Backend | Needs a key? |
|---|---|---|
| Read | Local Zotero API (localhost:23119) + synced SQLite mirror | No |
| Write — new item | Local desktop connector (localhost:23119) when personal + desktop up; else Web API. New items, attachments, PDFs. | No (connector path) |
| Write — everything else | Zotero Web API (api.zotero.org) — edits, deletes, enrich, tags, moves, collections create/update | Yes — configured once |
| External | CrossRef · OpenAlex · Semantic Scholar · Unpaywall · OpenCitations | No (feeds enrich/import) |
| Local-only | Files, desktop launch, vault, introspection | No |
Run zotio doctor any time to see connectivity, cache freshness, and a writes: line telling you whether write-back is available or read-only.
library healthOne command that answers a real question — "is this library fit for the next thing I'm going to do with it?" — instead of making you run six separate audits and eyeball the output.
library health composes the checks that already exist (citekey conflicts, duplicates, missing metadata, tag drift, broken attachments) into one ranked, finding-typed report. You pick what "ready" means with --for:
--for | Prepares for | Checks |
|---|---|---|
quick (default) | anything obviously broken | citekey conflicts, duplicates, broken attachments |
citation | a manuscript bibliography | missing/duplicate citekeys, citation-core fields, duplicates |
systematic-review | a PRISMA screening corpus | duplicates, screenable metadata (title/abstract), full-text PDFs |
vault | a trustworthy Obsidian/PKM vault sync | missing/duplicate citekeys (note filenames), citation-core fields |
all | a full sweep | every registered check |
Three things make it trustworthy, not just convenient:
--fail-on critical|high|info|none exits 11 when the bar isn't met — drop it in a pre-submission hook. --require-fresh 24h exits 12 if your local mirror is stale.9 (setup required) rather than falsely passing.recommended_action naming the exact existing command (items enrich, items duplicates resolve, tags audit fix) — health diagnoses, dedicated commands treat.--badge renders any health run as a shields.io endpoint JSON artifact — healthy green, findings yellow, gate-failure red, setup required orange:
Your thesis or review repo gets a live bibliography | healthy badge — and a failing build the moment a citekey conflict or duplicate slips in. Add --check-retractions to extend the gate to retracted papers (Crossref's Retraction Watch data), and gate the manuscript itself with zotio items bibcheck paper.tex --fail-on-unknown. The zotio-action packages this — install, sync, gate, and diff against a baseline so it fails only on new problems (guide).
See it running for real: zotio's own docs deploy publishes a live badge off the maintainer's real Zotero library (workflow, guide).
Every write command — items enrich, tags audit fix, items duplicates resolve, items preprint-check fix, items create/update/move/delete, import apply, vault push — flows through one mutation envelope with identical, predictable semantics.
--yes applies; --dry-run always wins.--agent does not auto-apply. Agent mode sets JSON + non-interactive defaults, but a write still needs an explicit --yes.--max-changes defaults to 500 (50 under --agent); irreversible ops (merge, permanent delete, empty-trash) refuse to run without --allow-destructive.sync.journal list / journal show). journal undo <run-id> reverses the reversible ops (tag renames, collection membership, and creates — reversed by trashing the created item) and loudly refuses the rest (merges, deletions, field overwrites) rather than guessing.Bulk-import references through a review checkpoint. Nothing hits your library until you've seen — and can edit — the manifest.
--attach-mode none|linked-file|stored contract. stored uploads the file bytes: --via connector hands the item and its file to Zotero desktop in one session, while the direct Web API route is guarded by the file-storage precondition (--allow-zotero-cloud to override).One-shot importers are there too: import doi|pmid|arxiv|isbn|url|file|pdf.
Keep an Obsidian or Logseq vault in step with Zotero in both directions — without ever clobbering your prose.
Each note has a managed region (frontmatter + a fenced annotations block, refreshed on every sync) and your region (## Notes, prose preserved untouched). Write-back is fast-forward only: if both sides changed, zotio never merges blindly — it writes a reviewable conflict artifact under _vault-zotero-conflicts/ and reports it, so divergence becomes something you resolve on purpose (vault resolve --keep-vault | --keep-remote | --recreate), never a silent overwrite. Run vault audit for a read-only preflight before any push.
Configure the vault once in ~/.config/zotio/config.toml:
pyzotero don't give youitems retract-check — check every DOI against Crossref's Retraction Watch data: retractions, expressions of concern, and corrections, with notice DOIs and dates. Opt into the library health gate with --check-retractions. (This one reads the network.)collections gaps — citation-graph gap analysis: rank the papers your collection cites most that are missing from your library (OpenCitations + Semantic Scholar), then import doi them. (Network too.)items bibcheck <manuscript> — parse \cite{}/@citekey from .tex or pandoc Markdown and resolve every key against your library — unknown and ambiguous keys flagged, and an unknown key carries the closest keys in your library as an advisory suggestion when one is close enough to name, so a one-character typo is a named fix rather than a hand-grep of the .bib. --fail-on-unknown exits 11 for CI.tags audit — group tags that differ only by case or variant, with item counts and ready-to-run merge commands. On a real 840-tag library it surfaced 53 duplicate groups in one pass.library stats — a one-command dashboard: items by type and year, top venues, PDF coverage, and optional intake buckets with --added-by month|year.items audit — count and list items missing PDFs, abstracts, DOIs, tags, or citation-core fields; --verify-files checks PDFs actually exist on disk.items duplicates — detect likely duplicates by DOI or title (attachments/notes excluded), then duplicates resolve to merge safely.items citekey-conflicts — find missing or duplicate Better BibTeX keys before they break a LaTeX build.items find — resolve a DOI, ISBN, PMID, arXiv ID, citation key, URL, OpenAlex work ID, or exact title against the local mirror before importing a duplicate. A title matches whatever its case, whitespace, quote and dash styling, or trailing full stop, so a title pasted from a reference list resolves. When the lookup as a whole matches nothing, the closest titles or keys in the library are listed separately with a score, so a typo is distinguishable from an absent paper; they are suggestions to confirm, never matches. The selectors are OR-ed, so a run that matched on another selector reports no near rows for the one that missed.items summarize — assemble a bounded, synthesis-ready bundle for an item or collection (citation + abstract + your annotations + a capped fulltext excerpt + known metadata gaps + a synthesis prompt) and hand it to any LLM. zotio does the budgeting; it never calls the model.annotations export · annotations timeline · annotations search — pull highlights and notes as Markdown or JSON, ordered by date or searched by text.reading-list — a to-read tag queue with an add → start → done lifecycle for triaging what to read next.items note-template — generate a pre-filled Obsidian/Logseq reading note for an item.items open — print or launch a zotero:// deep link to an item, collection, or PDF (cross-platform).library wrapped — your Zotero year in review: hero counts, monthly rhythm with peak highlight, a type-mix ratio bar, highlights (busiest day, longest streak, deep cut, most-annotated paper, top tag), top venues and full author names, PDF coverage — plus a shareable SVG card in four layouts via --card-style overview|rhythm|picks|cycle (the card below is cycle, crossfading through all three):
items enrich — fill missing DOIs and abstracts from CrossRef → OpenAlex → Semantic Scholar; fill missing citation fields from CrossRef; attach open-access PDFs from Unpaywall; and record provenance in each item's Extra field. --missing-citation fills only blank fields supported by the item type, including provider-backed volume, issue, and pages. --validate reports title, year, and render-field discrepancies against CrossRef, plus DOI registration failures from OpenCitations, without writing.items preprint-check — find arXiv preprints that now have a published CrossRef record; preprint-check fix upgrades them with the journal DOI — preview-first, journaled, and it never overwrites a conflicting DOI.items bibliography — render a shared scope with a CSL style, or export it as CSL-JSON, BibTeX, BibLaTeX, or RIS. CSL-JSON uses unique Better BibTeX citation keys for Pandoc and Quarto.collections export — a whole collection and its subcollections as one BibTeX, RIS, or CSL-JSON file, structure preserved in comments.export snapshot — a reproducible, resumable, fully paginated JSONL export with a <output>.lock.json content lockfile (sorted key+version + sha256) for drift detection and clean review handoffs.sync · watch · tail — populate the mirror, keep it fresh with periodic incremental syncs, or stream live changes. watch --health diffs library health between cycles and reports new findings to stdout or a webhook — hear about drift the cycle it appears.schema drift — after a Zotero upgrade, detect item-type / field / creator-field changes against a saved baseline.zotio publishes a machine-readable trust model so an MCP host, CI job, or shell script can discover what's safe, fresh, and writable before it acts.
--agent on any command: JSON + compact + non-interactive + no color, in one flag. (It never auto-applies writes.)capabilities — the full registry (140 commands), each tagged with operation, data_sources, write_target, destructive, and requires preconditions. Commands with several write routes also list each route's target and preconditions.agent-context — a structured description of the whole CLI, embedding the registry and discovery hints.which "<capability in your words>" — resolve a natural-language query to the command that does it.Scope grammar — one selection vocabulary across reads, audits, exports, and enrich:
Exit codes: 0 ok · 2 usage · 3 not-found · 4 auth · 5 API · 7 rate-limited · 9 precondition/setup (including another writer holds the lock — retry) · 10 config · 11 quality-gate failed · 12 freshness-gate failed · 13 degraded — incomplete: part of a read was unreadable, or part of a batched write was rejected after other elements succeeded. Output is not guaranteed; read the reported failures and reconcile before retrying.
zotio comes in three pieces you can install independently: the CLI (the engine — everything runs through it), the agent skill (drives the CLI inside coding agents), and the MCP server (exposes the CLI to MCP hosts like Claude Desktop). Most people want the CLI; add the skill or MCP server for your agent of choice.
zotioHomebrew (macOS):
This installs both zotio and the zotio-mcp MCP server; brew upgrade tracks new releases.
Linux (deb / rpm / apk): there is no apt/dnf/pacman repository — the packages
are GitHub release assets. Install
with dpkg -i, rpm -i, or apk add --allow-untrusted; the
install guide has a snippet
that resolves the latest version and your architecture. Homebrew works on Linux
too — the tap ships formulae, not casks (brew install orgmentem/tap/zotio).
Windows (WinGet / Scoop): winget install OrgMentem.zotio, or
scoop bucket add orgmentem https://github.com/OrgMentem/scoop-bucket && scoop install zotio.
Both install zotio and zotio-mcp. WinGet can serve the previous version for
a short window after a release while Microsoft merges the version bump; Scoop
updates immediately.
Prebuilt binaries: every GitHub release ships archives for macOS, Linux, and Windows (amd64/arm64) with cosign-signed checksums and SBOMs. Unpack and put zotio on your PATH; on macOS clear the Gatekeeper quarantine (xattr -d com.apple.quarantine zotio), on Unix chmod +x zotio.
From source:
Then let the CLI walk you through setup — Zotero detection, the local-API toggle, an optional Web API key, first sync, and a health check:
A focused skill — bundled in this repo as SKILL.md — that teaches a coding agent to drive the CLI directly (the most efficient path; no MCP server in the middle).
Recommended — the skills CLI (works across Claude Code, Cursor, Codex, Cline, opencode, and 40+ agents):
Manual:
SKILL.md into ~/.claude/skills/zotio/SKILL.md (or your project's .claude/skills/zotio/).https://raw.githubusercontent.com/OrgMentem/zotio/main/SKILL.md — or paste it into your agent's skill store.zotio-mcpzotio-mcp ships alongside the CLI — the Homebrew formula and every release archive include both binaries. Register it:
For Claude Desktop, every release ships per-platform MCPB bundles — download the .mcpb for your platform, double-click it, and Claude Desktop walks you through the install.
Install the zotio-mcp binary and add to ~/Library/Application Support/Claude/claude_desktop_config.json:
The ZOTERO_API_KEY is optional for read-only local-desktop use (the local API needs no key); set it to enable writes and reach group libraries.
Reads go to your Zotero desktop app at localhost:23119 — no API key required while Zotero is running. First enable the local API in Zotero: Settings → Advanced → "Allow other applications to communicate with Zotero."
Creating items and saving attachments also works keyless — those go through the same local desktop connector.
Editing writes (items update/delete/move, items enrich, tags mutations, vault push/pull/resolve, most of import apply) route to the Zotero Web API and need a key. Configure it once:
Generate a key at https://www.zotero.org/settings/keys. The first Web API write prints a one-time stderr notice naming the target. A key is also needed to read group libraries or to read while the desktop app is closed. Run zotio doctor to see a writes: line reporting whether write-back is available.
Once installed (above), invoke /zotio <query> in Claude Code. The skill drives the CLI directly — the most efficient path, no MCP server in the middle.
Once registered (above), the MCP server exposes a command-orchestration facade (command_search / command_run) rather than one tool per endpoint — agents discover and drive the CLI the same way a human would (see dev/adr/0001-mcp-command-surface.md; switch surfaces via ZOTIO_MCP_SURFACE). It also serves Zotero context as resources — zotero://context, zotero://agent-context, zotero://status, zotero://schema, zotero://freshness, zotero://health/{scope}, zotero://capabilities, and bounded graph resources (collections/{key}/tree, items/{key}/children|attachments|context) — plus guided prompts (prepare-library-health, prepare-import, sync-vault-safely, inspect-library, export-reading-notes, prepare-citation-export, synthesize).
Also available: --csv, --plain, --quiet, --compact, and --deliver stdout|file:<path>|webhook:<url>.
doctor: connection refused — open Zotero desktop and enable Settings → Advanced → "Allow other applications to communicate with Zotero."items missing-pdf / analytics return nothing — run zotio sync first to populate the local mirror.annotations export outputs empty sections — PDF annotations must be made in Zotero's built-in PDF reader, not an external app.citekey-conflicts finds no keys — install the Better BibTeX extension; citation keys live in the extra field.zotio doctor to check credentials; verify echo $ZOTERO_API_KEY.Config file: ~/.config/zotio/config.toml. Static request headers can be set under [headers]; per-command overrides take precedence.
| Variable | Required | Description |
|---|---|---|
ZOTERO_API_KEY | No for reads | Required for writes (routed to the Zotero Web API), group libraries, and access while the desktop app is closed. Local desktop reads need no key. Configure once by piping the token into zotio auth set-token --stdin. |
Run zotio --help for the full command list, or zotio <command> --help for any subcommand. Ask the CLI directly when you know the goal but not the command:
agent-context · analytics · annotations · attachments · auth · capabilities · collections · completion · creators · demo · doctor · export · feedback · groups · import · init · items · journal · library · profile · reading-list · schema · search · searches · sync · tags · tail · vault · version · watch · which · workflow
zotio manages and safeguards your Zotero library. papio is the acquisition side: it finds and downloads validated, provenance-tracked PDFs from open access and your own institutional subscriptions through your normal browser, then hands them to zotio for preview-first import. If your library has items without PDFs, papio fills them.
Built by studying these projects and resources:
Licensed under MIT.
Zotero is a registered trademark of the Corporation for Digital Scholarship. zotio is an independent project and is not affiliated with or endorsed by Zotero or the Corporation for Digital Scholarship.