Notion Describe

Return the JSON Schema and a working example for one operation, plus which tool runs it (notion_read or notion_write). Use this BEFORE calling the operation when the payload shape is non-trivial (query filters, structured block trees, database property definitions). For simple ops, just call it — errors carry the schema.

Operation name to describe, as listed by notion_read / notion_write.

Notion Describe

Notion Read

Run one Notion read operation by name. Nothing is modified.

Call: { operation, payload } — payload carries that operation's fields. Common: search_pages { query }, get_page { page_id }, get_page_markdown { page_id }, query_database { database_id, where? }, get_block_children { block_id }.

Responses are slimmed; pass verbose:true in payload for the raw Notion object. Every id field (page_id, block_id, database_id, view_id, …) also accepts a Notion URL, as copied from Share → Copy link. A block link's #fragment is used for block_id fields and a database link's ?v= for view_id fields.

If the payload is malformed, the error response includes the schema + a working example so you can correct and retry in one round-trip. Call notion_describe(operation) ahead of time only for complex shapes (query_database filters).

The read operation to run. This list is the complete menu of read operations enabled on this server; notion_describe(operation) returns any operation's full schema.

Operation parameters. Pass either single-op fields directly, or { items: [...], atomic?, idempotency_key?, concurrency? } for batch.

Notion Read

Notion Write

Run one Notion write operation by name. Archive, trash and delete operations remove content — confirm with the user before running them.

Two ways to call:
  • Single: { operation: "set_page_title", payload: { page_id, title } }
  • Batch:  { operation: "set_page_title", payload: { items: [{page_id, title}, ...], atomic?: false, idempotency_key?: "...", concurrency?: 3 } }
create_page, append_blocks, update_block and update_page_markdown also take a markdown string.

Responses are slimmed; pass verbose:true inside payload (single) or per item (batch) for the raw Notion object. Every id field (page_id, block_id, database_id, view_id, …) also accepts a Notion URL, as copied from Share → Copy link.

If the payload is malformed, the error response includes the schema + a working example so you can correct and retry in one round-trip. Call notion_describe(operation) ahead of time only for complex shapes (block trees, database property definitions, batch_mixed_blocks).

The write operation to run. This list is the complete menu of write operations enabled on this server; notion_describe(operation) returns any operation's full schema.

Notion Write


Discover Zotero tools

Discover the available Zotero tools by keyword — useful for progressive disclosure when you do not want to load every tool definition up front (the code-execution-with-MCP pattern). Pass an optional `query` (matched against tool names, titles, and descriptions) and `detail` ("names" or "descriptions", default "descriptions"). Returns the matching `zotero_*` tools so you can pick the right one for a task. With no query, returns the full catalog.

How much to return (default "descriptions").

Keyword to match against tool names/titles/descriptions.

Discover Zotero tools

Annotate a PDF (highlights, notes)

Add or delete Zotero PDF annotations (highlights, underlines, notes), the same objects you create in the Zotero PDF reader. `action:"add"` needs `parent` (a regular item key OR a PDF attachment key) and `annotations`: each with `type` (highlight|note|underline, default highlight), `text` (the exact passage to highlight), optional `comment`, `color`, `page` (0-based page index). **You do not need page coordinates**: give the passage in `text` and it is located in the PDF and anchored to the exact lines it occupies, so quoting a passage is enough to highlight it. Pass `page` to disambiguate a passage that repeats, or `occurrence` to pick among repeats; pass `position` ({"pageIndex":N,"rects":[[x1,y1,x2,y2],...]} in PDF points, bottom-left origin) only to place a highlight yourself. `action:"delete"` trashes the annotations in `annotation_keys`. Writes go to the running Zotero desktop app for your personal library (via its connector protocol, or its local-API writes where available), otherwise to the cloud Web API.

Default "add".

Annotation keys to trash (action:"delete").

Annotations to add.

Reading-order character offset of the passage start on the page (refines sort_index).

Hex color, e.g. "#ffd400" (highlight default) or "#26a69a".

Comment attached to the annotation (markdown-ish plain text).

Which occurrence of `text` to anchor when the passage appears more than once (1-based, in reading order). Only needed when a first attempt reports an ambiguous passage.

0-based PDF page index (annotationPageLabel uses page+1 when unset).

Page height in points (refines sort_index, e.g. 841.89 for A4).

Explicit page label (overrides page+1).

