Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
ZOTERO_LOCALNoUse the local Zotero API instead of the web APIfalse
ZOTERO_API_KEYNoYour Zotero API key (for web API)
ZOTERO_LIBRARY_IDNoYour Zotero library ID (for web API)
ZOTERO_LIBRARY_TYPENoThe type of library (user or group)user

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
extensions
{
  "io.modelcontextprotocol/ui": {}
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
zotero_get_annotations

Get annotations (highlights and attached notes on PDF/EPUB attachments) for a specific item or across the active Zotero library. item_key: pass the parent item key OR an attachment key — both work; attachment-to-parent resolution is automatic. ALWAYS pass item_key when you know which item you want; calling without it returns every annotation in the library (potentially thousands). use_pdf_extraction=True falls back to direct PDF parsing when the Zotero API has no stored annotation record — useful for annotations made outside Zotero desktop. limit: cap on annotations returned; None (default) returns all. Uses Better BibTeX when Zotero desktop is running locally, otherwise the Zotero web API. Example: zotero_get_annotations(item_key='ABC12345') → every highlight/note on that paper.

zotero_get_notes

Retrieve standalone or child notes from the active Zotero library. item_key: optional; when provided, returns only notes attached to that parent item; when omitted, returns notes across the entire library (capped by limit). If you want to search note content instead, use zotero_search_notes. limit: max notes to return (default 20). truncate=True (default) shortens long note bodies for display — pass False for complete content. raw_html=True returns the note's original HTML instead of stripped text; use this when you intend to edit and round-trip via zotero_update_note (preserves formatting). Example: zotero_get_notes(item_key='ABC12345', raw_html=True) → every note on that item in round-trippable HTML.

zotero_search_notes

Search note and annotation text across the active Zotero library. query: matched against the stripped-text body of notes/annotations (case-insensitive substring). Use this when you DON'T already know the parent item; if you do, prefer zotero_get_notes(item_key=…) which returns all notes for that item directly. limit: max results (default 20). raw_html=True returns matched notes as raw HTML while matching still runs on stripped text — use for round-tripping via zotero_update_note. Scope: active library only (use zotero_switch_library to change). Example: zotero_search_notes(query='mindfulness') → notes anywhere in the library whose text contains that word.

zotero_create_note

Create a new child note attached to a Zotero item. item_key: parent item key (the note becomes a child of this item). note_title: title displayed in Zotero's note pane. note_text: note body; simple HTML is preserved (p, strong, em, ul/ol/li, a, blockquote, code). tags: optional list of tag strings to attach to the note. Requires a writable library (web API key or hybrid mode) — fails in local-only mode. To edit an existing note instead, use zotero_update_note. Example: zotero_create_note(item_key='ABC12345', note_title='Reading notes', note_text='Key claim: ...', tags=['to-cite']).

zotero_update_note

Update the HTML body of an existing Zotero note. item_key: the NOTE's own key (NOT the parent item's key) — use zotero_get_notes or zotero_search_notes to find it. note_text: new HTML content. append=False (default) REPLACES the entire note body; append=True concatenates note_text after the existing body. To preserve existing formatting when editing, first fetch the note with zotero_get_notes(raw_html=True), modify the HTML, then pass the full HTML back. Requires a writable library (web API key or hybrid mode) — fails in local-only mode. Example: zotero_update_note(item_key='NOTE1234', note_text='Revised summary', append=False).

zotero_delete_note

Move a Zotero note to the Trash. Non-destructive: trashed notes remain recoverable from the Trash view in Zotero desktop. item_key: the NOTE's own key — use zotero_get_notes or zotero_search_notes to find it (passing a parent item's key will fail or trash the wrong thing). To permanently delete, the user must empty the Trash in the Zotero UI — no API exists for that step. Scope: notes only; this tool cannot trash items, collections, or attachments. Requires a writable library (web API key or hybrid mode) — fails in local-only mode. Example: zotero_delete_note(item_key='NOTE1234').

zotero_create_annotation

Create a TEXT-HIGHLIGHT annotation on a PDF or EPUB attachment, with optional comment and tags. For rectangular selections of figures or non-text regions, use zotero_create_area_annotation instead. attachment_key: the PDF/EPUB attachment key — NOT the parent item key (use zotero_get_item_children to find attachments). page: 1-indexed page number (page 1 is the first page). text: exact text to highlight; the tool locates and rectangles it on the page via the PDF/EPUB text layer — scanned/image-only PDFs will not match. color: hex color (default '#ffd400' yellow). comment: optional note attached to the highlight. tags: optional list of tag strings to apply to the annotation. Requires PyMuPDF (pip install zotero-mcp-server[pdf]) and a writable library (web API key or hybrid mode). Example: zotero_create_annotation(attachment_key='NHZFE5A7', page=4, text='mindfulness-based therapy', comment='definition to cite').

