mcp-zotero
A Zotero integration server that lets any LLM browse, search, organize, and modify your Zotero library and produce cited Word documents.
Browse & search: list collections (incl. optionally trashed ones), list collection items, search the library by query or list items sorted by field, and batch-retrieve item metadata (with optional abstracts).
Read content: get the full-text of an item's indexed PDF attachment, capped by
max_characters.Add items: add papers by DOI with automatic metadata resolution (plus optional OA PDF attachment via Unpaywall), or add items with direct metadata across all 37 Zotero item types in batches.
Organize: create collections, optionally nested under a parent, and add items into collections.
Attachments: attach a linked URL to an existing item or as a standalone, import a PDF from a URL into Zotero storage (auto-indexed), and batch-find/attach open-access PDFs via Unpaywall (with dry-run).
Cite in Word: replace
<zcite>placeholders in a.docxwith native Zotero field codes and a bibliography, in APA, IEEE, Vancouver, Harvard, or Chicago style (outputs a_citedfile).Delete (gated): permanently delete up to 50 items per call or delete a collection (items kept), only if
UNSAFE_OPERATIONSpermits.Environment info: return the configured user ID, library type/ID, and library path.
Multi-library targeting: every tool accepts per-call
library_type/library_idoverrides to reach user or group libraries without restarting.
Provides tools for searching, organizing, and adding items to a Zotero library by DOI or metadata, managing collections, importing PDFs with full-text indexing, and injecting citations into Word documents.
Click on "Deploy 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., "@mcp-zoterosearch my Zotero library for papers on deep learning"
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.
MCP Zotero
Note: This is an unofficial community project and is not affiliated with, endorsed by, or supported by the Zotero team or the Corporation for Digital Scholarship. "Zotero" is a registered trademark of the Corporation for Digital Scholarship.
A Model Context Protocol server for Zotero integration. It gives any LLM full access to your Zotero library: search, organize, add papers by DOI, import PDFs, read full-text content, and inject live citations into Word documents.
Originally based on mcp-zotero by Abhishek Kalia. This project has since been extensively rewritten with a new architecture, 15 tools (up from 5), citation injection, PDF management, and Claude skill support.
How it works
The server is designed to be usable by any LLM without external documentation. On connection, it sends workflow instructions via the MCP instructions field, and each tool description includes cross-references and usage guidance. An LLM that has never seen this server before can discover the full workflow — from adding papers to producing a cited Word document — directly from the tool listing.
For advanced use cases (PDF upload policy, citation style guidance, source transparency), a Claude skill is included for Claude.ai Projects. But the skill is optional: the MCP server is fully self-documenting.
Related MCP server: zoty
Local vs Remote LLMs
Scenario | MCP server | Skill needed? |
LLM with filesystem access (Claude Code, LM Studio, etc.) | All 15 tools | No |
LLM without filesystem access (Claude.ai Projects, Claude Desktop) | API tools (search, add, metadata) | Yes, for citation injection |
LLMs with filesystem access can use all tools directly, including inject_citations which reads and writes .docx files on disk.
LLMs without filesystem access — including Claude Desktop, which connects to MCP but cannot generate files locally — can use the included Claude skill (skills/zotero-skill-mcp-integrations/), which runs citation injection entirely inside a sandbox. MCP tools handle all Zotero API operations; the skill handles document assembly.
Claude Skill Setup (for Claude.ai Projects and Claude Desktop)
Download the skill
.zipfrom the latest GitHub ReleaseExtract it and upload the folder to your Claude.ai Project as a skill
The skill enables citation injection directly inside the sandbox, without requiring local filesystem access
Setup
Requires Node.js 22 or newer.
The server is listed in the official MCP Registry as
io.github.Xevos117/mcp-zotero (starting with the first release after 2.0.2), so registry-aware clients can find and install it from
there; the configuration below works with any MCP client.
Get your Zotero credentials:
# Create an API key at https://www.zotero.org/settings/keys # (enable library read/write + file access) # Then retrieve your user ID: curl -H "Zotero-API-Key: YOUR_API_KEY" https://api.zotero.org/keys/currentSet environment variables:
export ZOTERO_API_KEY="your-api-key" export ZOTERO_USER_ID="user-id-from-curl" export UNPAYWALL_EMAIL="your@email.edu" # Optional: enables OA PDF lookup via Unpaywall export UNSAFE_OPERATIONS="none" # Optional: "none" | "items" | "all" (see below)
Targeting a group library
By default the server targets your personal Zotero library (/users/<ZOTERO_USER_ID>/...).
To target a group library instead, set two additional environment variables:
Variable | Values | Required? | Default |
|
| No |
|
| numeric library ID | Yes when type= | — |
Example for a group library:
ZOTERO_API_KEY=...
ZOTERO_USER_ID=12345 # optional for groups: used by get_user_id and per-call library_type="user"
ZOTERO_LIBRARY_TYPE=group
ZOTERO_LIBRARY_ID=6178978 # the group IDThe API key in ZOTERO_API_KEY must have access to the target group library — generate or scope keys at https://www.zotero.org/settings/keys.
The server refuses to start if ZOTERO_LIBRARY_TYPE=group is set without ZOTERO_LIBRARY_ID, or if a library ID is not numeric.
Per-call library override
Every tool accepts two optional args that override the env defaults for that single call:
library_type—"user"or"group"library_id— numeric library ID
Resolution order: per-call arg > env var > implicit default (user, ZOTERO_USER_ID). library_id must be numeric. Overriding library_type to group on a server configured for a user library requires library_id; overriding to user on a group server without library_id uses ZOTERO_USER_ID.
Note that per-call overrides let the LLM reach any library the API key can access. Scope the key to the libraries you want exposed; deletions remain gated by UNSAFE_OPERATIONS.
This lets a single MCP instance target multiple libraries (e.g., a staging group and a final-output group) without restarting:
// First call targets the staging group
{
"tool": "add_items",
"args": {
"items": [...],
"library_type": "group",
"library_id": "5597114"
}
}
// Second call targets DART-output (different group)
{
"tool": "add_items",
"args": {
"items": [...],
"library_type": "group",
"library_id": "6178978"
}
}Environment Variables
Variable | Required | Description |
| Yes | API key for Zotero Web API v3. Create one at zotero.org/settings/keys with library read/write and file access permissions. |
| Yes | Your Zotero numeric user ID. Retrieve it with |
| No | Email for Unpaywall API requests (rate-limit policy). Enables OA PDF lookup in |
| No | Controls destructive operations (deletion). See Unsafe Operations below. Default: |
Unsafe Operations
By default, the MCP server does not allow any deletion. This is a safety measure to prevent an LLM from accidentally deleting items or collections from your library.
To enable deletion, set the UNSAFE_OPERATIONS environment variable to one of the following values:
Value |
|
| Use case |
| Blocked | Blocked | Safe mode — no deletions possible |
| Allowed | Blocked | Allow deleting items but protect collection structure |
| Allowed | Allowed | Full access — items and collections can be deleted |
Important notes:
If
UNSAFE_OPERATIONSis not set, empty, or set to an unrecognized value, it defaults tonone.The value is case-insensitive (e.g.
ALL,Items,NONEall work).delete_itemsdeletes items permanently: the Zotero Web API multi-item DELETE does not move them to the Zotero trash, so they cannot be restored from the desktop client.delete_collectionremoves the collection (folder) only — items inside it are not deleted and remain in your library.The
allvalue includes both item and collection deletion because managing collections inherently requires item-level access.
Configuration example:
{
"mcpServers": {
"zotero": {
"command": "npx",
"args": ["-y", "@xevos117/mcp-zotero"],
"env": {
"ZOTERO_API_KEY": "YOUR_API_KEY",
"ZOTERO_USER_ID": "YOUR_USER_ID",
"UNSAFE_OPERATIONS": "items"
}
}
}
}Integration with Claude Desktop
Install as a Claude Desktop extension (one click)
Download
mcp-zotero.mcpbfrom the latest release (available from the first release after 2.0.2).Open the file with Claude Desktop (macOS or Windows; e.g. double-click it) and confirm in the installation dialog. The bundle itself is not platform-specific (
manifest.jsonlists macOS, Windows and Linux, and CI runs it on Linux), so other MCPB-compatible clients can install it too.Fill in your Zotero API key and Zotero user ID (see Setup). Optional settings: library type and group ID (for a group library), the email for Unpaywall (open-access PDF lookup) and which deletions are allowed (default: none, see Unsafe Operations).
The extension bundles the server and all its dependencies, so no npm install is needed. It needs Node.js 22 or newer
(compatibility.runtimes.node in the manifest): according to the MCPB documentation, Claude Desktop runs Node.js
extensions with the runtime that ships with the app. The API key is declared as a sensitive setting (masked
input, stored securely by the client). Deletions stay disabled unless you change Deletions allowed in the
extension settings.
The extension contains only the MCP server: the Claude skills in skills/ (citation injection inside
the sandbox, scientific writing) are installed separately from their .zip files in the same release, as described
in Claude Skill Setup.
Manual configuration (any MCP client)
Add to your Claude Desktop configuration (or the equivalent file of another MCP client):
{
"mcpServers": {
"zotero": {
"command": "npx",
"args": ["-y", "@xevos117/mcp-zotero"],
"env": {
"ZOTERO_API_KEY": "YOUR_API_KEY",
"ZOTERO_USER_ID": "YOUR_USER_ID",
"UNPAYWALL_EMAIL": "YOUR_EMAIL"
}
}
}
}Integration with Claude Code
claude mcp add zotero -e ZOTERO_API_KEY=YOUR_API_KEY -e ZOTERO_USER_ID=YOUR_USER_ID -- npx -y @xevos117/mcp-zoteroAdd -e UNPAYWALL_EMAIL=... or -e UNSAFE_OPERATIONS=items as needed, and --scope user to make the server
available in every project (the default local scope covers only the current project). From a clone of this repository,
npx tsx src/server.ts runs the server from source instead (see Development).
Available Tools
Library browsing
Tool | Description |
| List all collections (folders) with keys, names, and parent relationships |
| Get items in a specific collection with keys, titles, authors, dates |
| Search by query, or list items sorted by field (date, title, etc.) |
| Batch metadata retrieval for multiple items — returns all type-specific fields (bookTitle, proceedingsTitle, university, etc.), the |
| Get full-text content of a PDF attachment via Zotero's fulltext index |
Adding content
Tool | Description |
| Add papers by DOI with automatic metadata resolution. Auto-attaches OA PDFs via Unpaywall |
| Add items with direct metadata — supports all 37 Zotero item types (books, theses, reports, etc.), batch-capable |
| Create a new collection, optionally nested under a parent |
| Download a PDF from URL, upload to Zotero storage, auto-index full text |
| Batch OA PDF lookup and auto-attach via Unpaywall (by item keys or collection) |
| Attach a URL to an existing item or create a standalone link |
Deleting content
Tool | Description |
| Permanently delete up to 50 items per call (not moved to the Zotero trash). Requires |
| Delete a collection (folder). Items inside are kept. Requires |
Citation & documents
Tool | Description |
| Inject live Zotero citations into a Word document. Supports APA, IEEE, Vancouver, Harvard, Chicago. Output is saved in the same folder as the input file with a |
| Returns the configured Zotero user ID |
Development
npm install
npm run build # Compile TypeScript
npm test # Unit and per-tool tests (vitest, fetch faked, ~1000 tests)
npm run test:e2e # Built server over stdio against a local fake Zotero API
npm run smoke # Quick stdio smoke test of the built server (user + group library)
npm run bundle # Claude Desktop extension: dist/mcp-zotero.mcpb (production dependencies only)
npm run smoke -- --bundle dist/mcp-zotero.mcpb # smoke test of the unpacked bundle, started from its manifest
npx tsx src/server.ts # Run directly without buildingTesting
Layer | Command | Runs on | What it covers |
Unit / per tool |
| every PR (Node 22, 24, 26) | every tool through a real MCP client, Zotero faked at the |
E2E + smoke |
| every PR | the compiled server over stdio, user and group libraries |
Live |
| PRs from this repository, | the real Zotero Web API on a dedicated test account (test/live); skipped unless |
Agent smoke | workflow, after the live suite passed | PRs from this repository, manual runs (e.g. on | a Claude Code agent uses the server like a user, following the versioned skill |
Word | manual | every release | test/WORD_CHECKLIST.md: open an injected document in Word with the Zotero plugin |
Every test case is listed in test/TEST_CASES.md.
Live tests locally use only ZOTERO_TEST_* variables, never your ZOTERO_API_KEY:
ZOTERO_TEST_API_KEY=... ZOTERO_TEST_USER_ID=... UNPAYWALL_EMAIL=you@example.org npm run test:liveAgainst a personal library add ZOTERO_LIVE_SAFE_MODE=1: the suite only creates items and collections
named mzt-<runId>… and tagged mzt-run:<runId>, deletes only those, and skips collection deletion and
group writes. A dedicated test account is marked by a collection named __mzt_disposable__.
Dependencies are updated monthly by a Claude Code agent (workflow,
skill .claude/skills/deps-update); patch-only PRs are auto-merged after
all required checks pass, anything else waits for review. GitHub Actions versions are updated by Dependabot.
CI setup (repository owner)
Environment | Secrets | Variables |
|
|
|
| — | the same four |
The agents authenticate to the Claude API with Anthropic Workload Identity Federation (GitHub OIDC, no API key). Recommended: one federation rule per environment, as narrow as possible:
Rule for | subject (exact, no | claims (exact string pairs) | condition (CEL) |
|
|
|
|
|
|
| — |
Claims are exact-match string pairs (only subject_prefix supports a trailing *), so a prefix such as
"this workflow on any ref" must be written as a CEL condition, as above; an exact job_workflow_ref
(claude-deps, main only) can be a plain claim.
Audience https://api.anthropic.com. The subject is an exact match unless it ends with *; jobs that use
an environment get the OIDC subject repo:Xevos117/mcp-zotero:environment:<environment>, so a rule
written for a branch or PR subject never matches. Numeric ids (not names) keep a renamed or re-created
repository/account from matching.
Trade-off for zotero-live: the "any ref" condition is needed for the agent smoke test to run on pull
requests, but it also accepts a modified copy of that workflow file pushed to any branch of this repository
(only people with write access can push). Pinning it to the exact claim
job_workflow_ref: Xevos117/mcp-zotero/.github/workflows/claude-agent-smoke.yml@refs/heads/main closes that
gap but disables the agent smoke test on PRs (it would then run only on manual runs from main).
Other owner settings:
The Zotero test account needs a collection named
__mzt_disposable__.Repository settings: "Allow auto-merge", and "Allow GitHub Actions to create and approve pull requests" (the dependency workflow opens its PR with
GITHUB_TOKEN). Keep the ruleset at 0 required approvals: auto-merge of the dependency PR relies on it, and it is also what makes the second setting acceptable. That setting is a conscious trade-off: it lets workflows open PRs (needed) and approve them (not used by any workflow here); if approvals are ever required, it would let a workflow approve its own PR, so revisit it together with any approval requirement.No GitHub App is used by any workflow (the agents authenticate to Anthropic with Workload Identity Federation and use only
GITHUB_TOKENon GitHub, read-only for the agents). If the Claude GitHub App is installed on this repository, uninstall it.Set a monthly spend limit on the Anthropic workspace used by the federation rules: it is the real cap on agent costs.
Residual risk: the agent smoke job that holds the OIDC token also runs the MCP server built from the PR's code (that is what it tests). It runs only for same-repository PRs by the owner or a bot, the token is bound to the
zotero-liveenvironment and the workspace spend limit caps any misuse.Required checks after the stack merges:
test (node 22),test (node 24),test (node 26),live-tests,agent-smoke.
Releasing
Update
CHANGELOG.md(moveUnreleasedto the new version) and the version inpackage.json, inserver.json(versionandpackages[0].version) and inmanifest.json(npm testfails if they differ).Make sure CI, live tests and the agent smoke test are green on
main.Run the manual Word checklist on a document injected by the release candidate and record the results in its log.
Before the first release that ships the Claude Desktop extension (and whenever
manifest.jsonor the Node.js requirement changes): build it withnpm run bundle, installdist/mcp-zotero.mcpbonce in a real Claude Desktop (macOS or Windows), fill in a test key and user ID, and check that the 15 tools appear andget_collectionsanswers.Publish the GitHub release: the release workflow runs the tests, publishes to npm, publishes
server.jsonto the MCP Registry and attaches the skill zips and the Claude Desktop extension (mcp-zotero.mcpb). The release tag must be the version (e.g.2.0.3): the registry and bundle jobs check out the tag and fail ifpackage.jsondiffers.If only the
mcp-registryjob fails, use Re-run failed jobs on the run: Re-run all jobs stops at the npm publish (the version already exists) and so never reaches the registry.
Debug with MCP Inspector
npx @modelcontextprotocol/inspector npx tsx src/server.tsLicense
MIT - see LICENSE for details.
Available Tools
15 toolsadd_itemsA
Add items to Zotero by providing metadata directly. Supports ALL 37 Zotero item types.
WHEN TO USE:
For items that do not have a DOI (books, theses, reports, etc.)
When you need full control over metadata (e.g., override a title, set a specific itemType, add custom fields) — even if a DOI exists, use add_items when the auto-resolved metadata would be incorrect or incomplete
Mixed batch: if some items have DOIs and others don't, call add_items_by_doi for the DOIs and add_items for the rest (two separate calls)
Prefer add_items_by_doi when DOIs are available AND you don't need to override metadata (it auto-resolves everything and attaches OA PDFs)
BATCH: Pass multiple items in the 'items' array (one tool call).
COMMON FIELDS (available for most types): title, date, abstractNote, url, DOI, publisher, place, pages, volume, language, extra
ITEM TYPE QUICK REFERENCE:
journalArticle: publicationTitle, volume, issue, pages, DOI, ISSN
book: publisher, ISBN, edition, numPages, series, seriesNumber
bookSection: bookTitle, publisher, pages, ISBN, edition
conferencePaper: proceedingsTitle, conferenceName, publisher, DOI
thesis: thesisType ("PhD thesis"|"Master's thesis"), university
report: reportType, reportNumber, institution
webpage: websiteTitle, websiteType, accessDate
preprint: repository, archiveID, genre ("Preprint")
patent: patentNumber, assignee, issuingAuthority, filingDate
computerProgram: versionNumber, company, system, programmingLanguage
CREATORS: Array of {firstName, lastName, creatorType} or {name, creatorType} for institutional. Default creatorType is "author". Some types use different primary types (e.g., "director" for film, "inventor" for patent, "artist" for artwork).
Invalid fields or creatorTypes for a given type are rejected with helpful error messages listing the valid options.
| Name | Required | Description | Default |
|---|---|---|---|
| tags | No | Tags to apply to all items | |
| items | Yes | Array of items to add. Each item must have itemType and title. Additional fields depend on the item type. | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| collection_key | No | Zotero collection key to add all items to. Get this from create_collection or get_collections. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries real weight and does disclose the rejection behavior for invalid fields/creatorTypes and the batching model. It does not cover what is returned (item keys), duplicate handling, or library permissions, which matters for a write tool.
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?
Front-loaded with purpose and when-to-use, then organized into labeled sections that are easy to scan. It is long, but the type reference and creator notes each carry information the schema does not encode, so the length is largely earned.
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 complex nested-schema write tool with no annotations and no output schema, the input side is thoroughly covered. The remaining gap is the result side: nothing says what comes back per item or how partial failures in a batch are reported.
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% for the top-level params, but the item objects use additionalProperties, so the description supplies genuinely new semantics: type-specific field lists, the 10 type quick-reference mappings, and the creators object shape plus creatorType defaulting rules.
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?
States a specific verb and resource ('Add items to Zotero') and immediately scopes it ('by providing metadata directly', 'Supports ALL 37 Zotero item types'). It is clearly distinguishable from add_items_by_doi, which is named in the body.
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 an explicit WHEN TO USE block with concrete conditions: no DOI, full metadata control, and the mixed-batch two-call pattern. It names the alternative (add_items_by_doi) and states both when to prefer it and when to override it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_items_by_doiA
Add items to your Zotero library by resolving DOIs. Works with ANY item type that has a DOI — journal articles, books, datasets, preprints, conference papers, reports, etc. For each DOI, resolves metadata via content negotiation and creates the item in Zotero with the correct type automatically. Returns a list of successfully added items (with item_key and title) and any failures.
WHEN TO USE vs add_items:
Use add_items_by_doi when the item HAS a DOI — it auto-resolves all metadata and attaches open-access PDFs (when the server has UNPAYWALL_EMAIL set).
Use add_items when the item does NOT have a DOI, or when you need to override specific metadata fields (add_items_by_doi does not allow metadata overrides).
Mixed batch: if some items have DOIs and others don't, make two separate calls — add_items_by_doi for the DOIs and add_items for the rest.
WORKFLOW TIPS:
To collect metadata for all added items, call get_items_details with the returned item_keys (single batch call).
To create a cited Word document, use the returned item_keys as placeholders in a .docx, then call inject_citations. See inject_citations description for the full workflow.
| Name | Required | Description | Default |
|---|---|---|---|
| dois | Yes | Array of bare DOIs (e.g. ["10.1038/s41586-023-06647-8"], without "https://doi.org/"). Each DOI will be resolved and added to Zotero. | |
| tags | No | Tags to apply to all added items | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| collection_key | No | Zotero collection key to add items to. Get this from create_collection or get_collections. | |
| auto_attach_pdf | No | Attach open-access PDFs found via Unpaywall (default: true; needs UNPAYWALL_EMAIL in the server settings, uses the user's Zotero file storage). Leave enabled unless the user does not want PDFs or attachment is causing errors. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does most of it well: it discloses automatic metadata resolution via content negotiation, automatic item-type selection, PDF attachment side effects gated on UNPAYWALL_EMAIL and using the user's file storage, and that results are split into successes and failures. It stops short of stating permission/auth requirements or how duplicate DOIs already in the library are handled.
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?
Front-loaded with the primary purpose, then cleanly sectioned into WHEN TO USE vs add_items and WORKFLOW TIPS. Each block is scannable and every sentence conveys routing or workflow information.
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?
No output schema exists, but the description compensates by stating the return shape (item_key and title for successes, plus failures). Combined with sibling routing and the PDF side-effect disclosure, an agent has everything needed to call it 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 coverage is 100%, so all six parameters are already documented, establishing a baseline of 3. The description adds only marginal semantics beyond the schema (that DOIs must be bare, that PDF attachment depends on server config, and that no metadata overrides are possible), none of which redefines parameter behavior.
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?
States a specific verb+resource ("Add items to your Zotero library by resolving DOIs") and immediately clarifies scope: any item type with a DOI. It explicitly names and differentiates itself from the sibling add_items in a dedicated section, so an agent can disambiguate without opening either schema.
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?
Gives explicit when-to-use and when-not-to-use rules against add_items, including the mixed-batch case ("make two separate calls"). It also names concrete alternatives for downstream steps (get_items_details, inject_citations), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_linked_url_attachmentA
Attach a linked URL to an existing Zotero item, or create a standalone linked-URL attachment. Use this to link external PDFs, web pages, or other resources to items already in your library. If parent_item is provided, the attachment is added as a child; otherwise it is standalone. A linked URL stores no file and no full text (get_item_fulltext cannot read it): to store a PDF in Zotero use import_pdf_to_zotero.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of the resource to link | |
| tags | No | Tags to apply to the attachment | |
| title | No | Display title for the attachment (defaults to the URL) | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| collections | No | Collection keys to add the attachment to (only used for standalone attachments, ignored when parent_item is set) | |
| parent_item | No | Key of the parent item (a regular item, not an attachment or note), from search_library or get_collection_items. If provided, the attachment becomes a child of that item. | |
| content_type | No | MIME type of the linked resource (e.g. "application/pdf", "text/html") | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden, and it delivers the key behavioral caveat: a linked URL stores no file and no full text, so get_item_fulltext cannot read it. It explains parent-vs-standalone creation and that collections is ignored for child attachments, but says nothing about permissions, duplicate handling, or what the call returns.
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?
Four tight sentences, each carrying a distinct decision-relevant fact, with the primary purpose front-loaded and the sibling redirection last. No filler or restatement of the title.
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 an unannotated eight-parameter creation tool with no output schema, the description covers the essential semantics and the most important limitation. It stops short of describing the returned result or whether re-adding the same URL duplicates an attachment, which leaves a modest gap.
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%, so the schema already documents all eight parameters thoroughly, including that collections applies only to standalone attachments. The description reinforces the parent_item branch but adds no format or syntax detail beyond the schema, so baseline 3 applies.
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?
States a specific verb and resource ('Attach a linked URL to an existing Zotero item, or create a standalone linked-URL attachment') and enumerates the two modes of operation. An agent can distinguish it from import_pdf_to_zotero or find_and_attach_pdfs purely from this sentence.
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 says what to use it for ('link external PDFs, web pages, or other resources to items already in your library') and names the correct alternative when the goal is to store a PDF ('use import_pdf_to_zotero'). It also states the conditional parent_item behavior, so the when-to-use decision is fully resolved.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_collectionA
Create a new collection (folder) in your Zotero library. Optionally nest it under a parent collection. Returns the new collection key and name. Use the key as collection_key in add_items_by_doi or add_items to organize imported papers.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name of the new collection | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| parent_collection | No | Zotero collection key of the parent collection. Get this from get_collections. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It does disclose the return payload (new collection key and name), which is genuinely useful, but it says nothing about permission requirements, duplicate-name handling, or failure modes when parent_collection is invalid.
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?
Three short sentences, front-loaded with the core action, then the optional variation, then the integration hint. No filler.
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 simple create tool with fully documented parameters and no output schema, the description supplies the return shape and the cross-tool linkage. Only mutation-specific behaviors (permissions, idempotency) are missing.
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 schema already documents all four parameters including the parent_collection pattern and library_id semantics. The description only restates that nesting under a parent is optional, adding little beyond the schema, so baseline 3 applies.
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 ('Create a new collection (folder) in your Zotero library') and names what the returned artifact is, which cleanly distinguishes it from siblings like get_collections and delete_collection.
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?
It states the optional nesting case and gives a concrete downstream workflow ('Use the key as collection_key in add_items_by_doi or add_items'), which tells the agent when this tool is a prerequisite for other tools. It stops short of explicit exclusions or prerequisite conditions such as requiring the parent collection to already exist.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_collectionA
Delete a collection (folder) from your Zotero library; confirm with the user first. Items inside the collection are NOT deleted — they remain in your library. Disabled unless the server setting UNSAFE_OPERATIONS is 'all'.
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| collection_key | Yes | Zotero collection key to delete. Get this from get_collections. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full disclosure burden and does it well: it flags the destructive nature, states the non-cascading side effect (items survive), requires user confirmation, and discloses a feature-flag gate that would otherwise cause a surprising failure. It stops short of saying deletion is irreversible or what the response contains.
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?
A single compact sentence with the action, the side-effect warning, the confirmation requirement, and the availability gate ordered by importance. No filler and nothing buried.
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 simple three-parameter deletion tool with no output schema, the description covers the decision-relevant facts: scope, side effects, gating, and confirmation. Only the irreversibility/undo story and expected result are unstated, which is a minor gap rather than a blocking one.
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 library_id, library_type, and collection_key are all documented in the schema, including the source of the key ('Get this from get_collections'). The description adds no further parameter-level detail, so baseline 3 applies.
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?
States a specific verb and resource ('Delete a collection (folder) from your Zotero library') and immediately disambiguates the ambiguous term 'collection' as a folder. It also preempts confusion with the sibling delete_items by noting that contained items are not deleted, so an agent can pick correctly without opening either schema.
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?
Gives clear operational guidance: confirm with the user first, and the tool is unavailable unless UNSAFE_OPERATIONS is 'all' — a real prerequisite an agent must check. It does not explicitly name the alternative route (delete_items, or emptying a collection before deleting) for an agent whose actual goal is removing items.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_itemsA
Delete one or more items from your Zotero library: they are permanently deleted (not moved to the Zotero trash), so confirm with the user first. Accepts up to 50 item keys per call. Disabled unless the server setting UNSAFE_OPERATIONS is 'items' or 'all'.
| Name | Required | Description | Default |
|---|---|---|---|
| item_keys | Yes | Array of Zotero item keys to delete (e.g. ["EUHUT5K3", "F9UQM7N2"]). Max 50 per call. | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full load and does well: it discloses irreversibility (bypasses the trash), the 50-key batch ceiling, and the server-side enablement requirement. It does not say what happens on partial failures or whether deleting an item referenced by a collection has side effects.
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?
Three short sentences, front-loaded with the destructive behavior, then the batch limit, then the enablement gate. Nothing is wasted or buried.
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 3-parameter, 1-required destructive tool with no output schema, the description covers the safety-critical facts an agent needs (irreversible, confirm first, gated by config). Return behavior and error semantics on invalid/unknown keys are the only notable omissions.
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 schema already documents item_keys format, the pattern, minItems 1 and maxItems 50, plus library_id/library_type defaults. The description's 'up to 50 item keys per call' merely restates schema constraints, adding no 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?
Names a specific verb and resource ('Delete one or more items from your Zotero library') and immediately distinguishes itself from delete_collection by scoping to items. The permanence clause ('permanently deleted, not moved to the Zotero trash') makes the operation 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?
Gives an explicit precondition for calling ('confirm with the user first') and an environment gate (UNSAFE_OPERATIONS must be 'items' or 'all'). It does not name delete_collection as the sibling to use for collections, so the alternative routing is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_and_attach_pdfsA
For each Zotero item, check Unpaywall for open access PDFs and attach them. Items must have a DOI; needs UNPAYWALL_EMAIL in the server settings. Uploads use the user's Zotero file storage; dry_run only reports what is available. Uses the same source as Zotero Desktop's 'Find Available PDFs'.
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | Only report which PDFs are available without downloading/attaching | |
| item_keys | No | Array of Zotero item keys to process (mutually exclusive with collection_key) | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| collection_key | No | Process all items in this collection (mutually exclusive with item_keys) | |
| skip_if_attachment_exists | No | Skip items that already have a PDF attachment |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that this is a mutating operation, that it consumes the user's Zotero file storage quota, that it requires server-side configuration, and what dry_run does. It stops short of failure semantics (what happens to items without a DOI — skipped or error?) and any rate-limit/partial-result behavior for large collections.
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?
Four front-loaded sentences, no filler, each conveying a distinct operational fact. Minor redundancy: the dry_run explanation largely restates the schema's own description of that parameter.
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 a 6-parameter, zero-required mutation tool with no annotations and no output schema, the description covers the essentials an agent needs (source, prerequisites, storage cost, dry-run). Gaps remain around return shape and partial-failure handling, but nothing critical to invoking it correctly is missing.
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%, so the baseline is 3 — every parameter already has its own documented semantics. The description reinforces scope ('for each Zotero item', DOI requirement) and the dry_run semantics, but adds no syntax or interplay detail beyond what the schema already states.
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?
States a specific verb ('check Unpaywall') plus the concrete side effect ('attach them') for a named resource (open access PDFs on Zotero items). This is clearly distinguishable from siblings like import_pdf_to_zotero or add_linked_url_attachment, which act on known files/URLs rather than discovering PDFs via Unpaywall.
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?
Gives real preconditions: items must have a DOI, UNPAYWALL_EMAIL must be configured, and dry_run is the report-only mode. However, it never names an alternative tool or states when *not* to use it (e.g. when no DOIs exist, use import_pdf_to_zotero instead), and the item_keys/collection_key exclusivity is left to the schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_collection_itemsA
Get all items in a specific Zotero collection (only items directly in it, not those in its subcollections). Returns item keys, titles, authors, and dates. Use the collectionKey from get_collections. Use the returned item keys with get_items_details, get_item_fulltext, or inject_citations.
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| collectionKey | Yes | Collection key from get_collections | |
| excludeAttachments | No | Exclude attachment and note items (default: true). Set false to list them too. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose the key behavioral trait: subcollection items are excluded, and it enumerates the return fields. It does not mention pagination, result limits, or read-only/permission implications, which are the remaining gaps for an unannotated read tool.
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?
Three tight sentences: purpose and scope first, then input origin, then output usage. Zero filler and nothing buried.
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, and the description compensates by naming the returned fields, plus it explains the chaining pattern. Missing only query-shaping details such as pagination or volume limits for large collections.
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%, so all four parameters (including library_id, library_type, and excludeAttachments defaults) are already documented. The description only reinforces collectionKey provenance, which the schema also states; baseline 3 applies.
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?
States a specific verb and resource ('Get all items in a specific Zotero collection') and immediately scopes it ('only items directly in it, not those in its subcollections'), then names the returned fields. An agent can distinguish this from get_collections or search_library without opening any schema.
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?
Gives explicit provenance for the required input ('the collectionKey from get_collections') and a concrete next-step chain ('use the returned item keys with get_items_details, get_item_fulltext, or inject_citations'). It does not say when to prefer search_library or otherwise avoid this tool, so it falls short of full when/when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_collectionsA
List all collections (folders) in your Zotero library. Returns collection keys, names, and parent relationships. Use collection keys with get_collection_items or as parent_collection in create_collection. Trashed collections are excluded by default.
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| include_trashed | No | Include trashed (deleted) collections. Default false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It discloses return fields (collection keys, names, parent relationships) and the default trashed exclusion, but omits pagination, auth/permission requirements, and an explicit read-only statement.
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?
Three short sentences, front-loaded with purpose, followed by return shape and integration guidance. Nothing is wasted and the exclusion default is placed at the end as a caveat.
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 description appropriately explains return fields. It covers the low-complexity call well, but missing pagination or result-limit notes keep it from being fully complete.
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%, and the schema already documents library_id, library_type, and include_trashed in detail. The description only restates the trashed default and adds no syntax or format meaning beyond what the schema provides.
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?
States a specific verb and resource ('List all collections (folders)') and adds scope ('in your Zotero library'), which clearly separates it from siblings like get_collection_items or search_library.
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?
Gives clear downstream guidance: use returned collection keys with get_collection_items or as parent_collection in create_collection. It also states the trashed-exclusion default. It stops short of naming explicit alternatives or when-not-to-use cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_item_fulltextA
Get the text of a Zotero item's stored PDF from Zotero's full-text index. Works for PDFs indexed by Zotero desktop and synced, or imported with import_pdf_to_zotero / find_and_attach_pdfs; not for linked URLs or web pages. Use this to read the full content of papers instead of relying on abstracts.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes | Item key (the parent item or its PDF attachment), from search_library or get_collection_items | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| max_characters | No | Maximum characters to return: a positive integer, or 0 for no limit (default: 50000); truncated: true in the result when cut |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden and does well by disclosing the data source, coverage conditions, and a key limitation. It does not discuss authentication, rate limits, or detailed output shape, but the schema covers truncation via max_characters.
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 three efficient sentences with the purpose front-loaded, followed by scope and usage guidance. Every sentence contributes useful information without 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 read-only retrieval tool with full schema coverage but no output schema, the description sufficiently explains what is retrieved and the main limitations. It could be slightly more complete by explicitly describing the returned text/truncation behavior, though the max_characters schema description already covers truncation.
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 has 100% description coverage for all four parameters, including item_key, library_id, library_type, and max_characters. The description adds context about the resource being read but does not add parameter-specific meaning beyond the schema, so the baseline of 3 is appropriate.
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 and resource: getting the text of a Zotero item's stored PDF from Zotero's full-text index. It also clearly distinguishes the tool from related capabilities by saying it does not work for linked URLs or web pages.
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?
It gives clear usage context: use this to read full paper content instead of relying on abstracts. It also states an important exclusion, linked URLs and web pages, but does not explicitly name a sibling tool to use for those excluded cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_items_detailsA
Get metadata for multiple Zotero items in a single call. Accepts an array of item keys and returns a map of key → metadata. Use this instead of one call per item. Returns all type-specific fields (e.g. bookTitle for bookSection, proceedingsTitle for conferencePaper, university for thesis). Set include_abstract to include abstracts (excluded by default to keep responses lightweight). Keys not found in the library are listed in not_found (present only when some are missing).
| Name | Required | Description | Default |
|---|---|---|---|
| item_keys | Yes | Array of Zotero item keys (e.g. ["EUHUT5K3", "F9UQM7N2"]). Get these from search_library, add_items_by_doi, or get_collection_items. | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. | |
| include_abstract | No | Include abstractNote in the response. Default false to keep responses lightweight. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden well: it discloses the return shape (map of key → metadata), the default exclusion of abstracts and why ('to keep responses lightweight'), and the conditional not_found list present only when keys are missing. It doesn't cover auth/library-permission behavior, but for a read-only metadata fetch the remaining gaps are minor.
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?
Leads with purpose and batching model, then return semantics, then the optional abstract flag, then edge-case behavior. Six short sentences, each carrying distinct information with no 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?
No output schema exists, so the description must explain return values — and it does: a key → metadata map, inclusion of type-specific fields with concrete examples, and the conditional not_found list. An agent knows what it will get back and how to handle missing keys.
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%, so every parameter is already documented in the schema, including include_abstract's default and purpose. The description largely restates that default rather than adding new syntax or format detail, so baseline 3 applies.
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?
States a specific verb and resource ('Get metadata for multiple Zotero items') plus the batching scope ('in a single call', 'array of item keys'), and returns a map of key → metadata. This distinguishes it clearly from single-item or fulltext siblings like get_item_fulltext.
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 says 'Use this instead of one call per item', giving the condition that selects it over iterative single-item calls. It does not name a sibling tool (e.g. how it differs from get_item_fulltext or search_library results), so the guidance is clear but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_user_idA
Returns the Zotero library configured in the server environment: user_id, library_type, library_id and library_path ("users/" or "groups/"). Pass library_path to the zotero-mcp-integrations skill script (inject.js) to generate Zotero field code URIs. Not needed when using the inject_citations MCP tool, which reads the library internally.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose the key behavior: it reads configuration from the server environment rather than from user input, and it spells out the return shape and path semantics ('users/<id>' or 'groups/<id>'). It stops short of stating failure behavior when no library is configured or whether credentials/scopes affect the result, which keeps it off a 5.
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?
Three sentences: what it returns first, then what to do with the value, then when to skip it. No filler, and the most actionable element (library_path) is front-loaded in the opening sentence.
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 description must explain the return values — and it enumerates all four fields plus the path format. For a zero-parameter lookup tool, an agent has everything needed to decide whether to call it and how to use the result.
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 tool takes zero parameters, so the baseline of 4 applies and there are no parameter semantics to explain. The description correctly spends no words on inputs and instead redirects that space to output field meaning and downstream consumption.
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?
States a specific verb and resource — returns the Zotero library configured in the server environment — and enumerates exactly which fields come back (user_id, library_type, library_id, library_path) with the path format shown. It also indirectly corrects the narrow-sounding name by clarifying the scope is the whole library config, and names inject_citations as the sibling that makes it unnecessary.
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?
Gives explicit positive guidance ('Pass library_path to the zotero-mcp-integrations skill script (inject.js) to generate Zotero field code URIs') and explicit negative guidance ('Not needed when using the inject_citations MCP tool, which reads the library internally'). The when/when-not split and the named alternative leave nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
import_pdf_to_zoteroA
Download a PDF from a URL and upload it to Zotero storage as an imported_url attachment. Unlike linked URL attachments, imported files are stored in Zotero's storage and become fulltext-indexed (searchable via get_item_fulltext). Use this when you need the PDF content to be indexed by Zotero. The URL must lead straight to the PDF file (no login, paywall or HTML landing page); files up to 100 MB, counted against the user's Zotero file storage. For items with a DOI, find_and_attach_pdfs finds open-access PDFs by itself.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of the PDF to download and import | |
| tags | No | Tags to apply to the attachment | |
| title | No | Display title for the attachment (default: filename) | |
| filename | No | Filename for the attachment (default: extracted from URL or document.pdf) | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| collections | No | Collection keys (only used for standalone attachments, ignored when parent_item is set) | |
| parent_item | No | Item key of the parent item. If provided, the attachment becomes a child of that item. | |
| content_type | No | MIME type recorded on the attachment (default: "application/pdf"). This tool imports PDFs only: the downloaded file must start with %PDF- or the import is rejected, whatever this value says. Full text is extracted only for "application/pdf". | application/pdf |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and discloses key behavior: imported files are stored in Zotero storage, become fulltext-indexed, count against storage quota, and must be PDFs up to 100 MB. It does not cover auth requirements, duplicate handling, or error responses beyond PDF validation, so it is strong but not exhaustive.
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 front-loaded with purpose, then alternates, usage condition, constraints, and a final routing note. Every sentence adds useful information and nothing is redundant.
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 nine-parameter tool with no annotations and no output schema, the description covers purpose, storage/indexing implications, size limits, URL requirements, and alternatives well. It omits return-value or error-shape details, which keeps it just short of fully complete.
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%, so the schema already documents all nine parameters. The description adds no parameter-level detail beyond the URL constraint and therefore matches the baseline for a fully documented 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?
States a specific verb and resource: download a PDF and upload it to Zotero storage as an imported_url attachment. It explicitly distinguishes imported attachments from linked URL attachments and names find_and_attach_pdfs as the alternative for DOI-based workflows.
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?
Directly says when to use it ("Use this when you need the PDF content to be indexed by Zotero") and gives clear exclusions and preconditions: no login, paywall, or HTML landing page. It also routes DOI-based cases to find_and_attach_pdfs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
inject_citationsA
Replace placeholder tags in a .docx file with native Zotero field codes that Zotero for Word can recognize and manage. The tool fetches item metadata from Zotero automatically — you only need to provide the .docx file.
WORKFLOW — how to create a Word document with live Zotero citations:
Collect item keys: use add_items_by_doi or add_items (or search_library for existing items)
Generate .docx: create a Word document (e.g. with the "docx" npm package) with placeholders where citations should appear. Each MUST be in its own dedicated TextRun — do NOT mix it with surrounding text.
Call this tool with the .docx file path. It replaces every zcite tag with a Zotero field code and appends a bibliography.
Tell the user to open the file in Word with the Zotero plugin, click Zotero → Add/Edit Bibliography, then Zotero → Refresh.
CITATION STYLES — ask the user which style they want before generating:
apa (default): author-year — (Smith, 2023)
harvard / chicago: author-date — (Smith, 2023) / (Smith 2023)
ieee / vancouver: numbered — [1], [2] WARNING: for numbered styles every MUST include a num="N" attribute with the sequential citation number. Without num, citations render as [?].
ZCITE TAG FORMAT: Supported attributes (any order):
keys (required): item key or comma-separated keys — "ABC12345" or "ABC12345,DEF67890"
num: citation number for IEEE/Vancouver — "1" or "1,2" (required for numbered styles)
locator: page reference — "12-15" (shown as "pp. 12–15"; other labels explicit, e.g. "chap. 3")
prefix: text before citation — "see"
suffix: text after citation — ", emphasis added"
OUTPUT: A new .docx file (original filename with _cited suffix) with Zotero field codes and a ZOTERO_BIBL bibliography at the end. A cited key that is not in the library, or that is not a Zotero key (8 characters from 23456789ABCDEFGHIJKLMNPQRSTUVWXYZ), is an error and no file is written.
WHERE THE FILE LIVES: this tool reads and writes files on the machine where this MCP server runs. If you create the .docx in a sandbox or cloud workspace that the server cannot see (for example code execution in Claude.ai or Claude Desktop, paths like /mnt/user-data/...), do not call this tool: use the zotero-mcp-integrations skill, which injects the citations inside the sandbox with metadata from get_items_details. If that skill is not installed, recommend it to the user: zotero-skill-mcp-integrations.zip from https://github.com/Xevos117/mcp-zotero/releases/latest.
| Name | Required | Description | Default |
|---|---|---|---|
| style | No | Citation style for the visible placeholder text. Zotero will reformat on refresh. Default: apa | apa |
| file_path | Yes | Absolute path to the .docx file containing <zcite keys="..."/> placeholder tags | |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does: it discloses that metadata is fetched automatically, that a NEW file is written with a _cited suffix, that an error on an unknown/malformed key means no file is written at all, and that the user must refresh in Word afterward. It also flags the numbered-style failure mode (missing num= renders as [?]).
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?
Front-loaded with a one-line statement of effect, then scannable headed sections (WORKFLOW, CITATION STYLES, ZCITE TAG FORMAT, OUTPUT, WHERE THE FILE LIVES) — each earns its place. It runs long, and the ZCITE tag format section documents file content rather than any parameter, which is necessary but adds bulk.
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?
No output schema exists, and the OUTPUT section fully specifies the return artifact, its naming, and the failure case. Combined with the sandbox-location caveat and the post-call Word refresh instruction, an agent has everything needed to call it or correctly decide not to.
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 exceeds it by expanding the style enum into concrete rendering behavior and warning that numbered styles change the required file syntax via num=. The library_id/library_type parameters are left entirely to the schema, so it is not a full 5.
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?
Names a specific verb and resource (replace <zcite> placeholders in a .docx with Zotero field codes) and states the concrete outcome (a Word-manageable citation plus bibliography). The final paragraph distinguishes it from the sibling path (the zotero-mcp-integrations skill) by deployment environment, so an agent can route correctly without opening a schema.
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 an explicit four-step workflow naming which sibling tools to call first (add_items_by_doi, add_items, search_library) and what to produce before invoking this one. It also states a hard when-NOT-to-use condition — sandbox/cloud workspaces the server cannot see (/mnt/user-data/...) — and names the alternative tool and fallback recommendation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_libraryA
Search your Zotero library or list items sorted by a field.
When 'query' is provided, matches it against titles, creator names and years (not abstracts, tags or full text). When 'query' is omitted, lists items sorted by the chosen field (default: dateAdded, descending).
Examples:
Search: { "query": "deep learning" }
Recent items: { "sort": "dateAdded", "limit": 10 }
Recent with search: { "query": "transformers", "sort": "dateAdded", "limit": 5 }
Use the returned item keys with get_items_details, get_item_fulltext, or inject_citations.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | Field to sort by (default: dateAdded) | dateAdded |
| limit | No | Maximum number of items to return, 1-100 (default: 25; above 100 returns 100) | |
| query | No | Search query. If omitted, returns items sorted by the chosen field. | |
| direction | No | Sort direction (default: desc) | desc |
| library_id | No | Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library. | |
| library_type | No | Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description usefully discloses that search matches titles, creator names, and years but not abstracts, tags, or full text, and explains the default listing behavior. However, it does not state read-only safety explicitly, nor return shape, pagination beyond the limit parameter, permissions, or error behavior.
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?
Front-loads the core purpose, then explains query versus no-query behavior, then provides compact examples and follow-up tools. The structure is efficient and every section helps invocation.
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 six-parameter search/list tool with no output schema, the description gives enough behavioral context to call it correctly, including search scope, ordering behavior, examples, and follow-up tools. It still leaves result structure and pagination behavior unstated beyond the limit parameter.
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%, so parameters are already documented. The description adds meaningful semantics for 'query' by specifying exactly which fields are matched and what happens when it is omitted, and its examples clarify how sorting and limiting interact.
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?
States a specific verb and resource: searching a Zotero library or listing items with sorting. The dual search/list behavior is clear, though it does not explicitly distinguish itself from collection-scoped siblings such as get_collection_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?
Clearly explains when to provide 'query' versus when to omit it, and gives concrete examples for search, recent listing, and combined use. It also points to follow-up tools for returned item keys, though it does not state exclusions versus collection-specific alternatives.
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.
14 tool updates
v2.0.4- Changed
add_items2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
add_items_by_doi4 fields changed- changed
Input schema / properties / auto_attach_pdf / descriptionPrevious value: -"Attach freely available OA PDFs via Unpaywall (default: true). This is lightweight and adds no cost — leave enabled. Only set to false if PDF attachment is causing errors."New value: +"Attach open-access PDFs found via Unpaywall (default: true; needs UNPAYWALL_EMAIL in the server settings, uses the user's Zotero file storage). Leave enabled unless the user does not want PDFs or attachment is causing errors." - changed
Input schema / properties / dois / descriptionPrevious value: -"Array of DOI strings (e.g. [\"10.1038/s41586-023-06647-8\"]). Each DOI will be resolved and added to Zotero."New value: +"Array of bare DOIs (e.g. [\"10.1038/s41586-023-06647-8\"], without \"https://doi.org/\"). Each DOI will be resolved and added to Zotero." - changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
add_linked_url_attachment3 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server." - changed
Input schema / properties / parent_item / descriptionPrevious value: -"Item key of the parent item. If provided, the attachment becomes a child of that item."New value: +"Key of the parent item (a regular item, not an attachment or note), from search_library or get_collection_items. If provided, the attachment becomes a child of that item."
- Changed
create_collection2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
delete_collection2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
delete_items2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
find_and_attach_pdfs2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
get_collection_items4 fields changed- changed
Input schema / properties / collectionKey / descriptionPrevious value: -"The collection key/ID"New value: +"Collection key from get_collections" - changed
Input schema / properties / excludeAttachments / descriptionPrevious value: -"Exclude attachment and note items (default: true)"New value: +"Exclude attachment and note items (default: true). Set false to list them too." - changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
get_collections2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
get_item_fulltext7 fields changed- changed
Input schema / properties / item_key / descriptionPrevious value: -"The Zotero item key (parent item or attachment key)"New value: +"Item key (the parent item or its PDF attachment), from search_library or get_collection_items" - changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server." - changed
Input schema / properties / max_characters / descriptionPrevious value: -"Maximum characters to return (default: 50000, 0 = no limit)"New value: +"Maximum characters to return: a positive integer, or 0 for no limit (default: 50000); truncated: true in the result when cut" - added
Input schema / properties / max_characters / maximumAdded value: +9007199254740991 - added
Input schema / properties / max_characters / minimumAdded value: +0 - changed
Input schema / properties / max_characters / typePrevious value: -"number"New value: +"integer"
- Changed
get_items_details2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
import_pdf_to_zotero2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
inject_citations2 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server."
- Changed
search_library6 fields changed- changed
Input schema / properties / library_id / descriptionPrevious value: -"Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library."New value: +"Numeric user or group ID for this call (a group's ID is the number in its zotero.org/groups/<id> URL). Default: the configured library. Required with library_type 'group' when the server is configured for a user library." - changed
Input schema / properties / library_type / descriptionPrevious value: -"Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server."New value: +"Library for this call: 'user' (personal) or 'group'. Default: the library configured on the server." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of items to return (default: 25, max: 100)"New value: +"Maximum number of items to return, 1-100 (default: 25; above 100 returns 100)" - added
Input schema / properties / limit / maximumAdded value: +9007199254740991 - added
Input schema / properties / limit / minimumAdded value: +1 - changed
Input schema / properties / limit / typePrevious value: -"number"New value: +"integer"
14 tool updates
v2.0.2- Changed
add_items3 fields changed- added
Input schema / properties / collection_key / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
add_items_by_doi4 fields changed- added
Input schema / properties / collection_key / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / dois / items / minLengthAdded value: +1 - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
add_linked_url_attachment4 fields changed- added
Input schema / properties / collections / items / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +} - added
Input schema / properties / parent_item / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$"
- Changed
create_collection3 fields changed- added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +} - added
Input schema / properties / parent_collection / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$"
- Changed
delete_collection4 fields changed- removed
Input schema / properties / collection_key / minLengthRemoved value: -1 - added
Input schema / properties / collection_key / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
delete_items3 fields changed- added
Input schema / properties / item_keys / items / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
find_and_attach_pdfs4 fields changed- added
Input schema / properties / collection_key / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / item_keys / items / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
get_collection_items3 fields changed- added
Input schema / properties / collectionKey / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
get_collections2 fields changed- added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
get_item_fulltext3 fields changed- added
Input schema / properties / item_key / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
get_items_details3 fields changed- added
Input schema / properties / item_keys / items / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
import_pdf_to_zotero5 fields changed- added
Input schema / properties / collections / items / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$" - changed
Input schema / properties / content_type / descriptionPrevious value: -"MIME type of the file (default: \"application/pdf\")"New value: +"MIME type recorded on the attachment (default: \"application/pdf\"). This tool imports PDFs only: the downloaded file must start with %PDF- or the import is rejected, whatever this value says. Full text is extracted only for \"application/pdf\"." - added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +} - added
Input schema / properties / parent_item / patternAdded value: +"^[23456789ABCDEFGHIJKLMNPQRSTUVWXYZ]{8}$"
- Changed
inject_citations2 fields changed- added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
- Changed
search_library2 fields changed- added
Input schema / properties / library_idAdded value: +{ + "description": "Override the configured library ID for this call. Defaults to ZOTERO_LIBRARY_ID env (or ZOTERO_USER_ID). Required when library_type is 'group' and the server is configured for a user library.", + "pattern": "^\\d+$", + "type": "string" +} - added
Input schema / properties / library_typeAdded value: +{ + "description": "Override the configured Zotero library type for this call. Defaults to ZOTERO_LIBRARY_TYPE env (or 'user'). Use to target a different library per call without restarting the server.", + "enum": [ + "user", + "group" + ], + "type": "string" +}
15 tool updates
v1.0.9- First observed
add_items - First observed
add_items_by_doi - First observed
add_linked_url_attachment - First observed
create_collection - First observed
delete_collection - First observed
delete_items - First observed
find_and_attach_pdfs - First observed
get_collection_items - First observed
get_collections - First observed
get_item_fulltext - First observed
get_items_details - First observed
get_user_id - First observed
import_pdf_to_zotero - First observed
inject_citations - First observed
search_library
TDQS
Scored across 15 tools
Most tools have clearly distinct purposes, and the descriptions explicitly differentiate potentially overlapping pairs such as add_items vs add_items_by_doi and add_linked_url_attachment vs import_pdf_to_zotero. The three attachment-related tools (linked URL, imported PDF, find-and-attach OA PDFs) could still be confused at a glance, but the descriptions resolve the boundary reasonably well.
All tool names use snake_case and follow a predictable verb_noun or verb_object pattern (get_collections, create_collection, add_items, delete_items, search_library). Minor multi-verb names like find_and_attach_pdfs still fit the overall convention cleanly.
With 15 tools, the server is well-scoped for Zotero library management: reads, searches, collection operations, item creation, attachment handling, full-text retrieval, citation injection, and deletion are all represented without excessive redundancy. Each tool appears to earn its place.
Core workflows are well covered: collection listing/creation/deletion, item add/search/details/delete, PDF import/fulltext, and citation injection. Notable gaps include updating existing item metadata, moving items between collections, and removing items from a collection without deleting them, but these are workaroundable or secondary for the apparent purpose.
Maintenance
Related MCP Connectors
Zotero MCP server for Claude and ChatGPT: search, citations, safe writes, PDF passages and pages.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Remote MCP server for full read/write access to a Zotero library
Research paper search with real citations and reference formatting for AI assistants
Related MCP Servers
- AlicenseAqualityDmaintenanceThis server allows users to interact with their Zotero library through the Model Context Protocol, providing tools for searching items, retrieving metadata, and accessing full text using natural language queries.31,048 PyPI162MIT
- AlicenseAqualityCmaintenanceA lightweight MCP server that connects AI agents to a local Zotero library for paper management and metadata retrieval. It enables users to search titles and abstracts, browse collections, and automatically ingest papers via arXiv ID or DOI with PDF attachments.821 PyPI20MIT
- AlicenseAqualityDmaintenanceAn MCP server that gives any MCP-compatible assistant access to your Zotero reference library, enabling search, citation, bibliography generation, and .docx processing while keeping Zotero as the ground truth for references.91MIT
- AlicenseNot gradedqualityDmaintenanceConnects your Zotero research library with AI assistants (Claude, ChatGPT, etc.) via the Model Context Protocol, enabling paper review, summaries, annotations, and semantic search both locally and through the web API.MIT