Zotero position: {"pageIndex": N, "rects": [[x1,y1,x2,y2],...]} (points, bottom-left origin), its JSON string, or shorthand [N, [x1,y1,x2,y2]]. Optional: when omitted, the passage in `text` is located in the PDF and its coordinates are computed for you.

Explicit annotationSortIndex; computed from position when omitted.

Tags to attach to the annotation.

The highlighted/underlined passage itself (required for highlight/underline). For notes, goes in annotationText of an extracted-text style note or leave to `comment`.

Annotation type; default "highlight".

Item key or PDF attachment key to annotate.

Annotate a PDF (highlights, notes)

Attach a file (PDF, snapshot) to an item

Add a stored file attachment (e.g. a PDF full text) under an existing item. Give `parent` (the item key) and either `url` (Zoteus downloads it, then stores it) or `path` (a file on the machine running Zoteus). `filename` and `content_type` are inferred when omitted. Saves through the Zotero desktop app when one is reachable (Zotero 10+ local API; you may be asked once to allow Zoteus write access, choose "Always Allow"), and otherwise through the cloud Web API, which needs ZOTERO_API_KEY with file access and uses your Zotero file-storage quota. `url` works on every setup including a remote/hosted Zoteus that cannot see your desktop, so prefer it over `path` unless the file really is on the server. Returns the new attachment key.

MIME type; inferred from the extension if omitted (pdf -> application/pdf).

File name to store; inferred from path/url if omitted.

Group library to attach in; forces the cloud path.

Key of the parent item to attach the file to.

Filesystem path to the file, on the machine running Zoteus.

Attachment title, e.g. "Full Text PDF".

URL to download the file from; works on remote/hosted servers.

Zotero attachments (files)

Upload, download, or inspect attachment files. `action`: "upload" stores a file as a Zotero attachment using the full File Storage protocol (provide `url` to have Zoteus fetch it, or `file_path` for a file on the machine running Zoteus; optional `parent_item` to attach it under an item, `title`, `content_type`) and returns the new attachment key; "download" fetches an attachment's file to a local path (provide `item_key`; optional `save_path`, default under the Zoteus data dir) and returns the path and byte count; "info" returns an attachment item's metadata. File bytes are written to / read from disk, never streamed through the conversation. Upload/download use the cloud Web API and your file-storage quota. When Zoteus runs on a different machine than Zotero, `file_path` refers to the server's disk, so use `url` instead.

File to upload, on the machine running Zoteus.

Attachment item key (download/info).

Parent item key to attach under (upload).

Where to write the downloaded file.

URL to download and upload instead of `file_path`; works on remote/hosted servers.

Zotero attachments (files)

Server-rendered bibliography (library items)

Produce a formatted bibliography for items already in a Zotero library, rendered server-side by Zotero in a CSL style. Provide `item_keys` and optionally `style` (name or CSL id; Zotero default is chicago-note-bibliography), `locale`, and `linkwrap`. Returns XHTML. Note: this endpoint is item-only and capped at 150 items. For arbitrary CSL-JSON or items not in the library, use zotero_format_bibliography instead.

Library item keys (max 150).

Wrap URLs/DOIs in links.

Locale (e.g. en-US).

Style name or CSL id.

Create or update Zotero items

Create new items or update existing ones in a single batch (the server auto-chunks into groups of 50). `items` is an ARRAY of item-data objects; each object has `itemType` as a **plain string** (e.g. "journalArticle", "book", "preprint", "report") plus its valid fields, `creators` (each `{creatorType, firstName, lastName}` or `{creatorType, name}`), `tags` (`[{tag}]`), and `collections` (array of 8-char collection keys). To UPDATE an existing item, also include its `key` and current `version`; to CREATE, omit both. Every item is validated against the Zotero schema before anything is sent — if any item is invalid, nothing is written and the problems are returned. Use zotero_schema to discover valid fields/creator types for an itemType. Writes go to the cloud Web API (requires ZOTERO_API_KEY).

Example:
```json
{"items": [{"itemType": "journalArticle", "title": "The Role of Metadata in Machine Learning", "creators": [{"creatorType": "author", "firstName": "Ada", "lastName": "Lovelace"}], "date": "2024-01-15", "DOI": "10.1234/example.5678", "tags": [{"tag": "ml"}], "collections": ["ABCD1234"]}]}
```

Array of Zotero item-data objects (itemType + fields; include key+version to update). Example: {"items":[{"itemType":"journalArticle","title":"The Role of Metadata in Machine Learning","creators":[{"creatorType":"author","firstName":"Ada","lastName":"Lovelace"}],"date":"2024-01-15","DOI":"10.1234/example.5678","tags":[{"tag":"ml"}],"collections":["ABCD1234"]}]}