zotero_create_area_annotation

Create a PDF AREA/IMAGE annotation — a rectangle drawn on an arbitrary page region (figures, diagrams, tables, non-text content). For highlighting selectable text, use zotero_create_annotation instead. attachment_key: PDF attachment key — NOT the parent item key (use zotero_get_item_children to find attachments). page: 1-indexed page number (page 1 is the first page). x, y: top-left corner in NORMALIZED page coordinates in [0, 1] — (0, 0) is the page's top-left, (1, 1) is the bottom-right. width, height: rectangle size, also normalized to [0, 1] and relative to the page (not to x, y). comment: optional note attached to the annotation. color: hex color (default '#ffd400' yellow). Scope: PDFs only — EPUB attachments are NOT supported. Requires a writable library (web API key or hybrid mode) — fails in local-only mode. Example: zotero_create_area_annotation(attachment_key='NHZFE5A7', page=7, x=0.15, y=0.22, width=0.6, height=0.35, comment='Figure 3 — mean completion rates').

zotero_update_annotation

Update an existing Zotero annotation. Editable fields: text (highlight text), comment, color (hex like '#ffd400'), and tags. Tags can be replaced wholesale via tags, or edited incrementally via add_tags/remove_tags (mutually exclusive with tags). Position/page/sortIndex are anchored to the PDF/EPUB geometry and are not editable.

zotero_delete_annotation

Move a Zotero annotation to the Trash. Trashed annotations are recoverable from Zotero's Trash — empty the Trash in the Zotero UI for permanent deletion.

zotero_get_item_metadata

Fetch detailed metadata (title, creators, date, DOI, publisher, tags, abstract, URL, etc.) for ONE Zotero item by key. If the metadata and abstract don't contain what you need, call zotero_get_item_fulltext to read the paper — but that is resource-intensive (10K+ tokens) and should NEVER be used for searching; use zotero_search_items or zotero_semantic_search instead. item_key: the 8-character Zotero item key (NOT a DOI or title). include_abstract=True (default) includes the abstractNote in markdown output; pass False to trim tokens when you don't need it. (Ignored in bibtex/json formats.) format='markdown' (default) returns a human-readable block; format='json' returns the complete raw Zotero item record; format='bibtex' returns a BibTeX citation string suitable for .bib files. Scope: active library only (switch with zotero_switch_library). Unlike list endpoints, this returns items EVEN IF THEY ARE IN THE TRASH — a Status: In Trash line is surfaced when the item is trashed (recoverable via the Zotero UI). Collection membership is shown as keys rather than a bare count so the caller can verify entries against zotero_search_collections (the Zotero API does not cascade collection-delete to items, so dangling references can linger). Example: zotero_get_item_metadata(item_key='RTKZQI8E', format='bibtex').

zotero_get_item_fulltext

Return the full extracted text of a Zotero item's primary attachment (PDF or EPUB). WARNING: returns the entire paper (often 10K+ tokens). Use ONLY when the user explicitly wants to READ the paper — not for searching or browsing. For topic search use zotero_semantic_search; for metadata only use zotero_get_item_metadata. Avoid calling this on multiple papers in one conversation unless the user specifically asked to read several. item_key: 8-character Zotero item key (parent item, not the attachment). The tool locates the attached PDF/EPUB itself. Scope: active library only. Extraction path (in order): local Zotero storage via SQLite when running in local mode (fastest, respects pdf_max_pages config); Zotero's server-side fulltext index; direct download + PyMuPDF parsing as a last resort. Image-only scanned PDFs without OCR may return little or no text. Example: zotero_get_item_fulltext(item_key='RTKZQI8E').

zotero_get_attachment_path

Return the local filesystem path(s) of a Zotero item's attachments. Local mode only. Useful when you want to read a large PDF directly (e.g., a book) instead of going through zotero_get_item_fulltext, which is page-limited.

zotero_get_collections

List all collections in the currently active Zotero library as a hierarchical tree (parents and nested subcollections, each with its 8-character key). Use this when the user wants to see the full library structure. If you already know a name and just need the key, prefer zotero_search_collections — it returns only matches. Scope is limited to the active library — switch libraries with zotero_switch_library before listing. Deep hierarchies render inline without truncation, so very deep trees can be long. limit: cap on collections returned; pass None (default) to use 100, or raise to 5000 for libraries with thousands of collections. include_trashed: when True, also show collections in the Zotero Trash (annotated as such). Default False, matching Zotero desktop's default view. Example output:

  • Orals (Key: MT53KB66)

    • Early America (Key: 3249BZKE)

      • I. Historiography & Methodology (Key: XFN79DUT)

