From mjlab
Use the Zotero local HTTP API (and Zotero Web API where needed) to search the user's reference library, add references by DOI, export BibTeX or CSL-formatted bibliographies, and manage collections and tags from within an analysis or manuscript repository. TRIGGER when the user mentions Zotero, citations, references, BibTeX, `.bib` files, DOIs in the context of citing a paper, or a manuscript draft asking for "the right paper for this claim."
How this skill is triggered — by the user, by Claude, or both
Slash command
/mjlab:zoteroThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Zotero 7+ ships a **local HTTP API** at `http://localhost:23119/api/`. When Zotero is running on the user's machine, the agent can search the library, fetch metadata, and export formatted bibliographies with no install, no auth, and no MCP server. This skill is the agent's playbook for using it.
Zotero 7+ ships a local HTTP API at http://localhost:23119/api/. When Zotero is running on the user's machine, the agent can search the library, fetch metadata, and export formatted bibliographies with no install, no auth, and no MCP server. This skill is the agent's playbook for using it.
/api/) before issuing read or write requests. If you get 403 "Local API is not enabled," tell the user the exact setting to flip rather than guessing or retrying.bridges2024ccc require the Better BibTeX plugin. If the user wants citekey-driven manuscripts and BBT isn't installed, recommend installing it once.Probe:
curl -sS -o /dev/null -w '%{http_code}\n' http://localhost:23119/api/
users/0 shortcutThe local API has the same URL grammar as the Web API. Locally, users/0 means "the current local user library" — the agent does not need to know the real numeric userID. Group libraries use groups/<id>/….
Base for all read examples below: http://localhost:23119/api/users/0
Title + creator + year search (the default):
curl -sS 'http://localhost:23119/api/users/0/items?q=miller-jensen&limit=10'
Everything-search (includes indexed full-text of PDFs):
curl -sS 'http://localhost:23119/api/users/0/items?q=NF-kB+bursting&qmode=everything&limit=20'
Filter by type and tag:
# only journal articles tagged 'macrophage' OR 'TAM', NOT preprints
curl -sS 'http://localhost:23119/api/users/0/items?itemType=journalArticle&tag=macrophage%20%7C%7C%20TAM&tag=-preprint'
Tag boolean syntax is non-obvious:
&tag=A&tag=B → A AND B&tag=A%20%7C%7C%20B (A || B URL-encoded) → A OR B&tag=-A → NOT ASort and paginate:
curl -sS 'http://localhost:23119/api/users/0/items?sort=dateAdded&direction=desc&limit=25&start=0'
Sort keys: dateAdded, dateModified, title, creator, itemType, date, publicationTitle. Default limit is 25, max 100.
Pass include= to receive multiple representations in a single response:
curl -sS 'http://localhost:23119/api/users/0/items/MHCQTYP2?include=data,bib,citation&style=cell'
This returns the item's raw data plus a bib field (Cell-style HTML bibliography) and a citation field (inline citation).
The format query parameter switches the response type.
BibTeX for a whole collection:
curl -sS 'http://localhost:23119/api/users/0/collections/ABCD1234/items?format=bibtex&limit=100' -o refs.bib
BibLaTeX (preferred over BibTeX for modern manuscripts):
curl -sS '…/items?format=biblatex' -o refs.bib
CSL-formatted bibliography for a specific set of items, in a specific journal's style:
curl -sS 'http://localhost:23119/api/users/0/items?itemKey=KEY1,KEY2,KEY3&format=bib&style=cell&locale=en-US'
Common lab journal styles (use the exact CSL slug):
cell, nature, science, the-lancetthe-journal-of-immunology, nature-immunology, elifechicago-author-date for fallback / non-journal docsA list of all available styles: https://www.zotero.org/styles-files/styles.json. If the user names a journal not in styles.json, search there before failing.
Other useful formats: csljson (machine-readable bibliography for downstream tools), ris, csv.
# top-level collections
curl -sS 'http://localhost:23119/api/users/0/collections/top'
# sub-collections of a collection
curl -sS 'http://localhost:23119/api/users/0/collections/ABCD1234/collections'
# items in a collection
curl -sS 'http://localhost:23119/api/users/0/collections/ABCD1234/items?limit=100'
# all tags (with counts via numItems)
curl -sS 'http://localhost:23119/api/users/0/tags?limit=200'
Local API is currently read-only. Three paths to add items, in order of agent preference:
1. Connector endpoint (against the running desktop app, no auth). Best for adding a single item from a URL or DOI. Goes into the user's library immediately and is visible in Zotero's UI.
# uses Zotero's built-in translators
curl -sS -X POST 'http://localhost:23119/connector/saveItems' \
-H 'Content-Type: application/json' \
-d '{"items":[{"itemType":"journalArticle","title":"…","DOI":"10.1038/s41586-024-…","creators":[{"creatorType":"author","firstName":"Jane","lastName":"Doe"}],"date":"2024"}]}'
2. Zotero Web API. Required for batch writes or scripted manuscript pipelines. Needs an API key and the numeric userID.
export ZOTERO_API_KEY=zXXXXXXXXXX # from https://www.zotero.org/settings/keys
export ZOTERO_USER_ID=1234567
curl -sS -X POST "https://api.zotero.org/users/${ZOTERO_USER_ID}/items" \
-H "Zotero-API-Key: $ZOTERO_API_KEY" \
-H "Content-Type: application/json" \
-H "Zotero-Write-Token: $(openssl rand -hex 16)" \
-d '[{"itemType":"journalArticle","title":"…","creators":[{"creatorType":"author","firstName":"Jane","lastName":"Doe"}],"DOI":"10.1000/xyz","date":"2024"}]'
Store secrets in a gitignored .env, never in committed code. See using-git-and-github.
3. Translation-server (optional, separate Node service on :1969). The only path that resolves a bare DOI/PMID/arXiv ID into full Zotero JSON without going through Zotero's own UI. Most labs don't run it; mention as an option, don't assume.
If the user says "add 10.1038/s41586-024-12345 to my library":
/connector/saveItems with itemType: 'journalArticle' and minimal fields; Zotero's translators fill in the rest from Crossref.collections array via the Web API.The most common items in a biology lab repo:
title, creators[] (each {creatorType, firstName, lastName}), publicationTitle, volume, issue, pages, date, DOI, ISSN, URL, abstractNotetitle, creators[], repository (e.g. "bioRxiv"), archiveID (e.g. "2024.01.15.575912"), date, DOI, URLpublisher, place, ISBN, numPagesbookTitle, bookAuthor, pagesthesisType, university, place, dateGet a fresh template for any type: GET /api/items/new?itemType=preprint.
If installed, BBT exposes JSON-RPC at http://localhost:23119/better-bibtex/json-rpc:
# get the stable citekey for an item
curl -sS -X POST http://localhost:23119/better-bibtex/json-rpc \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"item.citationkey","params":[["MHCQTYP2"]]}'
Probe: POST with method: "api.ready". A "No endpoint found" response means BBT is not installed — fall back to core Zotero output (which still includes a BibTeX-flavored id field on ?format=bibtex exports, just not BBT's stable format).
When the agent is editing a manuscript in a lab repo:
@bridges2024ccc) if BBT is installed, otherwise by Zotero itemKey (which is stable per-library but not human-readable)..bib at manuscript/refs.bib. Regenerate on demand:
curl -sS 'http://localhost:23119/api/users/0/collections/ABCD1234/items?format=biblatex&limit=100' \
> manuscript/refs.bib
refs.bib in git. The user's Zotero library is the source of truth; the .bib is a snapshot.\cite{…} in the manuscript resolves to an entry in refs.bib.format=bib or format=bibtex returns nothing → the queried items are attachments or notes, not reference items. Filter with /items/top or an explicit itemKey= list of parent items.|| in tag queries must be URL-encoded.Last-Modified-Version conflicts on writes: include If-Unmodified-Since-Version: <n> to avoid clobbering changes the user made in the UI.Backoff: <s> and 429 Retry-After: <s> headers. The local API has no rate limit but be polite./api/users/0/… only sees the user's personal library. For a lab group library that's been synced to the desktop, use /api/groups/<groupID>/….localhost:23119/api/ for 200 before any requestusers/0 (not a hard-coded numeric ID) for local reads.env, never to a committed filemanuscript/refs.bib after adding new references and committed the resultpyzotero — Python client if shell curl gets unwieldy54yyyu/zotero-mcp — install if the user prefers MCP tools over shell callsnpx claudepluginhub miller-jensen-lab/claude-code-marketplace --plugin mjlabCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.