Array of 8-character collection keys.

Array of creator objects: {creatorType, firstName, lastName} or {creatorType, name}.

The Zotero item type as a plain string, e.g. "journalArticle", "book", "preprint", "report", "thesis".

Object mapping a relation predicate to a URI or array of URIs.

Array of tag objects: [{"tag": "name"}].

Create or update Zotero items

Permanently delete Zotero items

PERMANENTLY and IRREVERSIBLY delete items by key (this purges them — it is NOT the trash). Prefer zotero_trash_items, which is reversible. This tool is disabled unless the server is started with ZOTEUS_ALLOW_DELETE=true, and additionally requires `confirm: true` on every call. For the personal library it goes through the running Zotero desktop app when that app supports local-API writes, otherwise the cloud Web API. The current library version is used as a precondition; the operation auto-chunks to 50 keys per request.

Must be true to proceed with permanent deletion.

Item keys to permanently delete.

Permanently delete Zotero items

Export Zotero items

Export items in a bibliographic format and return the raw text. Choose `format` (bibtex, biblatex, better-biblatex, ris, csljson, csv, mods, tei, coins, rdf_*, refer, wikipedia, bookmarks). `biblatex` is Zotero's STOCK translator via the cloud Web API; BBT-specific options (citation-key generation, sentence-case, biblatexExtendedNameFormat, unicode→LaTeX) are NOT available there. `better-biblatex` uses the local desktop Better BibTeX plugin (your configured BBT export options apply) and is only available when desktop Zotero + BBT are running; it degrades to built-in `biblatex` otherwise. Narrow with `item_keys`, `collection_key`, `q`, or `item_type`. A `limit` (default 50) is always applied. For styled human bibliographies use the bibliography tools.

bibtex biblatex better-biblatex ris csljson csv mods tei coins rdf_bibliontology rdf_dc rdf_zotero refer wikipedia bookmarks

Export Zotero items

Format a bibliography (citeproc / any CSL style)

Render a formatted bibliography in any CSL style using citeproc-js — no Zotero library write required. Provide either `items` (an array of CSL-JSON objects, e.g. from zotero_import or external metadata) or `item_keys` (library items, which are exported to CSL-JSON first). Choose `style` (a name like "APA 7th" or a CSL id; default "apa"), `locale` (default "en-US"), and `format` (html/text/rtf; default html). The formatted bibliography text is returned. Use this for arbitrary items or styles; for items already in the library you can also use zotero_bibliography (server-rendered).

Output format (default html).

Library item keys (exported to CSL-JSON).

CSL-JSON items to format.

Locale (default "en-US").

Style name or CSL id (default "apa").

Attachment full-text

Not a search — to find which items contain a term, use `zotero_search_items` with qmode=everything. This reads, sets, or tracks one attachment's already-extracted full text by key. `action`: "get" returns the indexed text content plus indexing stats for an attachment item (only attachment items have full text; returns found:false if none); "set" stores extracted text for an attachment (provide `content` and the indexing counts); "since" returns the map of attachment keys whose full text changed after a given library `version` (useful for incremental indexing). Only attachment items support full text. "get" and "since" read through the running Zotero desktop app when there is one (no cloud key needed), otherwise the cloud Web API; "set" always writes via the cloud Web API.

Extracted text (set).

Attachment item key (get/set).

Library version for "since" (default 0).

Attachment full-text

Get attachment full text / passages / outline (read-only)

Retrieve an item's PDF or EPUB text for grounding. Pass a parent `item_key` (its best PDF/EPUB attachment is resolved automatically) or an attachment key. With `query`, returns the top relevant passages with locators (char offsets, nearest section, and a page); with `page_range` (e.g. "3-7"), returns just those pages, re-extracted from the PDF so the span is exact; with `outline:true`, returns the PDF's table of contents with page numbers (the cheapest way to decide which pages to read next); with none of them, returns a truncated head. Text comes from Zotero's full-text index when available; when the attachment is NOT indexed yet, the file itself is read and parsed on the fly (`fallback`, on by default; set `fallback:false` to disable), so a PDF added minutes ago still returns text (marked fulltextSource:"pdf" or "epub", with fileSource saying where the bytes came from). The file is read from the running Zotero desktop app, else straight out of the local Zotero storage folder, else downloaded from Zotero cloud storage. Page numbers are exact whenever the PDF was parsed, and otherwise an estimate (pageApprox) unless `precise_pages:true`. Read-only; the indexed text is served by the running Zotero desktop app when there is one, otherwise by the cloud Web API. Use this to cite a claim with a page after finding an item via zotero_search_items / zotero_semantic_search.