zotero_get_collection_items

Get all items in a specific Zotero collection. Supports detail='keys_only' (minimal), 'summary' (default, no abstracts), or 'full' (with abstracts). Includes PDF/notes indicators. TIP: To find papers on a specific topic, use zotero_semantic_search instead — it's faster and returns only relevant results.

zotero_get_item_children

List the child items (attachments, notes, and annotations that are direct children of the attachment) of ONE parent Zotero item. Use this to find an item's PDF/EPUB attachment key before calling zotero_create_annotation, zotero_create_area_annotation, or zotero_get_pdf_outline — all of which take an attachment key, NOT the parent item key. If you need children for several items at once, use zotero_get_items_children (one batched API call instead of N). item_key: the parent item's 8-character key. Returns parent-child structure as markdown: each attachment with its content type and filename, each note with its title. Scope: active library only. Example: zotero_get_item_children(item_key='RTKZQI8E') → its PDF attachment key + any notes.

zotero_get_items_children

Batch variant of zotero_get_item_children: fetch child items (attachments, notes, annotations) for MULTIPLE parent items in a single API round trip. Much cheaper than calling zotero_get_item_children N times — use this whenever you have 2+ item keys in hand. item_keys: list of 8-character parent item keys (also accepts a JSON-encoded list string). Pass as an ARRAY, not a single concatenated string. Returns a markdown section per parent with its children grouped underneath. Missing keys are reported per-item rather than aborting the whole call. Scope: active library only. Example: zotero_get_items_children(item_keys=['RTKZQI8E', '9UZR8GXT']).

zotero_get_tags

List all tags used in the currently active Zotero library, as a flat markdown list (one tag per line). Use this for tag discovery before filtering with zotero_search_by_tag or batch-editing with zotero_batch_update_tags. Scope is the active library only — switch with zotero_switch_library before listing. The list is flat: tags have no parent/child structure in Zotero, only a colon convention ("area/subtag") that this tool preserves verbatim. limit: cap on tags returned; None (default) returns all. Example output:

  • to-read

  • methods/qualitative

  • AI agents

zotero_list_libraries

List every Zotero library this MCP can address: the user's personal library (libraryID=1 conventionally), all group libraries the user is a member of (with groupID), and (in local mode) RSS feed libraries. Each entry shows the library/group ID, display name, and item count. Use this to discover a library ID before calling zotero_switch_library — the two form a read-then-switch workflow. If the user only wants to see Zotero collections inside the CURRENT library, use zotero_get_collections instead. No parameters. In local mode: reads the local Zotero SQLite DB (fast, includes RSS feeds). In web mode: queries /groups via the Zotero web API (no feeds). Read-only; no side effects. The active library isn't flagged in the output — track it yourself from the last successful zotero_switch_library call (or the ZOTERO_LIBRARY_ID env var if none). Example: zotero_list_libraries().

zotero_switch_library

Switch the active library context. EVERY subsequent read/write tool call (collections, items, annotations, search — all of them) operates on the library set here. Changes persist for the rest of the session or until the next switch. Discover valid library IDs/types via zotero_list_libraries first; don't guess. library_id: library ID string as returned by zotero_list_libraries (numeric for user/group, numeric for feeds). library_type: 'user' — the personal library; 'group' (default) — a group library; 'feeds' — a local RSS feed library; 'default' — RESET to whatever the ZOTERO_LIBRARY_ID / ZOTERO_LIBRARY_TYPE env vars configure (library_id is ignored in this mode). Fails fast if the library_id isn't accessible under the current credentials. Example: zotero_switch_library(library_id='5294983', library_type='group') or zotero_switch_library(library_id='', library_type='default').

zotero_list_feeds

List all RSS feed subscriptions configured in the local Zotero desktop install. Each entry includes the feed's library ID, display name, source URL, item count, and last-checked timestamp. Use this to discover a feed's library_id before calling zotero_get_feed_items; the two form a list-then-fetch workflow analogous to list_libraries + switch_library. No parameters. LOCAL MODE ONLY — RSS feeds live in the local SQLite database and are not exposed by the Zotero web API. Running this in web mode returns a clear error. Read-only; no side effects. Example: zotero_list_feeds() → all subscribed feeds.

zotero_get_feed_items

