zotero-mcp-server
Provides tools for searching, reading, and writing items, collections, tags, and notes in a Zotero library via the Zotero Web API v3.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zotero-mcp-serverFind recent articles in my Zotero tagged 'cybersecurity'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
zotero-mcp-server
An MCP (Model Context Protocol) server for the Zotero Web API v3 — search, read, and write items, collections, tags, and notes in a Zotero library.
Built for use with Claude (or any MCP client), aimed at literature-review workflows: pulling existing references into a "registro maestro," filing newly-found sources back into Zotero, tagging items by theoretical line, and attaching notes.
Tools
Tool | Read/Write | Description |
| Read | Quick-search items by text, type, tag, or collection |
| Read | Full metadata for one item, optionally with a formatted citation |
| Read | Notes/attachments of an item |
| Read | Full text of a PDF/HTML attachment — indexed Zotero text first, local PDF extraction as fallback. Paginated for long documents |
| Read | Search within notes and PDF annotations/highlights, not the bibliographic items |
| Read | List collections, optionally scoped to a parent |
| Read | List tags in use, with item counts |
| Read | Formatted bibliography (APA, Chicago, MLA, etc.) for up to 50 items in one call |
| Write | Create a new bibliographic item |
| Write | Attach a note to an existing item |
| Write | Add/remove tags on an item (version-checked) |
| Write | Create a new collection/subcollection |
This is a focused set covering the core research workflow, not full API coverage. Not included (would need to be added if you need them): saved searches, file uploads, full item updates beyond tags, deletion, group-membership management.
Deep reading: zotero_get_fulltext
This is what lets Claude read an actual PDF's argument, not just its title and abstract. Resolution is automatic:
Pass either a parent item's key or an attachment's key directly.
Zotero's own server-indexed full text is tried first (fast — works when Zotero desktop has already synced and indexed the PDF).
If not indexed, the server downloads the attachment and extracts text locally with a PDF parser.
Long documents are paginated via
char_offset/char_limit— call again with a higher offset to keep reading.
Limitation: scanned/image-only PDFs without OCR text return little or garbled text. The tool detects this (flags a warning when extracted text is too short for the page count) rather than silently returning garbage, but it cannot OCR a document itself.
Related MCP server: zotero-mcp
1. Get a Zotero API key
Go to https://www.zotero.org/settings/keys and create a new private key.
Grant it read/write access to the library you want to use (your personal library, and/or specific groups).
Note your numeric user ID, shown on the same page — this is not your username.
For a group library, the group ID is the number in the group's URL, or from
GET https://api.zotero.org/users/<userID>/groups.
2. Configure environment variables
Variable | Required | Description |
| Yes | The key generated above |
| Yes |
|
| Yes | Numeric user ID or group ID |
| No | Override for the local Zotero desktop API ( |
| No |
|
| No | Port for |
Never commit these values or hardcode them in source — set them as environment variables or secrets in whatever you use to run the server.
3. Build
npm install
npm run build4. Run
Locally, over stdio (for local MCP clients, e.g. Claude Desktop's local server config):
ZOTERO_API_KEY=... ZOTERO_LIBRARY_TYPE=user ZOTERO_LIBRARY_ID=... node dist/index.jsAs a remote server, over Streamable HTTP (needed to add it as a custom connector in claude.ai):
TRANSPORT=http PORT=3000 \
ZOTERO_API_KEY=... ZOTERO_LIBRARY_TYPE=user ZOTERO_LIBRARY_ID=... \
node dist/index.jsThis exposes POST /mcp (the MCP endpoint) and GET /health (a plain liveness check). Deploy it somewhere reachable over HTTPS (Render, Fly.io, Railway, a small VM behind a reverse proxy with TLS, etc.) — claude.ai's custom connector setup needs a public https:// URL, not localhost.
A Dockerfile is included, so any of the platforms below can build and run it with no extra configuration beyond setting environment variables.
Deploying to a public HTTPS endpoint
Pick one. Render is the easiest if you don't mind pushing to GitHub; Fly.io is the easiest if you'd rather stay in a terminal.
Option A — Render (dashboard, needs a GitHub repo)
Push this project (this whole folder) to a new GitHub repository.
In the Render dashboard, click New → Web Service and connect that repository.
Render will detect the
Dockerfileautomatically — leave Environment as Docker. Leave build/start commands blank (the Dockerfile handles both).Under Environment Variables, add:
ZOTERO_API_KEYZOTERO_LIBRARY_TYPE(userorgroup)ZOTERO_LIBRARY_ID(
TRANSPORT=httpis already set inside the Dockerfile — no need to add it.)
Click Create Web Service. Render builds the image and deploys it; this takes a few minutes the first time.
Once live, Render shows a URL like
https://zotero-mcp-server.onrender.com. Your MCP endpoint ishttps://zotero-mcp-server.onrender.com/mcp.Sanity check:
curl https://zotero-mcp-server.onrender.com/healthshould return{"status":"ok"}.
Free-tier note: Render's free web services sleep after inactivity and take a few seconds to wake on the next request — fine for testing, worth upgrading if you'll use this daily.
Option B — Fly.io (CLI, no GitHub needed)
Install the CLI:
curl -L https://fly.io/install.sh | sh(or see fly.io/docs/flyctl).fly auth loginFrom inside the
zotero-mcp-serverfolder:fly launchIt detects the
Dockerfileand proposes an app name and region — accept or edit.Say no to adding a Postgres/Redis database (not needed).
Say no to deploying immediately if it asks — set secrets first (next step).
Set your credentials as secrets (never as plain fly.toml values):
fly secrets set ZOTERO_API_KEY=your_key ZOTERO_LIBRARY_TYPE=user ZOTERO_LIBRARY_ID=your_idOpen the generated
fly.tomland confirminternal_port = 3000under[http_service](it should be auto-detected from the Dockerfile'sEXPOSE 3000; fix it manually if not).Deploy:
fly deployYour MCP endpoint is
https://<your-app-name>.fly.dev/mcp. Checkhttps://<your-app-name>.fly.dev/health.
Option C — Railway (CLI or dashboard)
Install the CLI (
npm install -g @railway/cli) or use the Railway dashboard connected to a GitHub repo — same idea as Render.CLI path:
railway login, then from the project folderrailway initandrailway up. Railway detects theDockerfileautomatically.Set env vars:
railway variables set ZOTERO_API_KEY=... ZOTERO_LIBRARY_TYPE=user ZOTERO_LIBRARY_ID=...(or via the dashboard's Variables tab).Railway services aren't public by default — go to Settings → Networking → Generate Domain to get a public
https://your-app.up.railway.appURL.MCP endpoint:
https://your-app.up.railway.app/mcp.
Option D — Self-hosted VM (full control, more steps)
Provision a small Ubuntu 22.04+ VM (DigitalOcean, Linode, a spare EC2 instance, etc.) and point a DNS A record at its IP, e.g.
zotero-mcp.yourdomain.com.SSH in and install Node 20 and Caddy (Caddy handles HTTPS automatically via Let's Encrypt — much less setup than nginx + certbot):
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - sudo apt-get install -y nodejs sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt-get update && sudo apt-get install -y caddyCopy the project to the VM (
scpthe zip, thenunzip) and build:cd zotero-mcp-server && npm install && npm run buildCreate
/etc/systemd/system/zotero-mcp.service:[Unit] Description=Zotero MCP server After=network.target [Service] Environment=TRANSPORT=http Environment=PORT=3000 Environment=ZOTERO_API_KEY=your_key Environment=ZOTERO_LIBRARY_TYPE=user Environment=ZOTERO_LIBRARY_ID=your_id ExecStart=/usr/bin/node /home/youruser/zotero-mcp-server/dist/index.js Restart=always User=youruser [Install] WantedBy=multi-user.targetThen:
sudo systemctl daemon-reload && sudo systemctl enable --now zotero-mcpPoint Caddy at it — edit
/etc/caddy/Caddyfile:zotero-mcp.yourdomain.com { reverse_proxy localhost:3000 }Then:
sudo systemctl reload caddy. Caddy fetches a TLS certificate automatically on first request.Check
https://zotero-mcp.yourdomain.com/health. MCP endpoint:https://zotero-mcp.yourdomain.com/mcp.
Actualizar un despliegue ya existente
Ya tienes esto corriendo en Render con un repositorio en GitHub — actualizar es mucho más simple que el despliegue inicial, no hay que tocar Render para nada:
Descomprime el nuevo
.zipen tu computadora, igual que la primera vez.Ve a tu repositorio en GitHub (
zotero-mcp-server-v2o el nombre que le hayas puesto).Add file → Upload files, y arrastra TODO el contenido de la carpeta descomprimida — igual que la vez pasada. GitHub reemplaza automáticamente los archivos que ya existían (por ejemplo
package.json,src/index.ts) y agrega los nuevos (src/tools/fulltext.ts).Commit changes.
Eso es todo — Render detecta el cambio en GitHub y vuelve a desplegar solo, normalmente en 3-8 minutos. No hace falta volver a tocar las variables de entorno ni ninguna otra configuración de Render.
Verifica en el panel de Render que el nuevo deploy diga "Live" antes de probar las herramientas nuevas.
5. Connect it to Claude
Once deployed and reachable over HTTPS:
In claude.ai, go to Settings → Connectors → Add custom connector.
Enter the deployed URL, e.g.
https://your-deployment.example.com/mcp.Claude will discover the 9 tools above automatically.
For Claude Desktop with a local stdio server instead, add an entry to its MCP server config pointing at node /absolute/path/to/dist/index.js, with the environment variables from step 2 set in that config.
Testing
npx @modelcontextprotocol/inspector node dist/index.jsThis opens a local UI to call each tool by hand before wiring it up to Claude.
Notes on the Zotero API this server relies on
Auth:
Zotero-API-Keyheader, per request.Versioning for writes:
zotero_update_item_tagsreads the item's currentversionbefore patching, and sends it viaIf-Unmodified-Since-Version— if the item changed elsewhere in the meantime, Zotero returns412and the tool reports it clearly instead of silently overwriting.Rate limits: Zotero may return
429with aRetry-Afterheader, or aBackoffheader on any response. This server surfaces both as actionable error text; it does not currently auto-retry.Item creation fetches the field template for the requested
itemTypefromGET /items/newfirst, so only valid fields for that type are sent.
Official API docs: https://www.zotero.org/support/dev/web_api/v3/
Available Tools
9 toolszotero_add_noteAdd Note to Zotero ItemA
Attach a child note to an existing item.
This tool WRITES to the library and requires an API key with write access.
Args:
parent_item_key (string): 8-character key of the item to attach the note to
note_text (string): note content — plain text or basic HTML
tags (string array, optional): tags for the note itself
Returns: the key of the newly created note.
Examples:
Use when: "add a note to item ABCD1234 saying it belongs to the 'resistencia cotidiana' line" -> parent_item_key="ABCD1234", note_text="Línea teórica: resistencia cotidiana."
Don't use when: you want to change the item's own fields (use zotero_update_item_tags for tags, or edit directly in Zotero for other fields)
Error Handling:
Returns "Error: Not found (404)" if parent_item_key doesn't exist
Returns "Error: Permission denied (403)" if the API key lacks write access
| Name | Required | Description | Default |
|---|---|---|---|
| tags | No | Tags to attach to the note itself (independent of the parent item's tags). | |
| note_text | Yes | Note content. Plain text is accepted and wrapped in a paragraph; basic HTML (<p>, <b>, <i>, <ul>/<li>) is also accepted as-is. | |
| parent_item_key | Yes | 8-character key of the item this note should be attached to. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that the tool writes to the library and requires write-access API key, which complements the readOnlyHint=false annotation. It also explains return value (new note key) and error cases (404/403), adding meaningful behavioral context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (purpose, write notice, args, returns, examples, error handling) and is front-loaded with the main purpose. It is somewhat lengthy and repeats parameter details already present in the schema, but every section serves a useful purpose and there is no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (3 params, 1 optional) and no output schema, the description fully covers invocation context: when to use, permissions, return value, errors, and examples. It also clarifies distinction from siblings, making it self-sufficient for an agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description covers 100% of parameters, so baseline is 3. The description adds value through a concrete example mapping natural language to arguments (parent_item_key='ABCD1234', note_text='...') and clarifies the purpose of each parameter. However, it largely repeats schema descriptions rather than adding new semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Attach a child note to an existing item,' which clearly states the action ('attach'), the object ('child note'), and the target ('existing item'). It distinguishes from siblings by explicitly saying not to use it when modifying the item's own fields, directing to zotero_update_item_tags for tags.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit 'Use when' and 'Don't use when' guidance, including an example with concrete arguments and a clear alternative tool (zotero_update_item_tags). This exceeds the minimum by giving both positive and negative usage criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_create_collectionCreate Zotero CollectionA
Create a new collection (folder), optionally as a subcollection of an existing one.
This tool WRITES to the library and requires an API key with write access.
Args:
name (string): collection name, 1-255 characters
parent_collection_key (string, optional): parent collection key, for a subcollection
Returns: the key of the newly created collection.
Examples:
Use when: "create a collection called 'Estado del arte - Resistencia'" -> name="Estado del arte - Resistencia"
Don't use when: the collection may already exist — check with zotero_list_collections first to avoid duplicates
Error Handling:
Returns "Error: Permission denied (403)" if the API key lacks write access
Returns "Error: Library locked (409)" if a sync is in progress — retry shortly
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name of the new collection. | |
| parent_collection_key | No | 8-character key of a parent collection, to create this as a subcollection. Omit for a top-level collection. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate write (readOnlyHint=false), but the description adds context by stating it requires API key write access, mentions error codes (403, 409), and notes the operation is not idempotent. It does not contradict annotations and provides practical behavioral details beyond basic flags.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with sections for args, examples, and error handling. It is informative without being excessively long. Minor redundancy exists with the schema parameter descriptions, but overall every sentence contributes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is a creation operation with no output schema. The description covers return value, auth prerequisites, duplicate-prevention guidance, and common errors. This is complete enough for an agent to use it effectively, especially given the rich annotations and full schema coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the description essentially repeats the parameter meanings (name, parent_collection_key). It adds a usage example but no additional semantic depth beyond the schema. Baseline 3 is appropriate given full schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states this tool creates a new collection (folder) and optionally as a subcollection. It uses a specific verb (create) and resource (collection), and distinguishes itself from siblings like zotero_list_collections and zotero_create_item.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage guidance with an example ('Use when: create a collection called...') and an explicit exclusion with alternative ('Don't use when: the collection may already exist — check with zotero_list_collections first'). This is exactly the kind of when/when-not guidance expected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_create_itemCreate Zotero ItemA
Create a new bibliographic item (article, book, webpage, report, etc.) in the library.
This tool WRITES to the library and requires an API key with write access. It fetches the correct field template for item_type from Zotero before submitting, so only the fields you provide are set — you don't need to know the full schema for every item type.
Args:
item_type (string): e.g. 'journalArticle', 'book', 'thesis', 'report', 'webpage'
title (string): required
creators (array, optional): [{creator_type, first_name, last_name}] or [{creator_type, name}] for institutional authors
date, abstract_note, url, doi, publication_title, publisher, place, language, extra (all optional strings)
tags (string array, optional)
collection_keys (string array, optional): file the item into one or more existing collections
Returns: the key of the newly created item.
Examples:
Use when: "add this article to my library: Hollander & Einwohner (2004), 'Conceptualizing Resistance', Sociological Forum" -> item_type="journalArticle", title="Conceptualizing Resistance", creators=[{creator_type:"author", first_name:"Jocelyn A.", last_name:"Hollander"}, {creator_type:"author", first_name:"Rachel L.", last_name:"Einwohner"}], publication_title="Sociological Forum", date="2004"
Don't use when: the item may already be in the library — search first with zotero_search_items to avoid duplicates
Error Handling:
Returns "Error: Permission denied (403)" if the API key lacks write access
Returns "Error: Zotero rejected the item" with the specific field/type problem if item_type is invalid
| Name | Required | Description | Default |
|---|---|---|---|
| doi | No | DOI, if applicable. | |
| url | No | URL of the item, if applicable. | |
| date | No | Publication date, free text or ISO format (e.g. '2023', '2023-05-14'). | |
| tags | No | Tags to attach to the new item, e.g. ['linea-resistencia-cotidiana', 'putumayo']. | |
| extra | No | Free-text notes field, useful for anything not covered by a dedicated field (e.g. verification notes). | |
| place | No | Place of publication. | |
| title | Yes | Title of the item. | |
| creators | No | Authors/editors/etc., in order. See creator_type for available roles per item type. | |
| language | No | Language code or name, e.g. 'es', 'en'. | |
| item_type | Yes | Zotero item type, e.g. 'journalArticle', 'book', 'bookSection', 'thesis', 'report', 'webpage', 'document'. See GET /itemTypes for the full list. | |
| publisher | No | Publisher name. | |
| abstract_note | No | Abstract or summary text. | |
| collection_keys | No | 8-character keys of collections to file this item into. Omit to leave it uncategorized. | |
| publication_title | No | Journal, book, or conference proceedings title (for articles/book sections). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations by disclosing that the tool 'WRITES to the library and requires an API key with write access.' It also reveals template-fetching behavior ('fetches the correct field template for item_type'), specifies error messages for permission and invalid item_type, and states the return value (the key of the newly created item). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (Args, Returns, Examples, Error Handling) and the purpose statement is front-loaded. Though the Args list somewhat duplicates the schema, it is compact and all content is relevant; nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter, nested-creator creation tool with no output schema, the description covers prerequisites, template-fetching behavior, all parameters, the return key, examples, exclusions (search first), and error handling. It is complete for an agent to select and invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by summarizing creators structure (e.g., 'institutional authors' with 'name') and collection_keys usage, plus a concrete example mapping natural language to parameter values, which clarifies how to populate the fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb and resource: 'Create a new bibliographic item (article, book, webpage, report, etc.) in the library.' It distinguishes itself from siblings like zotero_add_note (notes) and zotero_search_items (searching) by focusing specifically on creating new bibliographic items.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides use cases: 'Use when: "add this article to my library..."' and exclusion: 'Don't use when: the item may already be in the library — search first with zotero_search_items to avoid duplicates.' Also states the prerequisite of an API key with write access, giving clear when-to-use vs. alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_get_itemGet Zotero ItemARead-onlyIdempotent
Retrieve full metadata for a single item by its key, optionally including a formatted bibliography entry.
Args:
item_key (string): 8-character Zotero item key
include_bibliography (boolean): also return a formatted reference (default: false)
citation_style (string): CSL style name for the bibliography, e.g. 'apa' (default: 'apa')
response_format ('markdown' | 'json')
Returns: full item data (type, creators, title, date, DOI/URL, abstract, tags, collections, notes count), plus a formatted bibliography entry if requested.
Examples:
Use when: "get the full record for item X42A7DEE" -> item_key="X42A7DEE"
Use when: "give me an APA citation for this item" -> include_bibliography=true, citation_style="apa"
Don't use when: searching by title/author (use zotero_search_items instead)
Error Handling:
Returns "Error: Not found (404)" if the item key doesn't exist in this library
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes | The 8-character Zotero item key (e.g. 'X42A7DEE'). | |
| citation_style | No | CSL citation style to use when include_bibliography is true, e.g. 'apa', 'chicago-note-bibliography', 'modern-language-association' (default: 'apa'). | apa |
| response_format | No | Output format: 'markdown' for human-readable text or 'json' for machine-readable structured data. | markdown |
| include_bibliography | No | If true, also return a formatted bibliography entry for the item (default: false). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is clear. The description adds value by disclosing error behavior ('Returns "Error: Not found (404)"') and describing the contents of the returned data ('type, creators, title, date, DOI/URL, abstract, tags, collections, notes count'), which is not covered by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (Args, Returns, Examples, Error Handling) and is front-loaded with the core purpose. Every sentence earns its place, and examples are compact and illustrative without being verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description explicitly lists the fields returned and the optional bibliography behavior. It also covers error cases and points to the correct alternative tool. This makes it fully self-contained for an agent to decide when and how to call the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description goes beyond the schema by providing usage examples that tie parameters to intents (e.g., 'give me an APA citation' -> include_bibliography=true, citation_style='apa') and clarifying the effect of response_format. This adds meaningful context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Retrieve full metadata') and clearly identifies the resource ('a single item by its key'). It explicitly differentiates itself from sibling tools by stating 'Don't use when: searching by title/author (use zotero_search_items instead)'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use examples ('get the full record for item X42A7DEE', 'give me an APA citation') and an explicit exclusion with an alternative tool ('Don't use when: searching by title/author (use zotero_search_items instead)'). This is textbook usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_get_item_childrenGet Zotero Item ChildrenARead-onlyIdempotent
List the child items (notes and file attachments) of a parent item.
Args:
item_key (string): 8-character Zotero key of the PARENT item
only_notes (boolean): return only notes, excluding attachments (default: false)
limit (number): 1-100 (default: 25)
offset (number): pagination offset (default: 0)
response_format ('markdown' | 'json')
Returns: child items with their type (note/attachment), and for notes, the note text.
Examples:
Use when: "does this item have any notes attached?" -> item_key=, only_notes=true
Don't use when: you want top-level library items (use zotero_search_items instead)
Error Handling:
Returns "Error: Not found (404)" if the parent item key doesn't exist
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return, 1-100 (default: 25). | |
| offset | No | Number of results to skip, for pagination (default: 0). | |
| item_key | Yes | The 8-character Zotero key of the parent item. | |
| only_notes | No | If true, return only child notes (exclude attachments) (default: false). | |
| response_format | No | Output format: 'markdown' for human-readable text or 'json' for machine-readable structured data. | markdown |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnly and idempotent, but the description goes further by detailing the return shape (child item types and note text), pagination behavior via offset, and the exact 404 error format, which is valuable beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-organized with headers, bullet-style args, examples, and error handling. Each section is purposeful and front-loaded, with no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 parameters, no output schema, and good annotations, the description covers purpose, usage, parameter meanings, return values, pagination, and error cases—leaving no significant gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description reinforces the key intent of item_key as the PARENT key and gives practical examples (e.g., only_notes=true for 'does this item have any notes?'), adding a small semantic layer beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb ('List') and precise resource ('child items (notes and file attachments) of a parent item'), immediately distinguishing it from sibling tools like zotero_search_items (top-level library items) and zotero_get_item (single item).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'Use when' and 'Don't use when' examples, naming the alternative zotero_search_items for top-level searches, and also includes error handling guidance for invalid item keys.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_list_collectionsList Zotero CollectionsARead-onlyIdempotent
List the collections (folders) in a Zotero library, optionally scoped to one parent collection.
Args:
top_level_only (boolean): only top-level collections (default: false)
parent_collection_key (string, optional): list subcollections of this collection instead of the whole library
limit (number): 1-100 (default: 25)
offset (number): pagination offset (default: 0)
response_format ('markdown' | 'json')
Returns: collections with their key, name, parent, and item count.
Examples:
Use when: "what collections do I have?" -> top_level_only=true
Use when: "list the subfolders inside my 'Putumayo' collection" -> parent_collection_key=
Don't use when: you want the items inside a collection (use zotero_search_items with collection_key instead)
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return, 1-100 (default: 25). | |
| offset | No | Number of results to skip, for pagination (default: 0). | |
| top_level_only | No | If true, return only top-level collections (exclude subcollections) (default: false). | |
| response_format | No | Output format: 'markdown' for human-readable text or 'json' for machine-readable structured data. | markdown |
| parent_collection_key | No | If set, list only the subcollections of this collection key instead of the whole library. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already disclose readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the agent knows this is a safe, read-only operation. The description adds valuable behavioral context by specifying the return structure ('collections with their key, name, parent, and item count') and explaining scoping behavior with parent_collection_key. This exceeds what annotations alone convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose, args, returns, and examples. The first sentence is front-loaded with the core purpose. However, the args list largely duplicates schema information that is already exhaustive, adding mild redundancy. Still, the overall length is reasonable and scannable, so it earns a 4 rather than a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a list operation with no output schema, the description is remarkably complete. It covers the core behavior, optional scoping, return fields, parameter usage, and exclusions. The examples give concrete context, and the explicit pointer to zotero_search_items for a different use case ties it into the sibling tool ecosystem. There are no significant gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage and detailed descriptions for all five parameters, so the baseline is 3. The description goes beyond the schema by adding example-driven semantics: 'what collections do I have?' -> top_level_only=true and 'list the subfolders inside my Putumayo collection' -> parent_collection_key. This helps an agent map natural language to parameter values, which is not in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb and resource: 'List the collections (folders) in a Zotero library, optionally scoped to one parent collection.' It distinguishes itself from the sibling tool zotero_search_items in the 'Don't use when' example, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance with concrete use cases: 'what collections do I have?' and 'list the subfolders inside my Putumayo collection.' It also gives a clear exclusion: 'Don't use when: you want the items inside a collection (use zotero_search_items with collection_key instead).' This is exactly the level of guidance expected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_list_tagsList Zotero TagsARead-onlyIdempotent
List the tags used in a Zotero library, with the count of items carrying each tag.
Useful for discovering what tag vocabulary is already in use before tagging new items (e.g. for the "línea teórica" or "línea de tiempo" categories in a state-of-the-art review), or for checking whether a specific tag already exists.
Args:
name_filter (string, optional): substring (or prefix, with starts_with) to match against tag names
starts_with (boolean): match only the start of tag names (default: false)
collection_key (string, optional): restrict to tags used within one collection
limit (number): 1-100 (default: 25)
offset (number): pagination offset (default: 0)
response_format ('markdown' | 'json')
Returns: tag names with the number of items carrying each one.
Examples:
Use when: "what tags start with 'linea-'?" -> name_filter="linea-", starts_with=true
Use when: "list all tags used in my Putumayo collection" -> collection_key=
Don't use when: you want the items with a specific tag (use zotero_search_items with tag= instead)
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return, 1-100 (default: 25). | |
| offset | No | Number of results to skip, for pagination (default: 0). | |
| name_filter | No | Only return tags whose name contains (or, with starts_with, begins with) this text. | |
| starts_with | No | If true, name_filter matches the start of the tag name instead of anywhere within it (default: false). | |
| collection_key | No | Restrict to tags used within this collection. Omit for the whole library. | |
| response_format | No | Output format: 'markdown' for human-readable text or 'json' for machine-readable structured data. | markdown |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds context beyond annotations by explaining the count-per-tag behavior, filtering/pagination, and response_format options. It does not contradict annotations, and the added behavioral details are useful but not extensive (e.g., no performance or rate-limit notes).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded: purpose paragraph, usage guidance, Args block, Returns, then examples. Although the Args section repeats schema, each section earns its place by adding context. The 'Don't use' note is particularly valuable. Length is appropriate for 6 optional parameters.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only list tool with 6 optional params, no output schema, and no nested objects, the description fully covers parameter semantics, return format, filtering options, and exclusion criteria. The examples demonstrate realistic invocation patterns, making it complete for an AI agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description's Args block largely mirrors the schema, but it enhances semantics with usage examples: 'name_filter="linea-", starts_with=true' and 'collection_key=<key>'. These examples clarify how parameters interact, going beyond the schema's standalone descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'List the tags used in a Zotero library, with the count of items carrying each tag' – a specific verb, resource, and output. It explicitly distinguishes from siblings in the 'Don't use when' section, naming zotero_search_items as the alternative for fetching items by tag.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear when-to-use scenarios: 'before tagging new items... or for checking whether a specific tag already exists.' It includes concrete 'Use when' examples (e.g., tags starting with 'linea-') and an explicit 'Don't use when' with a pointer to the correct sibling tool, making selection unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_search_itemsSearch Zotero ItemsARead-onlyIdempotent
Search for bibliographic items (articles, books, webpages, etc.) in a Zotero library.
This is a READ-ONLY quick search. It matches titles and creator names by default (query_mode='titleCreatorYear'); pass query_mode='everything' to also search full text of PDF attachments. Combine with item_type and/or tag filters to narrow results, and collection_key to search inside one collection only.
Args:
query (string, optional): Quick-search text
query_mode ('titleCreatorYear' | 'everything'): search scope (default: 'titleCreatorYear')
item_type (string, optional): Zotero item type filter, supports '||' (OR) and leading '-' (NOT)
tag (string, optional): tag filter, supports the same boolean syntax
collection_key (string, optional): restrict to one collection
include_trashed (boolean): include trashed items (default: false)
limit (number): 1-100 (default: 25)
offset (number): pagination offset (default: 0)
response_format ('markdown' | 'json')
Returns: matching items with type, creators, year, title, DOI/URL, tags, and a trimmed abstract.
Examples:
Use when: "find items about resistencia cotidiana in my library" -> query="resistencia cotidiana"
Use when: "list all journal articles tagged putumayo" -> item_type="journalArticle", tag="putumayo"
Don't use when: you need items from a specific known key (use zotero_get_item instead)
Error Handling:
Returns "Error: Permission denied (403)" if the API key lacks access to this library
Returns "No items found matching the given criteria" if the search is empty
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Filter by tag using Zotero's boolean search syntax, e.g. 'putumayo', 'putumayo && resistencia' (space = AND within one param, or repeat the param for AND). | |
| limit | No | Maximum number of results to return, 1-100 (default: 25). | |
| query | No | Quick-search text, matched against titles and creator names by default (Zotero 'q' parameter). | |
| offset | No | Number of results to skip, for pagination (default: 0). | |
| item_type | No | Filter by item type using Zotero's boolean search syntax, e.g. 'book', 'journalArticle', 'book || journalArticle' (OR), '-attachment' (NOT). | |
| query_mode | No | 'titleCreatorYear' (default) searches titles/creators/year only; 'everything' also searches full text of attachments. | titleCreatorYear |
| collection_key | No | Restrict the search to items within this collection (8-character Zotero collection key). Omit to search the whole library. | |
| include_trashed | No | Include items in the trash (default: false). | |
| response_format | No | Output format: 'markdown' for human-readable text or 'json' for machine-readable structured data. | markdown |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly labels the tool as 'READ-ONLY', consistent with the readOnlyHint annotation, but adds substantial behavioral context beyond the annotations: default query mode, the effect of query_mode='everything', combination of filters, collection restriction, trashed items behavior, response_format options, and error handling (e.g., 'Error: Permission denied (403)'). This gives the agent a rich understanding of what to expect.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (overview, args, examples, error handling) and is front-loaded with the core purpose. While it repeats parameter details already in the schema, each sentence serves a purpose (usage guidance or behavioral notes). It is slightly long but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 9 optional parameters and no output schema, the description covers all needed context: what it searches, how to filter, what it returns (type, creators, year, title, DOI/URL, tags, trimmed abstract), and error conditions. It also provides examples and distinguishes from sibling tools. This is a complete standalone description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description's Args section largely mirrors schema descriptions, but it adds value by showing how parameters combine (e.g., 'Combine with item_type and/or tag filters to narrow results') and giving concrete examples for query, item_type, and tag. It also clarifies the default for query_mode and response_format, which the schema already states, but the cohesive usage guidance justifies a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Search for bibliographic items (articles, books, webpages, etc.) in a Zotero library.' It also differentiates from siblings by explicitly saying 'Don't use when: you need items from a specific known key (use zotero_get_item instead).' This is a clear, unambiguous purpose with sibling distinction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit 'Use when' and 'Don't use when' guidance with concrete examples, such as 'find items about resistencia cotidiana' and 'list all journal articles tagged putumayo'. It also names the alternative tool (zotero_get_item) for the excluded case, giving the agent clear decision rules.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
zotero_update_item_tagsUpdate Zotero Item TagsAIdempotent
Add and/or remove tags on an existing item, leaving all other fields untouched.
This tool WRITES to the library and requires an API key with write access. It reads the item's current version first and sends a version-checked PATCH, so it will report a clear conflict error rather than silently overwriting a concurrent change.
Args:
item_key (string): 8-character key of the item to update
add_tags (string array): tags to add
remove_tags (string array): tags to remove
Returns: the item's updated tag list.
Examples:
Use when: "tag item ABCD1234 as verified and remove the 'pendiente' tag" -> item_key="ABCD1234", add_tags=["verificado"], remove_tags=["pendiente"]
Don't use when: you're creating a new item (use zotero_create_item, which accepts tags directly)
Error Handling:
Returns "Error: Version conflict (412)" if the item changed since being read — retry the call, which re-fetches the current version each time
Returns "Error: Not found (404)" if item_key doesn't exist
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate readOnlyHint=false and destructiveHint=false, but the description goes further by stating it requires an API key with write access, reads the current version first, sends a version-checked PATCH, and will raise a clear conflict error rather than silently overwrite. It also discloses that it returns the updated tag list. These are significant behavioral details beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (Args, Returns, Examples, Error Handling) and front-loads the core purpose. It is somewhat long but every sentence adds value, including the error handling details and the example use case. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the 'Returns' section is essential and included ('the item's updated tag list'). The description also covers error handling (412 and 404), authentication requirements, the exact parameter format, and usage boundaries relative to sibling tools. It is fully self-contained enough for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is empty (parameter count 0, 0% coverage in schema), so the description carries the full burden for parameter semantics. It lists all three args (item_key, add_tags, remove_tags) with types and brief descriptions, and provides an example mapping values to the parameters. This fully compensates for the missing schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Add and/or remove tags on an existing item, leaving all other fields untouched.' This specifies the verb (add/remove), the resource (tags on an existing item), and a key distinction (does not touch other fields). It also differentiates from sibling tools by explicitly noting it is for existing items, not creating new ones.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'Use when' and 'Don't use when' guidance with a concrete example and names the alternative tool (zotero_create_item). This tells the agent exactly when to invoke this tool versus its siblings, making usage unmistakable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
9 tool updates
v1.0.0- First observed
zotero_add_note - First observed
zotero_create_collection - First observed
zotero_create_item - First observed
zotero_get_item - First observed
zotero_get_item_children - First observed
zotero_list_collections - First observed
zotero_list_tags - First observed
zotero_search_items - First observed
zotero_update_item_tags
TDQS
Each tool targets a distinct resource and operation: search vs. fetch by key, items vs. children, collections vs. tags, creating items vs. notes vs. collections. The descriptions explicitly cross-reference each other to prevent misselection.
All tools follow a consistent `zotero_<verb>_<noun>` pattern with snake_case verbs. The only minor deviation is `zotero_get_item_children`, which uses a compound noun, but it's still clear and predictable.
Nine tools cover the essential read and write operations for a Zotero library without bloat. The count is well within the ideal range for a domain-specific server.
The server covers search, retrieval, collection management, item creation, note attachment, and tag updates, but lacks update/delete operations for items and collections. There is no way to edit item metadata, remove items, or reorganize collection membership, which are notable gaps for full lifecycle management.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Remote MCP server for full read/write access to a Zotero library
MCP server for Altmetric APIs - track research attention across news, policy, social media, and more
Auditable MCP server for PubMed, Europe PMC, ClinicalTrials.gov, and bioRxiv/medRxiv queries
Personal knowledge base MCP server with semantic search, auto-categorization, metadata extraction
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server that lets AI assistants search, create, organize, and cite from a Zotero library.393MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for interacting with a Zotero library via the local API. Enables searching, retrieving, creating, updating, and deleting Zotero items, managing collections and tags, and generating citations.-
- AlicenseNot gradedqualityCmaintenanceMCP server that connects to a local Zotero library, enabling search, citation generation with CSL styles, and automatic bibliography updates in Markdown documents.109MIT
- AlicenseNot gradedqualityBmaintenanceLocal read-only MCP server for Zotero libraries, enabling search, retrieval, and full-text access via the Zotero Web API.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/andresrocha99/zotero-mcp-server-v2'
If you have feedback or need assistance with the MCP directory API, please join our Discord server