When Zotero has no indexed full text for the attachment, read the file itself and extract it directly (default true).

Parent item key or attachment key.

Best-effort cap on total returned text (default 12000); a single passage is never split, so one passage may slightly exceed it.

Max passages (default 5).

Return the PDF's table of contents (heading, page, nesting level) instead of text.

Page span like "3-7" (1-based, inclusive). PDFs only.

Re-extract the PDF for exact page numbers (already the default with `page_range`).

Return top passages relevant to this query.

Get a Zotero item

Fetch one item by its key, returning the full item record (itemType, all bibliographic fields, creators, tags, collections, relations, version). Optionally set `include_children` to also return the item's child notes and attachments. Use `include` to additionally request rendered output: "bib" (formatted bibliography entry), "citation" (inline citation), or "csljson" (CSL-JSON for downstream formatting); combine with `style` (a CSL style id, default chicago-note-bibliography) and `locale`. The returned `version` is required if you later update or delete this item.

Extra rendered content: "bib", "citation", or "csljson".

Also fetch child notes/attachments.

The 8-character Zotero item key.

Locale for bib/citation, e.g. en-US.

CSL style id for bib/citation (default chicago-note-bibliography).

Get a Zotero item

List Zotero groups

List the group libraries the current API key can access, with each group's id, name, type, item count, and edit permissions. Use a returned group id with the `library_id`/`library_type:"group"` parameters of other tools to operate on that group library. Requires a cloud API key.

List Zotero groups

Import items by identifier or URL