Fetch recent items from a SPECIFIC Zotero RSS feed by its local library ID. Returns titles, authors, dates, and URLs as a markdown list. Find the right library_id first with zotero_list_feeds — guessing feed IDs never works. library_id: INTEGER library ID of the feed (as shown by zotero_list_feeds, NOT the feed's name or URL). limit: max feed items to return (default 20). LOCAL MODE ONLY — feeds aren't exposed by the Zotero web API. Calls in web mode return a clear error. Read-only; does not trigger a new RSS fetch (Zotero desktop refreshes on its own schedule). Example: zotero_get_feed_items(library_id=12, limit=30).

zotero_get_recent

List the most recently ADDED items (by dateAdded) in the active library, optionally scoped to a single collection. Use this for 'what did I add recently?' questions — NOT for general topic search (use zotero_semantic_search) or for a collection's full contents (use zotero_get_collection_items). limit: how many recent items to return (default 10). collection_key: optional 8-character collection key to restrict results to that collection; when omitted, returns the N most recent items across the whole library. Ordering is dateAdded DESC. All item types are returned, INCLUDING standalone notes and attachments — so results can mix papers, notes, and loose PDFs. If you only want parent items, filter client-side by itemType in the output. Scope: active library only (switch with zotero_switch_library). Example: zotero_get_recent(limit=20) or zotero_get_recent(collection_key='MT53KB66', limit=5).

zotero_get_item_related

Get all related items for a specific Zotero item. Returns items that are linked via the relations field.

search

ChatGPT custom connector SEARCH endpoint — name is REQUIRED by the MCP-over-web spec (see platform.openai.com/docs/mcp); do not rename. Not intended for general MCP clients — in Claude or other regular MCP contexts use zotero_semantic_search or zotero_search_items instead, which return richer markdown. Performs semantic search over the active Zotero library and returns a JSON string {"results":[{"id","title","url"}, ...]} matching the ChatGPT connector citation UI. URLs are zotero://select/items/ deep-links. query: topic string; natural language works (embedding match). No limit parameter — fixed at 10 per the connector UI's expected result-set size. Requires the semantic search DB populated — run zotero_update_search_database first if empty. SILENT FALLBACK: any error returns {"results":[]} rather than raising, to keep the ChatGPT connector stable. Example (agent-invoked): search(query='mindfulness-based therapy').

fetch

ChatGPT custom connector FETCH endpoint — name is REQUIRED by the MCP-over-web spec (see platform.openai.com/docs/mcp); do not rename. Not intended for general MCP clients — in Claude or other regular MCP contexts use zotero_get_item_fulltext and zotero_get_item_metadata, which return richer markdown. Retrieves a single Zotero item and returns a JSON envelope {"id","title","text","url","metadata":{...}} matching the ChatGPT connector citation viewer. id: an 8-char Zotero item key — typically from a previous search call. Blank/missing returns an empty envelope (no error). url field: Zotero web-library URL when ZOTERO_LIBRARY_ID is set; otherwise a zotero://select/items/ deep-link. text field: extracted fulltext via the same path as zotero_get_item_fulltext; if none can be extracted, falls back to title + authors + abstract so the connector isn't blank. metadata field: itemType, date, DOI, authors, tags, both URLs. SILENT FALLBACK: errors return an envelope with {"metadata":{"error":…}} rather than raising, to keep the ChatGPT connector stable. Example (agent-invoked): fetch(id='RTKZQI8E').

zotero_read_pdf_pages

Read specific page range(s) from a PDF attachment of a Zotero item. Use this when you know which pages to read — for example after getting the PDF outline via zotero_get_pdf_outline. Pages are 1-indexed. Requires PyMuPDF: pip install zotero-mcp-server[pdf]

zotero_search_items

Search Zotero items by substring match against metadata (title, creators, year, and — in 'everything' mode — abstract). Returns metadata + abstracts as markdown. IMPORTANT: keep queries SHORT and SIMPLE — 'Author Year' (e.g. 'Brewer 2011') or just an author name ('Cladder-Micus'). This is substring matching, not web search: each extra word NARROWS the match, so adding topic words usually returns fewer results, not more. For topic discovery, use zotero_semantic_search instead; for tag filtering use zotero_search_by_tag. If a query finds nothing, this tool automatically falls back to simplified queries and then semantic search. query: required substring. qmode: 'titleCreatorYear' (default) matches only title/authors/year; 'everything' also searches abstract. item_type: '-attachment' (default) excludes attachments; pass 'journalArticle', 'book', etc. to filter. tag: optional list of tag conditions (ANDed). limit: max results (default 10). collection_key: 8-char key to restrict to a collection (bypasses the fallback cascade). Example: zotero_search_items(query='Cladder-Micus') or zotero_search_items(query='Brewer 2011', limit=5).

zotero_search_by_tag

Find items carrying one or more tags, with boolean syntax support. tag: list of tag strings; each entry is a condition ANDed with the others, and within an entry you can use ' OR ' for disjunction and a leading '-' for exclusion. Example: tag=['methods OR methodology', '-draft'] matches items tagged 'methods' OR 'methodology' AND NOT tagged 'draft'. item_type: '-attachment' (default) excludes attachments; pass 'journalArticle', 'book', etc. to filter. limit: max results (default 10). collection_key: optional 8-char key to scope to a collection. Use zotero_get_tags to discover available tag names first. For free-text content search, use zotero_search_items or zotero_semantic_search instead. Example: zotero_search_by_tag(tag=['to-read'], limit=20).

zotero_search_by_citation_key

Look up a single Zotero item by its BetterBibTeX citation key (e.g. 'Smith2024' or 'cladderMicus2018'). Returns that one item's metadata, or a not-found message if no item has that key. citekey: the citation key exactly as assigned by BetterBibTeX (case-sensitive). In local mode: queries the running Better BibTeX plugin via its HTTP API (Zotero desktop must be running and have BBT installed). In web mode: scans the 'Extra' field of items for 'Citation Key:' lines — slower, and may miss items whose keys aren't persisted to Extra. Requires the Better BibTeX plugin in the user's Zotero install. For partial-key or free-text lookup, use zotero_search_items. Example: zotero_search_by_citation_key(citekey='hasan2026mcp') → metadata for that single item.

zotero_advanced_search

Advanced item search with multiple structured-field conditions joined by AND or OR. Use this when you need to filter by fields that zotero_search_items and zotero_search_by_tag can't express (date ranges, specific itemTypes, etc.). For plain text use zotero_search_items; for tags use zotero_search_by_tag; for topic discovery use zotero_semantic_search. conditions: list of {field, operation, value} dicts (also accepts a JSON string). Common fields: title, creator, date, dateAdded, dateModified, tag, itemType, publicationTitle, abstractNote, collection. Supported operations (exhaustive): is, isNot, contains, doesNotContain, beginsWith, endsWith, isGreaterThan, isLessThan, isBefore, isAfter. For 'added in the last N days', use field='dateAdded' with operation='isAfter' and an ISO date value (e.g. '2026-03-22'). join_mode: 'all' (AND, default) or 'any' (OR). sort_by: dateAdded, dateModified, title, creator, etc. sort_direction: 'asc' (default) or 'desc'. limit: max results (default 50, max 500). Example: zotero_advanced_search(conditions=[{'field': 'itemType', 'operation': 'is', 'value': 'preprint'}, {'field': 'dateAdded', 'operation': 'isAfter', 'value': '2026-03-22'}], join_mode='all').

zotero_semantic_search

Prioritized topic-search tool. Find papers by semantic similarity to a query using AI embeddings — the BEST tool for finding papers on a topic (e.g. 'papers about mindfulness-based therapy'), far more efficient than scanning collection items or reading abstracts. Works across the entire active library. query: the topic or concept; natural-language phrases work well. limit: max results (default 10). filters: optional metadata filters as a dict (e.g. {'itemType': 'journalArticle', 'year': '2023'}); also accepts a JSON string. Requires the semantic search database to be POPULATED — run zotero_update_search_database first if you just installed the server or added new items; check readiness with zotero_get_search_database_status. Available only when the [semantic] optional dependency is installed (pip install zotero-mcp-server[semantic]). Example: zotero_semantic_search(query='mindfulness-based cognitive therapy for depression', limit=5).

zotero_update_search_database

Build or refresh the semantic search embedding database from Zotero items. Run this: (a) after first install, (b) after adding items via zotero_add_by_doi / add_by_url / add_from_file, or (c) when the user has added items directly in Zotero desktop since the last update. By default the update is INCREMENTAL — only new or changed items are re-embedded, so repeated calls are cheap. force_rebuild=True re-embeds ALL items from scratch (slow; use when changing the embedding model or recovering from corruption). limit: optional cap on items processed (useful for smoke-testing). Progress is reported via the MCP context; on large libraries an incremental update is seconds, a full rebuild can take minutes. Requires the [semantic] optional dependency and a configured embedding provider (see config.json). Check status with zotero_get_search_database_status. Example: zotero_update_search_database() after adding a batch of papers.

zotero_get_search_database_status

Report the semantic search database's readiness and stats: item count, last update time, embedding provider / model, and whether the [semantic] optional dependency is installed. Use this to decide whether zotero_semantic_search will return useful results, or whether the user should run zotero_update_search_database first. Takes no parameters; no side effects. Returns a human-readable status block. If the [semantic] extras are not installed, returns an install hint instead of stats. Example: zotero_get_search_database_status() → count, last sync, provider summary.

zotero_batch_update_tags

Add and/or remove tags across multiple items in one call, selecting items by a text query, an existing tag, or both. Must supply at least one selector (query or tag) AND at least one action (add_tags or remove_tags) — otherwise returns an error. query: free-text matched against item metadata (title, creators, abstract, etc.) — same search as zotero_search_items. tag: filter to items already bearing this tag. When both are given, they are ANDed; pass tag as a list to OR multiple tags. add_tags, remove_tags: list of tag strings (or a JSON-encoded list string). Existing tags are preserved; this is not a replace-all. limit: max items to process (default 50). Attachments are auto-skipped. Requires a writable library (web API key or hybrid mode) — fails in local-only mode. Use zotero_get_tags to discover existing tag names first. Example: zotero_batch_update_tags(tag='to-read', add_tags=['reviewed'], remove_tags=['to-read'], limit=100) — mark everything tagged 'to-read' as 'reviewed'.

zotero_create_collection

Create a new collection (project/folder) in your Zotero library. To create a subcollection, pass parent_collection (not parent_key) as either a collection key (8-character string like 'KMMQDFQ4') or a collection name. Use zotero_search_collections to find collection keys.

zotero_delete_collection

Delete a collection (folder) from your Zotero library by its 8-character key. Items inside the collection are NOT deleted — they remain in the library (and in any other collections they belong to). Subcollections ARE deleted along with the parent. This is a hard delete — Zotero's API does not trash collections, so the operation cannot be undone via the API. Use zotero_search_collections to find the key first. Example: zotero_delete_collection(collection_key="KMMQDFQ4").

zotero_search_collections

Search collections by name in the active library and return their 8-character keys. Matching is case-insensitive substring and applies ONLY to the collection's own name — not to parent names, descriptions, or items inside the collection. Multi-word queries are ANDed across words (NOT OR-ed): query 'reading list' matches only collections whose name contains both 'reading' AND 'list'. To match either word, issue two separate searches. Leading/trailing whitespace is ignored and empty words are dropped. Returns the collection's key plus its parent (if any). include_trashed: when True, also match collections currently in the Zotero Trash (results annotated as such). Default False — trashed collections are otherwise invisible to automated clients. Performance: scans all collections in the active library (O(n)); for very large libraries expect a full-list pagination under the hood. Example: zotero_search_collections(query="orals") → keys for every collection with "orals" in its name.

zotero_manage_collections

Add or remove one or more items from collections. item_keys must be an ARRAY of item keys, e.g. ["KEY1", "KEY2"] — not a single string. add_to and remove_from also accept arrays of collection keys. Use zotero_search_items to find item keys and zotero_search_collections to find collection keys.

zotero_add_by_doi

Add an item to the active Zotero library by DOI, resolving rich metadata (title, creators, journal, year, abstract) from CrossRef. Use this as the FIRST choice when the user gives you a DOI — cleaner metadata than zotero_add_by_url. For arXiv IDs or raw URLs use zotero_add_by_url; for a local PDF use zotero_add_from_file. doi: the DOI string (with or without the '10.' prefix, with or without a leading 'https://doi.org/'). collections: optional list of 8-character collection keys (or collection names — resolved automatically) to file the item under. tags: optional list of tag strings to attach. attach_mode: 'auto' (default) downloads a PDF if CrossRef links one and storage is available; 'none' skips PDF download; 'required' fails if no PDF can be attached. PDF uploads may fail on the Zotero cloud free-tier 300MB quota — metadata still lands even when the upload fails. Requires a writable library (web API key or hybrid mode); fails in local-only mode. Remember to run zotero_update_search_database afterwards to make the new item searchable semantically. Example: zotero_add_by_doi(doi='10.1145/3708319', collections=['9SU943GB'], tags=['MCP']).

zotero_add_by_url

Add an item to the active Zotero library from a URL. Routes by URL shape: doi.org/... → CrossRef metadata (same path as zotero_add_by_doi); arxiv.org/abs/... → arXiv metadata + PDF; anything else → webpage item (title + URL, minimal metadata). Prefer zotero_add_by_doi when you have a clean DOI — it skips the routing and is more robust. For a local file use zotero_add_from_file. url: the URL to import. collections: optional list of 8-character collection keys (or names) to file the item under. tags: optional list of tag strings to attach. attach_mode: 'auto' (default) attaches a PDF if one is available; 'none' skips; 'required' fails if no PDF can be attached. PDF uploads may fail on the Zotero cloud free-tier 300MB quota — metadata still lands even when the upload fails. WARNING: for bibliography use, a general web-page URL produces a 'webpage' itemType that often isn't acceptable as a citation; resolve to a DOI and use zotero_add_by_doi instead when possible. Requires a writable library (fails in local-only mode). Run zotero_update_search_database afterwards for semantic search. Example: zotero_add_by_url(url='https://arxiv.org/abs/2602.14878', collections=['9SU943GB']).

zotero_add_by_isbn

Add a book to your Zotero library by ISBN. Resolves metadata via Open Library (primary) and Google Books (fallback). Accepts ISBN-10, ISBN-13, with or without hyphens, or a URL/isbn: prefix. Response includes the resolver source so you can audit metadata quality.

zotero_update_item

Update metadata on an existing Zotero item by key. Only fields you pass are modified; unspecified fields are left alone. TAG SEMANTICS (easy to get wrong): tags REPLACES the entire tag list. To add tags without touching existing ones, use add_tags. To remove specific tags, use remove_tags. These three are mutually exclusive — prefer add_tags/remove_tags for incremental edits. Similarly, collections/collection_names REPLACE the item's collection memberships (pass collections=[] to clear all memberships); for incremental moves use zotero_manage_collections instead. item_key: 8-character Zotero item key of the item to update. Editable fields include: title, creators, date, publisher, place, publication_title, volume, issue, pages, DOI, ISBN, ISSN, url, language, abstract, short_title, edition, book_title, extra, item_type. To migrate an item across types (e.g., journalArticle → book), pass item_type with a valid Zotero item-type vocabulary value; overlapping fields are preserved and type-specific fields that do not map to the target type are dropped. Requires a writable library (web API key or hybrid mode); fails in local-only mode. To edit notes use zotero_update_note, not this. Example: zotero_update_item(item_key='RTKZQI8E', add_tags=['reviewed'], doi='10.1145/3708319').

zotero_delete_item

Move a Zotero item to the Trash. Works for any item type (book, journalArticle, webpage, attachment, etc.). For notes, use zotero_delete_note — identical mechanism, constrained to notes for safety. Trashed items are recoverable from Zotero's Trash — empty the Trash in the Zotero UI for permanent deletion. By default refuses to trash notes; set allow_note=True to override.

zotero_find_duplicates

Scan the active library (or a single collection) for duplicate items and return candidate groups for review. This tool only IDENTIFIES duplicates — it doesn't merge them. Call zotero_merge_duplicates to actually merge a group. method: 'both' (default) — match on title OR DOI; 'title' — normalized-title match only (lowercase, punctuation-stripped); 'doi' — exact DOI match only (safest for automation). Prefer 'doi' when the user intends to run merge_duplicates unattended. collection_key: optional 8-character key to restrict scanning to one collection; otherwise scans the whole active library. LIBRARY SIZE CAP: refuses to scan a library with > 5,000 items (the whole-library scan is O(n²) on titles) — on larger libraries you MUST pass collection_key to narrow the scope. limit: max groups to return (default 50). Returns a markdown block per group with keys, titles, DOIs, and dateAdded — use this to decide which item to KEEP before calling zotero_merge_duplicates(keeper_key=..., duplicate_keys=[...]). Read-only; works in local or web mode. Example: zotero_find_duplicates(method='doi', limit=20).

zotero_merge_duplicates

Merge one or more duplicate items INTO a keeper: consolidates tags, collections, notes, annotations, and all child items onto the keeper, then moves the duplicates to Trash (recoverable from Zotero desktop's Trash view). SAFETY: dry-run by DEFAULT — prints what would happen without changing anything. Pass confirm=True to actually execute. Always run dry-first at least once to verify the keeper choice. Discover groups first with zotero_find_duplicates. keeper_key: 8-character key of the item to KEEP. All metadata gaps on the keeper are filled from duplicates where possible; conflicting fields keep the keeper's value. duplicate_keys: ARRAY of 8-character item keys to merge into the keeper and trash (also accepts a JSON-encoded list string) — pass as an array, not a single concatenated string. The keeper itself must NOT appear in this list. confirm: False (default) runs dry; True executes the merge. Requires a writable library (web API key or hybrid mode); fails in local-only mode. Example dry-run: zotero_merge_duplicates(keeper_key='ABC12345', duplicate_keys=['XYZ98765']). Example execute: same, plus confirm=True.

zotero_get_pdf_outline

Extract the table of contents (outline/bookmarks) from a PDF attachment, returned as a hierarchical markdown list with each entry's page number. Use this to orient in a paper before calling zotero_get_item_fulltext — the outline is typically < 200 tokens versus 10K+ for the full text. If the PDF has no embedded outline, returns a short 'no outline' message rather than failing. item_key: the PDF ATTACHMENT key OR the parent item key — both are accepted; attachment-to-parent resolution is automatic. Find the right key with zotero_get_item_children if unsure. Scope: PDFs only (EPUBs have no outline extraction here). Requires PyMuPDF (pip install zotero-mcp-server[pdf]). Read-only; works in local or web mode. Example: zotero_get_pdf_outline(item_key='RTKZQI8E').

zotero_add_from_file

Add an item to the active Zotero library from a LOCAL .pdf or .epub file. Attempts to extract the DOI from the file content; if found, enriches metadata via CrossRef (title, creators, journal, year, abstract). If no DOI is found, falls back to best-effort title/author guesses from the filename or document text. Use this when the user has a file on disk but no DOI/URL handy. If you have a DOI use zotero_add_by_doi; for an online URL use zotero_add_by_url. file_path: ABSOLUTE path to a .pdf or .epub file (relative paths fail). Other extensions are rejected. title: optional override if metadata extraction misses. collections: optional list of 8-char keys/names to file under. tags: optional list of tag strings. Requires a writable library (fails in local-only mode). PDF uploads may hit the 300MB Zotero cloud free-tier quota — metadata still lands. Run zotero_update_search_database afterwards for semantic search. Example: zotero_add_from_file(file_path='/Users/me/paper.pdf', collections=['9SU943GB']).

zotero_add_item_relation

Add a related item relationship to a Zotero item. Creates a bidirectional link between two items.

zotero_remove_item_relation

Remove a related item relationship from a Zotero item.

zotero_add_by_bibtex

Add one or more items to Zotero from BibTeX. Provide EITHER bibtex (inline string) OR file_path (absolute path to a .bib / .bibtex file) — not both. Supports multiple @entries per call. The citation key from each entry is preserved in the Extra field. If an entry has a DOI, an open-access PDF attachment is attempted.

zotero_add_by_csl_json

Add one or more items to Zotero from CSL JSON. Provide EITHER csl_json (inline — a JSON string, object, or array) OR file_path (absolute path to a .json / .csljson file) — not both. The id field is preserved in the Extra field as the Citation Key. If an entry has a DOI, an open-access PDF attachment is attempted.

scite_enrich_item

Fetch a Scite.ai citation report for ONE paper: supporting, contrasting, and mentioning citation counts, the total citing-publication count, and any editorial notices (retraction, correction, expression of concern, erratum). Use this to vet a paper's reception before citing it — richer than a bare citation count. Provide EITHER doi OR item_key (not both needed; doi wins if both are passed). If you pass item_key, the tool pulls the DOI from that Zotero item and fails clearly if none is recorded. doi: the DOI string, with or without 'https://doi.org/' prefix — leading prefixes/whitespace are normalized. item_key: 8-character Zotero item key; must have a DOI in metadata or the 'Extra' field to be resolvable. No Scite account or API key required — uses the free public endpoints, so calls can fail transiently: expect 'Could not reach Scite API — try again later' when Scite is slow or unreachable (not a permanent error). For batch enrichment across many items, use scite_enrich_search (search + enrich in one call); for a retraction scan across a collection/tag, use scite_check_retractions. Example: scite_enrich_item(doi='10.1162/tacl_a_00638') or scite_enrich_item(item_key='RTKZQI8E').

scite_enrich_search

Search Zotero and enrich every result with a Scite citation tally (supporting / contrasting / mentioning) plus any retraction or correction notices. Returns the same markdown as zotero_search_items with extra per-item Scite fields. Use this INSTEAD of calling scite_enrich_item N times — it does one batched Scite request, not N. For a plain search with no Scite overhead use zotero_search_items; for a retraction-only scan use scite_check_retractions. query: title/author query — SAME substring semantics as zotero_search_items, so 'Author Year' (e.g. 'Brewer 2011') works best and extra words NARROW (not broaden) the match. limit: max results to enrich (default 10). Items without a DOI in metadata are returned without Scite fields (Scite needs DOIs to resolve). Scope: active Zotero library only (switch with zotero_switch_library). No Scite account or API key required — uses the free public endpoints, so Scite-side enrichment can fail transiently (Zotero results still return, just without Scite fields). Example: scite_enrich_search(query='Cladder-Micus', limit=5).

scite_check_retractions

Scan Zotero items for editorial notices on Scite — retractions, corrections, expressions of concern, erratum/corrigendum. Returns ONLY items flagged with at least one notice; silent clean items are omitted (count reported in the summary line). Use this to vet a reading list before citing. THREE scoping modes (mutually exclusive, first non-null wins): (1) collection — scan all items in a specific Zotero collection; (2) tag — scan items bearing a specific tag; (3) recent (no args) — scan the most-recently-MODIFIED items up to limit. collection: collection name OR 8-char key; names are resolved via zotero_search_collections. tag: existing tag name (exact, case-sensitive). limit: items to check per call — default 50, max 500. Items without a DOI are skipped silently (Scite needs DOIs). Scope: active library only. No Scite account or API key needed; the public endpoints can fail transiently — on network errors the tool returns 'Could not reach Scite API — try again later' rather than partial results. For a single-paper check prefer scite_enrich_item (richer output, also includes notices). Example: scite_check_retractions(tag='to-cite', limit=100) or scite_check_retractions(collection='Orals', limit=500).

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/54yyyu/zotero-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server