The goal of this skill is not to showcase the command surface, but to reliably land the user's natural-language Zotero tasks on the correct runtime path and bring back genuinely useful results.
First, grasp these principles
- As long as the task's true goal is to operate onan existing local Zotero libraryoran existing/to-be-created reading workspace, use this skill, even if the user did not say
zot。 - In Claude Code or Codex, users should directly state their needs, not first memorize commands. The skill is responsible for translating needs into runtime actions.
zotis the only execution surface.ref/The old Python reference implementation inzot mcp serveis not the current main path.- Both local SQLite and Zotero Local HTTP are read-only paths; the connector is only responsible for adding BibTeX/RIS items to the target currently selected in the Zotero UI; all other mutations go through the Zotero Web API. The three boundaries must not be mixed.
- Each write path owns the success and failure of this operation; when the connector or Web API errors out, it must not automatically fall back to the other path.
- Answers should first give the conclusion, evidence, changes, or reason for failure. Do not dump raw JSON to the user first.
First bucket by intent
1. Query items
Users usually say:
- "Find papers related to reward hacking in my library"
- "Find that paper by Smith2024"
- "Show me the 10 most recently added references"
- "See what items are in this collection"
- "List the feeds in the current library"
- "Page through to items 100-150"
- "Which collection names contain transformer"
- "Give me the whole library's item count and distribution by type"
Preferred routing:
- Ordinary in-library search:
library search- non-empty search's
meta.fulltext_indexbeinglegacy-tables/fts5-sidecar/unavailable。unavailableindicates that attachment full text was not searched this time; title/author/tags may still match. Do not treatno such table: fulltextItemWordsas SQLite being unreadable.
- non-empty search's
- Pure listing / paging:
library list --collection ... --limit ... --offset ... - Whole-library statistics:
library stats - Citation key direct access:
library citekey - Recently added items:
library recent --count - Collection fine-grained reading:
collection get/subcollections/items/item-count/tags - Search within collection names:
collection search - Library-level organizational information:
library tags/libraries/feeds/feed-items
2. Get evidence
Users usually say:
- "Pull out this reference's details, children, and citations"
- "Pull out all the PDF annotations, outline, and notes"
- "Download the attachments locally"
- "Search reward shaping in all my notes"
- "See what tags this one currently has"
- "Open its DOI in the browser"
- "Open this one's attachment in the local PDF reader"
Preferred routing:
- Single-item main entry:
item get/related/children/cite/export - PDF evidence:
item pdf/fulltext/outline - annotation:
item annotation list/search - Note keyword search:
item note search - Tag read-only listing:
item tag list - Open local resources:
item open(attachment) /item open --url(DOI or URL) - Attachment download:
item download <attachment-key>
3. Create a workspace
Users typically say:
- "Create an llm-safety workspace for me"
- "Organize mechanistic interpretability related papers into a long-term workspace"
- "Later I want to do question-answering retrieval within this topic"
- "See what entries are currently in the llm-safety workspace"
- "Export this workspace as BibTeX / markdown for me to paste into an email"
Preferred routing:
- Create and maintain:
workspace new/add/import/remove/delete - View members:
workspace list/workspace show <name> --limit N - Index and query:
workspace index/search/query- Starting from 0.5.0, incremental reindexing is the default (only embed newly added items). When a full rebuild is needed, add
--force-rebuild; to skip full-text PDFs, add--no-fulltext。
- Starting from 0.5.0, incremental reindexing is the default (only embed newly added items). When a full rebuild is needed, add
- Export:
workspace export <name> --format markdown|json|bibtex
Note:
- The workspace name must be kebab-case, for example
llm-safety workspace searchis keyword searchworkspace queryis question-answering retrievalworkspace export --format bibtexwill call the local BibTeX export item by item; entries without BibTeX data will be skipped
4. Save queries
Users typically say:
- "Save this filter condition as a Zotero saved search"
- "List what saved queries I currently have"
- "Delete this expired saved search"
Preferred routing:
library saved-search listlibrary saved-search createlibrary saved-search delete
Boundaries:
- The Zotero Web API currently only provides the metadata and conditions of a saved search, and does not directly return search results
- It should be explained that what is saved is the "query conditions", not a "snapshot of a dynamic result set"
5. Download attachments
Users typically say:
- "Download the attachment ATCH005"
- "Pull the PDF of this entry to the current directory"
Preferred routing:
- Known attachment key:
item download - When only the parent item is known: first
item children, then determine the attachment key
Things not to do:
- Do not disguise attachment download as
item attach - Do not mix upload and download into one action
6. Import references into Zotero
Users typically say:
- "Import this bib into Zotero"
- "Save these RIS entries into my current collection"
The only write route is item import:
- File input:
item import --file <path>; raw text input:item import --text <text> - Only explicitly add when automatic detection is unreliable
--format bibtex|ris - Without
--confirmfirst dry-run, read and repeat back the target collection/library currently selected in the Zotero UI, target writability, record count, and format - Only after the user confirms the above target and record count, execute with the same input
item import ... --confirm
Boundaries:
- The connector only adds BibTeX/RIS entries; it does not support update, tag, note, collection mutation, or merge/dedupe
- The target is determined by the current selection in the Zotero UI; the collection cannot be specified in the command
- Stop when Zotero is not running, the connector is unreachable, or the target is read-only; do not switch to the Web API
7. Safe writing
Users typically say:
- "Add a note to this reference"
- "Tag it with priority"
- "Add priority to all entries with the reading-list tag"
- "Add this entry to a collection"
- "Preview first, then merge these two references"
- "Merge duplicate entries"
- "Write the formal publication information of the preprint back"
First distinguish between capabilities and backends:
| Path | Current capability | Credentials / operating conditions |
|---|---|---|
| local SQLite / Local HTTP | Read-only query, extraction, and dedupe planning | Local Zotero data; cannot write |
| connector local import | Only item import add BibTeX/RIS entries |
Zotero is running, connector is reachable, the current UI target is writable |
| Zotero Web API | All mutations except connector import, including merge/dedupe | ZOT_LIBRARY_ID + ZOT_API_KEY or the corresponding profile configuration |
Write decision order:
- First run
zot --json doctor, readcapabilities.connector_write、capabilities.web_writeandwrite_credentials。 - BibTeX/RIS add imports go only through the connector: first preview the target, writability, record count, and format without
--confirm, repeat them back, obtain confirmation, then executeitem import ... --confirm。 - All other mutations go through the Web API. When
ZOT_LIBRARY_IDorZOT_API_KEYis missing, stop and name the missing credential; do not try the connector. - For merge/dedupe, first run the local preview without
--confirm, repeat back the keeper, sources, confidence, and skipped items; before executing--confirm, check the Web credentials again.
Command routing:
- connector writes: only
item import。 - Web API writes:
item create/add-doi/add-url/add-file/update/trash/restore/attach、item note add/update/delete、item tag add/remove/batch、collection mutation、saved-search mutation、annotation creation、sync update-status --applyand merge/dedupe. library dedupe --confirmBy default, only the normal-confidence group is executed; the low-confidence group remains inskipped_low_confidence. An ordinary confirm is not equal to low-confidence authorization; only if the user has seen the preview and separately, explicitly accepts this part of the risk, may--include-low-confidence。- Do not claim that the connector can perform tag, note, collection, attachment, annotation, saved-search, status-sync, or merge/dedupe mutations.
8. Configuration troubleshooting
Users usually say:
- "Why can't this environment write to Zotero"
- "First help me check whether the configuration is correct"
- "Switch the current profile to work"
- "Initialize a new config profile"
Preferred routing:
- Diagnosis:
doctor - Configuration:
config show/init/set/profiles list/profiles use
The connector does not need an API key or plugin configuration.doctor When connector_write shows as unavailable, first check whether Zotero is running and whether the local connector is reachable; do not expand the connector into a general-purpose write backend by modifying the profile.
9. Retractions and citation quality (Scite)
Users usually say:
- "Has this paper been retracted"
- "Scan my whole library for retraction notices"
- "Check whether there are retractions or errata in entries related to the attention topic"
Preferred routing:
- Single item:
item scite report --item-key Koritem scite report --doi 10.x/y - Multiple items in the library:
item scite search <query>(batch-query Scite by the DOIs of existing entries in the library) - Whole-library retraction scan:
item scite retractions [--collection K] [--tag T] --limit N
Note:
- Scite reports depend on the external Scite service; when the external network is unavailable, say so directly and do not fabricate results
item scite reportMust provide--item-keyor--doi, one of the two
10. Sync and incremental updates
Users usually say:
- "Which entries in my library have changed since version N"
- "What is currently in the trash"
- "Is there any formal publication information for the preprint that needs updating"
Priority routing:
- Remote version delta:
item versions --since <number> - Trash enumeration:
item deleted --limit N - preprint status sync (dry-run vs
--apply):sync update-status [<key>] [--collection K] [--limit N] [--apply]
Note:
- All three commands depend on Zotero Web API credentials; first
doctor sync update-statusdefaults to dry-run; only when--applyis added does it actually write fields back to Zoterosync update-statuscurrently only covers preprint publication status; do not treat it as an attachment indexer
Invocation order
- If the system already has
zot, preferzot --json ... - Only in a development repository environment and when
zotis not inPATH, fall back to:
cargo run -q -p zot-cli -- ...
- For the same round of tasks, keep using the same invocation method; do not switch back and forth.
- In agent mode, use
--jsonby default to get the envelope; text mode is only for humans; do not mix them in the same session.
Diagnostic gate
In the following scenarios, run doctor:
- First contact with this environment
- Any write operation
- PDF / outline / annotation / attachment related tasks
- semantic index / semantic search / workspace query
- citation key lookup
- saved search / configuration troubleshooting / profile switching
- The user says "why isn't it working"
Preferred:
zot --json doctor
Development environment fallback:
cargo run -q -p zot-cli -- --json doctor
Focus on these fields:
db_existscapabilities.local_sqlite_readcapabilities.local_sqlite_read.fulltext.legacy_tablescapabilities.local_sqlite_read.fulltext.sidecar_presentcapabilities.local_http_readcapabilities.connector_write(only indicates the local BibTeX/RIS import capability)capabilities.web_write.configured(only indicates that credentials exist)capabilities.web_write.verified(currentlyfalse; doctor does not verify key/scope/permission over the network)write_credentials.configured(only indicates Web API credential, not connector import capability)pdf_backend.availablebetter_bibtex.availablelibraries.feeds_availablesemantic_indexannotation_supportembedding.configuredconfig_file
Hard constraints
--libraryonly acceptsuserorgroup:<id>--jsonis a global flag and must be placed before the subcommand (e.g.zot --json item get K); it cannot be written aszot item --json get K- workspace names must be kebab-case
- The workspace file is parsed by
AppConfig::state_dir().join("workspaces"); do not write~/.config/zot/workspacesas a path for all platforms. Linux/XDG example:~/.config/zot/workspaces/<name>.toml;macOS:~/Library/Application Support/zot/workspaces/<name>.toml;Windows:%AppData%\zot\workspaces\<name>.toml. At runtime, usedoctor/config show(doctor.data.config_file). Index sidecar file<name>.idx.sqlite, PDF cache sidecar file.md_cache.sqlite zot mcp serveis currently unavailableitem add-filedoes not support--attach-modeitem annotation create/create-areaonly applies to PDF attachments, and the attachment'scontent_typemust beapplication/pdf, otherwiseattachment-not-pdfitem annotation createsupports--occurrence N(since 0.5.0, default 1), used to select the Nth occurrence among the same text appearing multiple times on the same page. The returned JSON containsoccurrence/total_matches/more_occurrences, which can be used for chained calls.
library saved-searchhandles the conditions of a saved search, not the result itemslibrary saved-search create --conditionsmust be a JSON array, each item in the form{"condition": "...", "operator": "...", "value": "..."}, at least one entry; an empty array directly reportssaved-search-conditions- Pdfium path override:
ZOT_PDFIUM_LIB_PATH/PDFIUM_LIB_PATHpoints to the lib,ZOT_PDFIUM_CACHE_DIRoverrides the automatic download cache directory - Never directly modify
zotero.sqlite - The Zotero Local HTTP API is read-only; the only local write path is the connector's
item import - The connector has no authentication but only listens on loopback, and only supports adding BibTeX/RIS entries to the target currently selected in the Zotero UI; it cannot be used for merge/dedupe or other mutations
- API keys and raw merge plan tokens must not enter output, logs, fixtures, documentation examples, or eval
Safety gate
By default, treat as actions with side effects:
item createitem add-doiitem add-urlitem add-fileitem import --confirmitem updateitem trashitem restoreitem attachitem merge --confirmitem note additem note updateitem note deleteitem tag additem tag removeitem tag batchitem annotation createitem annotation create-areacollection createcollection renamecollection deletecollection add-itemcollection remove-itemlibrary saved-search createlibrary saved-search deletelibrary duplicates-merge --confirmlibrary dedupe --confirmsync update-status --applyconfig initconfig setconfig profiles use
Execution rules:
-
When the user is only "taking a look," "analyzing," or "evaluating," do not secretly write to the library.
-
Ordinary, single-item, reversible write operations may be executed after the user explicitly requests them.
-
High-risk actions are divided into three tiers: first summarize the upcoming changes, then confirm, then execute:
Tier A, reversible soft delete (still requires confirmation, but can be recovered in steady state):
item trash/item restoreitem note delete(actually moves the note to trash; the wordingNote moved to trash)
Tier B, high-risk deletion, merging, or state writes:
collection deletelibrary saved-search deletelibrary duplicates-merge --confirmlibrary dedupe --confirmitem merge --confirmsync update-status --apply
Tier C, batch writes (affecting a group of entries; must first be tried on a small scale, then expanded):
item import --confirm(first dry-run to restate target writability, record count, and format)item tag batch --add-tag/--remove-tag(without--confirmonly does a local preview; after verifyingmatched、affected、truncated、sample_keysandexceeds_max_affected, use exactly the same filters/mutations plus--confirm; when exceeding the default 50 entries, you must explicitly increase it after review--max-affected)library duplicates-merge(multiple sources → single keeper)library dedupe --confirm(batch merging of multiple groups across the entire library/entire collection; first use--collectionto scope a small range, and review low-confidence groups)
-
item merge/item tag batch/library duplicates-merge/library dedupe/sync update-statuswithout--confirm/--applyis itself a dry-run preview; treat the preview as "not yet changed," and do not mistakenly say "already merged / already written back." -
The connector import preview and confirm must keep the same input and format, and re-check the current target's writability before confirm; connector failures must not be switched to Web.
-
The merge/dedupe preview is local read-only planning, and confirm only goes through the Web API; when Web credentials are missing, keep the preview results and stop, and do not switch to the connector.
-
library dedupelow-confidence groups are skipped by default. Do not treat an ordinary confirm as authorization, and do not append--include-low-confidenceon your own; you must first display these groups separately, then obtain one explicit risk authorization. -
When write permission is missing or the target path does not support the mutation, stop at read-only analysis; do not pretend success.
-
item tag batch --confirmreturnsstate: applied|partial|failedand per-operation results. As long asfailed_operations > 0, the failed items must be clearly reported; do not call everything successful just because the command returned an envelope.
Common semantic differences
workspace searchis keyword search,workspace queryis question-answering retrievallibrary recent --count 10is the most recent N items,library recent 2026-04-01 --limit 20filters by time boundarylibrary semantic-searchis library-level semantic search, not equivalent to workspace queryitem add-doi/item add-url/item create --doi|--url|--pdfsupports--attach-modeitem add-filecan include--doito supplement metadata, but does not accept--attach-mode- feeds are not accessed through
--library group:<id>, but instead uselibrary feeds/feed-items item downloaddownloads local attachment files, and by default does not overwrite existing targets; only after reviewing existing files add--force。item attachuploads new attachmentsitem mergeis general merging with manual selection of keeper/source,library duplicates-mergefirst finds duplicates, then merges by keeperlibrary dedupeis batch cleanup with automatic keeper selection across the entire library/entire collection,library duplicates-mergeis the merge of a single manually specified keeperconfig showis to view the effective configuration,config profiles useis to switch the default profile
Typical mappings from natural language to actions
-
"Find papers related to reward hacking in my library, then pick the most relevant one for me to cite"
Firstlibrary search, thenitem get/item cite -
"Show me the 10 most recent items that just entered the library"
Uselibrary recent --count 10 -
"Pull out the PDF annotations and notes for this paper"
Firstdoctor, thenitem get/item children/item annotation list -
"Create an llm-safety workspace for me; I'll do Q&A with it later"
Firstworkspace new/import, thenindex/query -
"Export the llm-safety workspace as BibTeX for me to paste into an email"
Firstworkspace show llm-safety, thenworkspace export llm-safety --format bibtex -
"Save this filter condition as a saved query"
Uselibrary saved-search create -
"Download attachment ATCH005"
Useitem download; when the target already exists, report the conflict first; do not append on your own--force -
"Open ATTN001's DOI in the browser and take a look"
Useitem open ATTN001 --url -
"Import this bib into Zotero" First
item import --file <path>dry-run, restate the current target, writability, record count, and format; only after the user confirms, execute the same command and append--confirm. The confirmation branch will read the target again; if it changes, abort and require a new preview -
"Save these RIS entries into my current collection" First
item import --text <ris> --format risdry-run; only after confirming that the collection currently selected in the Zotero UI is correct and writable, append--confirm; if the target changes, preview again -
"Insert the item with citation key vaswani_attention_2023 into the draft, and maintain references.bib" First
library citekey vaswani_attention_2023resolve the Zotero item key (e.g.PXW99EKT), then useitem cite PXW99EKT --style apato get the display citation,item export PXW99EKT --format bibtexto get the BibTeX; the agent uses the exported BibTeX entry to updatereferences.bib, and inserts the BibTeX citation key into the draft. The CLI does not directly edit the draft or the.bibfile -
"Preview first, then merge KEEP001 and DUPE001; after confirmation, actually merge"
Firstitem merge KEEP001 DUPE001do a local preview; after restating the result and confirming, check the Web credentials, then executeitem merge ... --confirm -
"I don't have an API key, and I want to clean up duplicate items locally" You can run
--confirmwithoutlibrary dedupeto generate a local read-only plan, but you must clearly state that actual merging no longer supports credential-free local execution;--confirmrequiresZOT_LIBRARY_ID+ZOT_API_KEY, and the connector cannot merge -
"This time explicitly merge via the Zotero Web API" First generate a merge preview, then confirm
capabilities.web_writeand the Web credentials; after the user confirms, execute--confirm; if Web fails, do not fall back to the connector -
"Help me directly UPDATE zotero.sqlite" Refuse direct SQLite writes; explain that local SQLite / Local HTTP are read-only. New BibTeX/RIS imports can go through the connector; other mutations can only be redirected to implemented Web commands
-
"Check the Scite report for each of these attention-related items in my library one by one"
Useitem scite search "attention" -
"Check whether KG326EEI has been modified recently"
Goitem versions --since <last-version> -
"Delete this saved search Recent RL"
Golibrary saved-search delete <key> -
"Why can't I write to Zotero in my current environment"
Firstdoctor, if necessaryconfig show
How to understand this when migrating from ref\zotero-cli
search->library searchget->item getopen->item openopen --url->item open --urlannotations->item annotation listoritem pdf --annotationsnotes->item note listnotes search->item note searchcollections->collection listcollection <id>->collection items <id>add doi/add url->item add-doi/item add-urltags->library tagsstats->library statsrecent [n]->library recent --count <n>merge->item merge; if finding duplicates first and then merging, golibrary duplicates/duplicates-merge
Old mental models not to migrate back:
- Do not add flat top-level alias
- Do not add
--api-base - Do not add compact JSON default output
- Do not turn connector-style
search/fetchback into another set of main commands
How to understand this when migrating from ref\zotagent
Remember two things first:
- Current
zotdoes not copyzotagent's flat command surface - Current
synconly does preprint publication status; same name but not an attachment indexer
Parts already covered or replaceable:
- DOI / URL / file import:
item add-doi/item add-url/item add-file/item create - Library-level semantic search:
library semantic-index/library semantic-search - Single PDF extraction:
item pdf/item fulltext/item outline - citation key entry: first
library citekey, then go toitem get/item cite - Retraction coverage:
item scite retractions/item scite report/item scite search(zotagent usually does not have this built in; this is extra capability in zot, not a migration) - Trash enumeration and version increments:
item deleted/item versions --since(replaces zotagent-style polling scripts)
Currently not filled in, cannot pretend it exists:
s2add --s2-paper-idsearch-inmetadatareadexpand- zotagent-style
status - zotagent-style
syncfull attachment index - By
title/author/year/publicationone-time manual item creation
How to handle these requests:
search-in/expand: clearly say there is currently no equivalent command; you can only firstitem fulltext/item pdfpull text, then do agent-side secondary locatingmetadata: explain that there is currently no field-scoped metadata search; at most fall back tolibrary searchplus existing filtersstatus: usedoctor+library semantic-statuscombined to answer; do not describesync update-statusas index statuss2/--s2-paper-id: directly state that it is currently not implemented; do not fake a replacement command- zotagent
sync: explain that currently onlylibrary semantic-index --fulltextorworkspace indexcan partially replace it, and the scope is mainly metadata + PDF
Fallback on failure
- No
zot: in the development repository, fall back tocargo run -q -p zot-cli -- ... - Zotero is not running or the connector is unreachable: stop and ask the user to start Zotero, confirm the local connector is reachable, then retry the original import; do not switch to Web
- connector target is read-only: name the current target as not writable, ask the user to select a writable collection/library in the Zotero UI and then rerun dry-run; do not switch to Web
- Web write unavailable: clearly tell the user that
ZOT_API_KEY/ZOT_LIBRARY_IDis missing; connector cannot replace other mutations - Target path does not support this mutation: explain the capability boundary and stop; do not imply that connector can merge, dedupe, tag, or update
- No Better BibTeX:
library citekeyonly use Extra fallback - No Pdfium: do not promise fulltext / outline / annotation / text processing after PDF download
- No embedding: semantic search will be degraded; for workspace Q&A switch to
--mode bm25 attach-mode autoNo OA PDF found: the item may still be created successfully
Output contract
The final answer should:
- First answer the user's actual question, rather than pasting commands first
- Then give the key evidence, actions already performed, or the reason for failure
- Clearly distinguish the Zotero item key (such as
PXW99EKT, for use byitem get/cite/export) from the BibTeX citation key (such asvaswani_attention_2023, for use in LaTeX/Markdown citations); the two must not be conflated as the same key - If it fails, name the specific gate and the next step: Zotero not running, connector unreachable, target read-only, Web credentials missing, no matching item, or write not confirmed
- By default, do not dump raw JSON; read the envelope first, then relay the useful information
- Prefer to restate the key fields of
data/metain the envelope, treat raw JSON as secondary evidence, and paste it only when necessary
The goal of this skill is not to explain the CLI more completely, but to let agents such as Claude Code and Codex reliably complete Zotero workflows using natural language.