Resolve bibliographic metadata to Zotero item-data and optionally save it to your library. `action: "by_identifier"` resolves a DOI, ISBN, PMID, arXiv id, or ADS bibcode (set `identifier`); `action: "by_url"` scrapes a web page (set `url`) and may return multiple choices to pick from. Set `save_to_library:true` (and optionally `collection_key`) to persist the resolved items — saved into the running Zotero desktop app when available, otherwise via the cloud Web API (requires ZOTERO_API_KEY); otherwise the resolved metadata is returned without saving. When a Zotero translation-server is reachable (ZOTEUS_TRANSLATION_SERVER_URL, default http://127.0.0.1:1969) it is the primary path; if none is running, DOI and arXiv ids fall back to built-in resolution (OpenAlex/Crossref and the arXiv API respectively) — the result then carries a `source` field ("scholar" or "arxiv"). ISBN/PMID/bibcode and web URLs require a translation-server.

Title for the attached file, e.g. "Full Text PDF".

File URL (e.g. an arXiv PDF) to download and attach as a stored attachment to the (single) imported item. Works on every save path: the desktop app when one is reachable, otherwise the cloud Web API.

Collection to add saved items to: an 8-char collection key or a Zotero treeViewID like "C20".

DOI (10.…), arXiv id (YYMM.NNNNN), ISBN, PMID, or ADS bibcode.

Persist the resolved items — into the running Zotero desktop app when available, otherwise the cloud Web API (needs a cloud key).

Web page URL to scrape (needs a translation-server).

Import items by identifier or URL

Build the semantic search index

Manage the local hybrid-search index used by zotero_semantic_search. Every job runs in the background on the server, so this tool returns immediately and never blocks on large libraries. THREE write actions, and picking the right one matters: `action: "update"` is the cheap one and should be the default for a library that is already indexed; `action: "build"` and `action: "refresh"` both rebuild the WHOLE index, which on a large library means many minutes and, with an API embedding provider, real spend (they differ in one thing: build resumes an interrupted build, refresh always starts over). `action: "build"`/"refresh" pages the library's top-level items (100-at-a-time, stopping at the server's item cap, ZOTEUS_INDEX_MAX_ITEMS, default 5000, or at a smaller `limit` if one is given), indexes their text (title, abstract, creators, tags) for BM25 keyword search and, if an embedding provider is configured, for vector search, persisting partial progress atomically as it goes; use it for the first build, after changing the embedding model, or to widen a previously capped build. It is ALSO the repair: if the index cannot be read at all, only `action:"build"` clears it, by deleting the unreadable file and opening a fresh one before rebuilding (nothing repairs it at startup or inside a query). `action: "update"` instead fetches only the items changed since the version the index recorded (Zotero's `?since=`), re-chunks and re-embeds just those, and removes items the library no longer holds (diffed from a cheap keys-only `?format=versions` census, since the deletion log is cloud-only); untouched items are never re-embedded, so adding a handful of items costs seconds instead of a full rebuild. Update falls back to a full rebuild by itself, and says so in `updateNotice`, when a delta would be wrong: no version stamp recorded yet, the library is now served by a different Zotero API (the desktop app and the cloud number their versions independently), or the embedding model changed. An update ALSO asks Zotero's full-text index what it has extracted since the build (that is a separate version sequence from item versions, so a PDF Zotero extracted when it was first opened changes no item version and appears in no delta) and indexes the new body text for items nothing else touched; on a library where nothing was extracted, that costs one request. A build or update interrupted by `action:"stop"`, a crash or a restart leaves a checkpoint, and `action: "build"` RESUMES from it: the items already committed stay searchable and are never re-fetched or re-embedded, and only work since the last save is redone (`resumedFrom` on the status reports how many were inherited). `action: "refresh"` is the one that always starts over. A build also indexes the reader's OWN words by default: every child note, and every PDF annotation (its highlighted passage and its comment), as extra passages carrying the parent item's key — so `zotero_annotate` writes text that search can then find, an item with forty annotations still takes one result slot, and a hit whose snippet came from one is marked source:"note" or source:"annotation". That corpus is one paged crawl of hand-written text, orders of magnitude smaller than attachment bodies; turn it off with `own_words:false` or ZOTEUS_INDEX_OWN_WORDS=false. An `action:"update"` keeps it current for the cost of one request when nothing was written: notes and annotations are ordinary items carrying ordinary versions, so an edit, an addition and a deletion are all found by comparing the library's note/annotation keys against the ones the index holds — which is also how an index built before this existed fills its gap, once, on its first update. Set `fulltext:true` to ALSO index the body text Zotero extracted from each item's attachments, which is what makes semantic search match a claim buried in a PDF rather than only its title and abstract; it is off by default because it multiplies build time and index size (default cap: 40000 characters per item, tunable with `fulltext_max_chars`), and only attachments Zotero has already extracted are available. That pass used to be refused inside Claude Desktop, where a build that reached it killed the server process partway through with no error at all (#37); the cause was the on-device embedding model asking Electron's allocator for a block it will not serve, so the server now embeds fewer passages per call there and the build runs to completion. It is somewhat slower inside the app than in a terminal and produces exactly the same index, so a user who wants the fastest possible first build can still run one headlessly against the same ZOTEUS_DATA_DIR and let Desktop read the result. A build runs in TWO passes and reports which one it is on as `phase`: every item's metadata is indexed first, across the whole library, and only then are attachment bodies crawled (`fulltextItemsScanned` of `fulltextItemsTotal`). So the library is fully searchable on titles, abstracts, creators and tags long before a full-text crawl that can run for hours finishes — tell the user they can search already rather than asking them to wait for state:"done". Start a job, then POLL `action: "status"` every few seconds until `state` is "done" (or "error"); calling build or update again while one is running just returns current progress. `action: "status"` reports `state` (idle|building|done|error), `operation` (build|update), `phase` (metadata|fulltext), fetch/embed progress, `itemsRemoved`, index size, the active embedder, `libraryVersion`/`libraryBackend` (the version stamp an update diffs from), `fulltextVersion` (how far into Zotero's separate full-text sequence the index has read), `resumedFrom` (items inherited when a build resumed an interrupted one), `itemsTotal`/`itemsAvailable` (which differ, with a warning, when the cap stopped the crawl short of the library), `ownWordsItems`/`ownWordsPassages` (the notes and annotations indexed, with `ownWordsReason` if they could not be read), and (when full text was requested) `fulltextItems`/`fulltextPassages` plus `fulltextReason` if it produced nothing. It also reports `localApiDegradedAt` when the job saturated Zotero's local API and the whole session fell back to the Zotero Web API: that fallback works, so nothing errors, but the Web API is slower and rate-limited and the rest of the build takes far longer than its start suggested, so tell the user rather than letting them watch an unexplained slowdown (the crawl also backs off to one attachment at a time by itself, to let the app recover). It reports where the index is stored (`storage`: sqlite or memory, set by ZOTEUS_INDEX_BACKEND), `storageNotice` when opening that store imported or refused an older JSON index, `persistError` when the index could not be written to disk at all, and how the last semantic query ranked vectors (`vectorScan`: "codes" for the two-stage path, "exact" for a full scan of every vector, with `vectorScanNotice` when that needs explaining). When the embedding provider is an API (ZOTEUS_EMBEDDINGS=openai or gemini), status also reports `embedRate`: the batch size, the pause between requests, the estimated tokens per request and the tokens per minute the build is actually sustaining, plus `passagesWithoutVectors` when the index holds passages nothing has embedded yet. A build whose embedder was rate-limited to a standstill keeps every passage it indexed and stays RESUMABLE: tell the user to run `action:"build"` again, which embeds only the passages that have no vector and re-fetches nothing, and NOT `action:"refresh"`, which starts the whole crawl over and pays for every vector a second time. A rate-limited request already backs off and retries by itself; if a build reports it is riding the provider's tokens-per-minute limit, the fix is ZOTEUS_EMBED_BATCH_DELAY_MS (with ZOTEUS_EMBED_BATCH_SIZE), not a smaller library. `action: "stop"` cancels a running job (partial data is kept and stays searchable; a stopped update leaves the version stamp untouched so the next one repeats the delta, and a stopped build leaves a checkpoint the next `action:"build"` resumes from). A partially built index is always usable for keyword search. Local embeddings are CPU-bound (see ZOTEUS_EMBEDDINGS), so large builds take a while: poll status rather than retrying build.

Also index the full text Zotero extracted from each item's attachments, so searches match the body of a PDF. Resource-intensive (slower build, much larger index); defaults to ZOTEUS_INDEX_FULLTEXT (off unless set).

Cap on indexed full-text characters per item; 0 means no cap (default 40000). Only used with fulltext.

Max items to index. Lowers the configured cap for this build only; it cannot raise it. The cap defaults to 5000 and is set by ZOTEUS_INDEX_MAX_ITEMS.

Also index the reader's OWN words — child notes and PDF annotations (highlight text and comments) — as passages carrying the parent item's key. On by default (ZOTEUS_INDEX_OWN_WORDS); the whole corpus is one paged crawl of hand-written text, so it costs a fraction of what fulltext does.

Build the semantic search index

List Zotero collections (read-only)

List collections in a Zotero library (key, name, parent collection key, item count). Read-only — available even in read-only mode (unlike zotero_manage_collections, which also writes). Use the keys to scope zotero_search_items (collectionKey) or zotero_tag_audit (scope.collection_keys).

Only top-level collections.

List Zotero collections (read-only)

List Zotero tags (read-only)

List tags in a Zotero library with their usage count and whether each was auto-applied by Zotero. Optional `q` substring filter and `limit`. Read-only — available even when the connector runs in read-only mode (unlike zotero_manage_tags, which also writes). For taxonomy hygiene use zotero_tag_audit.

Max tags (default 100).

Substring filter.

List Zotero tags (read-only)

Manage Zotero collections

List, create, rename, reparent, or delete collections, and move items into or out of a collection. Set `action` to one of: "list" (all collections with key/name/parent), "create" (needs `name`, optional `parent_collection` key — omit for top-level), "rename" (needs `collection_key` + `name`), "reparent" (needs `collection_key`; `parent_collection` key, or omit to move to top level), "delete" (needs `collection_key`), "add_items" / "remove_items" (need `collection_key` + `item_keys`; collection membership lives on each item). All actions except "list" write to the cloud Web API.

list create rename reparent delete add_items remove_items

Target collection key (all actions except list/create).

Item keys (add_items/remove_items).

Collection name (create/rename).

Parent collection key; omit for top-level.

Manage Zotero collections

Manage Zotero tags

List tags, or add/remove tags on items. Set `action` to "list" (returns library tags; supports `q` substring filter), "add" (add `tags` to each of `item_keys`), or "remove" (remove `tags` from each of `item_keys`). Tags are stored on the parent item's tag array, so add/remove edits the items (cloud Web API). Tag names are case-sensitive.

Items to modify (add/remove).

Substring filter for list.

Tag names to add or remove.

Manage Zotero tags

Manage Zotero saved searches

List, create, or delete saved-search DEFINITIONS. NOTE: the Zotero cloud Web API stores saved searches but does NOT execute them — to get the items a saved search matches, run an equivalent zotero_search_items query (or use the desktop local API when available). Set `action` to "list" (all saved searches with their conditions), "create" (needs `name` and `conditions`, each `{condition, operator, value}`), or "delete" (needs `search_key`). Writes go to the cloud Web API.

Search conditions (create).

Saved-search name (create).

Saved-search key (delete).

Manage Zotero saved searches

Zotero data model (types & fields)

Return the Zotero data model so you never hardcode item shapes. With no arguments, returns the schema version and the list of all item type names. With `item_type`, returns the valid fields and creator types for that type (the "primary" creator type is listed first). Use this to validate an item before creating or updating it: notes, attachments, and annotations are item types too but bypass the normal field/creator model.

If set, return the fields & creator types for this item type.

Zotero data model (types & fields)

Scholarly context (references, citations, related)

Explore the EXTERNAL scholarly graph around a paper (OpenAlex, Crossref fallback). This does NOT search, list, or read your Zotero library — it queries the open web, and results are works from the scholarly web, not your items. To search or inspect YOUR library use zotero_search_items, zotero_semantic_search, zotero_get_item, or zotero_list_tags instead. Provide a `doi` and an `action`: "lookup" (metadata + citation count), "references" (works this paper cites), "citations" (works that cite this paper, most-cited first), or "related" (similar works). Set `include_in_library: true` to additionally flag which results your library already holds (off by default because it scans the library); otherwise every result is just a web record. `limit` caps results (default 20). Read-only; calls external scholarly APIs.

The DOI of the paper (with or without the https://doi.org/ prefix).

Also scan the library and flag results already saved (default false; scanning is expensive).

Max results (default 20).

Search Zotero items

Search or list items in a Zotero library or collection. Quick search via `q` (`qmode`: titleCreatorYear=default, matches title/creator/year only; everything=also searches notes & attachment full text). For presence checks ("is X in my library?"): a default-mode `q` that matches nothing auto-retries once in `everything` mode, so terms appearing only inside PDF text don't false-negative — pin `qmode` explicitly to disable. An empty `everything` result is reported as strong-but-not-conclusive, since un-indexed/scanned/un-synced PDFs aren't full-text searchable. Also supports boolean `itemType` filters (use `||` for OR, repeat or `&&` for AND, leading `-` to negate, e.g. "journalArticle || book", "-attachment"), boolean `tag` filters (same syntax; escape a literal leading hyphen as "\-"), `since` (version) for incremental queries, `sort`/`direction`, and `limit`/`start` paging. Set `response_format` to "detailed" to also return technical fields (version, tags, collections, DOI, url) needed before chaining a write; the default "concise" returns high-signal projections (key, itemType, title, creators, date). Reads are served from the fast desktop local API when available, otherwise the cloud Web API. Returns `totalResults` so you can tell when to page rather than assuming you saw everything. For conceptual/"papers about X" queries by meaning rather than exact fields, use zotero_semantic_search instead.

Restrict to a collection by key.

Boolean itemType filter, e.g. "journalArticle || book".

Max items (default 25, max 100).

Quick/full-text search string.

Detail level of returned items.

Return items modified after this library version.

Boolean tag filter, e.g. "to-read && 2024".

Only top-level items (exclude child notes/attachments).

Search Zotero items

Semantic / hybrid library search

Search the library by meaning, not just keywords. Combines BM25 keyword scoring with vector similarity (when an embedding provider is configured) via reciprocal-rank fusion, and returns the best-matching items with a snippet and score. By default it searches item metadata and abstracts; if the index was built with `fulltext` on (zotero_index fulltext:true, or ZOTEUS_INDEX_FULLTEXT=true) it also searches the body text of attachments, and a hit whose snippet came from a PDF body is marked source:"fulltext". It ALSO searches the words the reader wrote — child notes and PDF annotations (highlight text and comments) — unless that was turned off (ZOTEUS_INDEX_OWN_WORDS=false); a hit from one is marked source:"note" or source:"annotation" and is attributed to the item it hangs off, so an item with forty annotations is one result rather than forty. `mode`: "auto" (hybrid, default), "keyword" (BM25 only), or "semantic" (vector only). "semantic" needs vectors in the index: when the configured embedder is not running (e.g. the on-device model runtime is not installed) it returns an error naming the cause instead of an empty result set, and "auto" keeps working as keyword search while saying so. The index must be built once before first use: when it is empty this tool starts a background build automatically (`auto_build`, on by default) and tells you to poll zotero_index action:"status" and retry — pass `auto_build:false` to opt out. For exact field/tag/itemType filtering use zotero_search_items instead; use this for conceptual/"papers about X" queries. To read the actual passages of a found item (with page locators) use zotero_get_fulltext.

Start building the index automatically in the background when it is empty (default true).

Max results (default 10).

Natural-language query.

Semantic / hybrid library search

Resolve CSL citation styles

Resolve a human citation-style name to a valid CSL style id and confirm it is available, or list common style aliases. `action: "resolve"` maps names like "APA 7th", "IEEE", "Vancouver", "Chicago", "MLA", "Nature" to the correct CSL id (e.g. apa, ieee, modern-language-association) and verifies the style can be fetched; pass the returned `styleId` as the `style` argument to zotero_format_bibliography or zotero_bibliography. `action: "list"` returns the built-in common aliases (any id from the CSL styles repository also works). Dependent styles are resolved to their independent parent automatically when formatting.

Style name to resolve (e.g. "APA 7th").

Resolve CSL citation styles

Incremental sync delta

Return what changed in a library since a given version, for efficient incremental sync. Provide `since` (a library version; 0 = everything). Returns, per object type (items/collections/searches/tags), the map of keys→version that changed after `since`, plus the deletion log (keys removed since `since`). This is the version-based delta the Zotero sync algorithm uses — fetch the changed keys, then pull only those with zotero_get_item/zotero_search_items. Reads via the cloud Web API.

Include the deletion log (default true).

Library version to diff from (default 0).

Which object types to check (default all).

Incremental sync delta

Audit tags against a controlled vocabulary

Audit a library against a controlled tag vocabulary with priority tiers. Provide the vocabulary inline as `vocabulary` (or a JSON file via `vocabulary_path`): { tags:[{name,tier?}], tiers?:[{name,required?}] }. Reports (1) off-taxonomy tags (library tags not in the vocabulary; Zotero auto-applied tags are bucketed separately unless include_auto), (2) items missing a tag from each required tier, and (3) optional per-collection coverage when `scope.collection_keys` is given. Read-only. Tag/auto-tag enumeration uses the cloud Web API.

Treat Zotero auto-applied tags as off-taxonomy too.

Max items listed per report (default 50).

Path to a JSON file with the vocabulary.

Trash or restore Zotero items

Move items to the trash (the safe, REVERSIBLE default) or restore them. This sets the `deleted` flag (1=trash, 0=restore) — it is NOT a permanent delete, so trashed items can be recovered here or in the Zotero app. Use this instead of zotero_delete_items unless you truly need irreversible removal. Provide `item_keys` and optional `action` (default "trash"). Writes go to the running Zotero desktop app for your personal library (via its local-API writes where available), otherwise to the cloud Web API.

Default "trash".

Item keys to trash or restore.

Trash or restore Zotero items

Update a Zotero item

Partially update one item (HTTP PATCH — only the fields you supply change; omitted fields are preserved). Provide `item_key` and a `patch` object of the fields to change (e.g. {"title":"New","extra":"note"} or {"tags":[{"tag":"reviewed"}]}). All field values are plain JSON strings/numbers/booleans/arrays — never wrapped in nested objects (e.g. `"title": "New"`, NOT `"title": {"title": "New"}`). Optimistic concurrency is handled for you: if you pass the item's `version` it is used; otherwise the current version is fetched first. If the item changed on the server in the meantime (412), the update is automatically re-fetched and retried once. Writes go to the cloud Web API. Set `dry_run:true` to preview the field-level before→after diff without writing (arrays like tags/collections are replaced wholesale by PATCH, not merged; a dry_run call performs no write).

Preview the field-level before→after diff without writing.

The 8-character item key.

Object of fields to change (PATCH semantics), e.g. {"title": "New title", "date": "2024-02-01", "tags": [{"tag": "reviewed"}], "collections": ["ABCD1234"]}. Structured fields (creators, tags, collections, relations) must be real JSON arrays/objects, not JSON-encoded strings. Values are plain, never wrapped in nested objects.

Array of 8-character collection keys.

Object mapping a relation predicate to a URI or array of URIs.

Array of tag objects: [{"tag": "name"}].

Known current version; fetched automatically if omitted.

Update a Zotero item

Zotero identity & access

Resolve the current Zotero identity (userID, username, display name) and per-library access scopes from the configured API key, and report which library backends are available (cloud Web API and/or the desktop local API). Call this first to discover the userID — never ask the user to type a numeric ID. If no API key is configured, the server runs in local-only read mode against the desktop library (users/0).

Zotero identity & access