Zotero MCP Server
Provides read-only access to a Zotero library, including collections, item metadata, tags, notes, PDF full text, and PDF annotations/highlights, with full-text search and multi-item comparison capabilities.
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., "@Zotero MCP ServerCompare the methodologies of my Zotero papers on EEG classification"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Zotero MCP Server — Setup Tutorial
This project is a local MCP server that gives Claude direct, read-only access to your Zotero library: metadata, tags, collections, notes, PDF full text (with OCR fallback for scans), and PDF annotations/highlights — plus search and multi-item comparison tools, so you can do NotebookLM-style Q&A and synthesis over your own research library, entirely on your own machine.
Everything below runs locally on your Mac. Nothing in this project uploads your library anywhere; the only thing that leaves your machine is whatever Claude includes in its replies during a normal conversation, exactly as with any other document you show it.
Target locations on your Mac:
Server code:
~/ClaudeFolders/ServerMCP/zotero-mcp/(pick whatever folder you like — this is just the convention used in the setup steps below)Your Zotero data (already exists):
~/Zotero/(zotero.sqlite+storage/with your PDFs)
Origin: the initial idea/prompt for this project came from a post on Reddit; this implementation was then built out interactively with Claude.
Features
Read-only access to your whole Zotero library: collections, items/metadata, tags, notes, PDF full text, and PDF annotations/highlights.
OCR fallback (via Tesseract) for scanned/image-only PDF pages that have no extractable text layer.
Full-text and notes search backed by a separate SQLite FTS5 index this server builds and maintains itself — it never writes to Zotero's own database.
A
compare_itemstool that bundles several items (metadata, abstracts, tags, annotations, optionally full text) in one call, for cross-paper synthesis.Automatically covers Zotero group/shared libraries as well as your personal library — a
list_librariestool lists what's available, and every search/list tool covers all of them by default (or can be scoped to one).Works as an MCP server for both Claude Desktop and the Claude Code CLI.
Optional weekly automatic re-indexing via a macOS
launchdjob, with anindex_last_updatedfreshness timestamp surfaced in relevant tool responses so Claude always tells you how current a full-text result is.Strictly read-only by design: it cannot create, edit, or delete anything in your Zotero library or on disk.
Related MCP server: zotero-mcp
Limitations
macOS only, as packaged. Setup (Terminal steps,
~/Library/...paths) and the optional automatic re-indexing (launchd) are macOS-specific. The Python code itself is plain and cross-platform, so it would likely run on Linux with an equivalent scheduler (cron/systemd) in place of the provided.plist, and probably needs only minor path handling changes for Windows — but neither has been tested, and the setup instructions below assume macOS throughout.Requires Zotero's desktop app to have actually synced your library locally (
zotero.sqlite+storage/with the PDFs present on disk). A library that only exists in the cloud and has never been opened locally won't have anything to read.Only PDF attachments are extracted for full-text search; other attachment types (web page snapshots, EPUBs, images without OCR'd text, etc.) aren't indexed.
Full-text/notes search depends on a separately built index (
rebuild_search_indextool, orscripts/build_index.py) rather than being always live against the current library state — very recent additions won't show up in full-text search until the next (re)index runs (metadata search and collection/item listing are always live, no indexing needed).Designed as a local MCP server: it's meant to run on the same machine as Zotero, launched by a local Claude Desktop app or the Claude Code CLI. Using it from a remote/cloud Claude session requires that session to be bridged to your computer somehow (e.g. a device-bridge feature of whichever Claude product you're using) — this isn't a built-in feature of the server itself.
Group/shared library visibility is limited to whatever your own Zotero account has already synced locally — the server only reads what's there; it can't request, elevate, or otherwise change your Zotero group permissions.
No authentication, multi-user, or remote-access layer of any kind — it's a single local process reading a single local SQLite file, intended for one person's own machine.
0. How it works, in one paragraph
Zotero stores everything in a SQLite database (zotero.sqlite) plus a storage/ folder holding the actual PDF files. This server opens that database read-only (it never writes to it, so it can't corrupt your Zotero library), and exposes a set of tools Claude can call: list collections, list/search items, fetch an item's full metadata, pull the full text of its PDF (extracted on demand with PyMuPDF, OCR'd with Tesseract if it's a scanned page), read your notes and PDF highlights/annotations, and run full-text search across the whole library using a separate search index this server builds for itself (never touching Zotero's own database file). A compare_items tool bundles several items at once so Claude can synthesize across papers in one go.
1. Prerequisites
Open Terminal and check you have what's needed:
python3 --version # should be 3.10 or newerYou'll also want Homebrew for installing Tesseract (OCR engine). Check if it's installed:
brew --versionIf that fails, install Homebrew first: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
2. Get the project files onto your Mac
The project folder is prepared and delivered to you as a zip alongside this tutorial. Once you've saved/unzipped it:
mkdir -p ~/ClaudeFolders
# unzip the delivered project so the result is:
# ~/ClaudeFolders/ServerMCP/zotero-mcp/...If you're setting this up with Claude's help (Cowork, or any session linked to your Mac), it can do this step directly once you grant it access to the ~/ClaudeFolders and ~/Zotero folders — just ask.
Resulting layout:
~/ClaudeFolders/ServerMCP/zotero-mcp/
├── zotero_mcp/ # the Python package (server + data access)
│ ├── server.py # MCP server entry point — the tools Claude calls
│ ├── db.py # read-only Zotero SQLite access
│ ├── fulltext.py # PDF text extraction + OCR fallback
│ ├── indexer.py # full-text search index (separate from Zotero's DB)
│ ├── tools_helpers.py
│ └── config.py
├── scripts/
│ └── build_index.py # CLI: build/update the full-text search index
├── test_connection.py # CLI: sanity-check your Zotero setup
├── pyproject.toml # dependencies + the `zotero-mcp` command
├── requirements.txt
├── .env.example # copy to .env and edit
└── README.md # this file3. Install Tesseract (OCR engine)
You chose to enable OCR, so scanned/image-only PDF pages can be indexed too:
brew install tesseractVerify:
tesseract --version(If you ever want to turn OCR off — faster indexing, but scanned PDFs won't be full-text searchable — set OCR_ENABLED=false in .env, step 5, and skip this.)
4. Create a Python virtual environment and install the server
cd ~/ClaudeFolders/ServerMCP/zotero-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .pip install -e . reads pyproject.toml, installs all dependencies (the mcp SDK, PyMuPDF, pytesseract, Pillow, python-dotenv), and registers a zotero-mcp command inside this virtual environment — that command is what Claude will launch later.
Check it installed cleanly:
which zotero-mcp # should print .../ServerMCP/zotero-mcp/.venv/bin/zotero-mcp5. Configure .env
cp .env.example .envOpen .env in a text editor (or nano .env) and confirm/adjust:
ZOTERO_DATA_DIR=~/Zotero
OCR_ENABLED=true
OCR_LANGUAGE=engZOTERO_DATA_DIR is the only setting you're likely to need to touch — it should already be correct given your setup. Leave the rest at their defaults unless you know you need to change them (see the comments in .env.example for what each one does — e.g. ZOTERO_LIBRARY_ID only matters if you use Zotero group libraries).
Important: if ~/Zotero is synced by any cloud backup/sync tool, make sure all your PDFs have fully finished downloading to this Mac before you build the search index (step 7) — a file that's only a cloud placeholder won't have content to extract yet.
6. Sanity-check the connection
With the virtual environment still active:
python test_connection.pyThis should print your library's item/collection/tag counts and confirm PDF files are found on disk. If it errors, it will tell you what's wrong (wrong path, Zotero database not found, etc.) — fix that before continuing.
7. Build the full-text search index
python scripts/build_index.pyThis extracts text from every PDF (OCR'ing scanned pages) and every note, and stores it in cache/fulltext_index.sqlite inside this project — completely separate from Zotero's own database. Depending on your library size this can take a while the first time (OCR especially); it prints progress as it goes. It's incremental: re-running it later only processes new or changed files.
You don't have to wait for this to finish before wiring the server into Claude — get_item_fulltext and get_item_notes/get_item_annotations work immediately without the index (they extract on demand). Only the search_library tool's fulltext/notes scopes need this index built.
8. Connect it to Claude
Set up whichever client(s) you actually use — Claude Desktop, Claude Code, or both.
8a. Claude Desktop app
Edit (creating it if it doesn't exist):
~/Library/Application Support/Claude/claude_desktop_config.json
If this file already has content (it often does — the desktop app stores other settings there too), don't replace the whole file. Add mcpServers as one more top-level key inside the existing { ... } object, alongside whatever's already there — a JSON file can only have one top-level object, so pasting a second { "mcpServers": ... } block after the closing brace will silently break the file. After editing, validate it before restarting the app:
python3 -m json.tool ~/Library/Application\ Support/Claude/claude_desktop_config.json > /dev/null && echo "valid JSON"If the file is empty or doesn't exist yet, this is the whole content:
{
"mcpServers": {
"zotero": {
"command": "/Users/YOUR_USERNAME/ClaudeFolders/ServerMCP/zotero-mcp/.venv/bin/zotero-mcp"
}
}
}Replace YOUR_USERNAME with your actual Mac username (or run echo ~ in Terminal to get the full path and use that verbatim — use the absolute path, not ~, since the app doesn't expand ~ itself).
Quit and reopen Claude Desktop completely (Cmd+Q, not just closing the window), then check Settings → Developer (or the small hammer/tools icon in a chat) to confirm "zotero" shows as connected.
8b. Claude Code (CLI)
Either add it as a project-scoped .mcp.json in whatever project folder you run claude from:
{
"mcpServers": {
"zotero": {
"command": "/Users/YOUR_USERNAME/ClaudeFolders/ServerMCP/zotero-mcp/.venv/bin/zotero-mcp"
}
}
}...or use the CLI helper (syntax can vary slightly by Claude Code version — run claude mcp add --help if this doesn't match what you have):
claude mcp add zotero /Users/YOUR_USERNAME/ClaudeFolders/ServerMCP/zotero-mcp/.venv/bin/zotero-mcpThen verify with claude mcp list (or the /mcp slash command inside a session).
9. Try it
Start a fresh Claude conversation (Desktop or Code) and try things like:
"List the collections in my Zotero library."
"Search my Zotero library for anything about [your topic]."
"Summarize the paper '[a title from your library]' and pull out anything I highlighted."
"Compare these three papers on [topic] — what do they agree and disagree on?" (give titles/keys, or ask Claude to search first)
"What Zotero libraries do you have access to?" (lists your personal library plus any group/shared libraries)
Claude will call list_collections, search_library, get_item, get_item_fulltext, get_item_annotations, compare_items, etc. as needed — you don't need to name the tools yourself.
Whenever Claude relies on the full-text search index (the fulltext/notes scopes of search_library, or get_library_stats), it's instructed to tell you when that index was last built (e.g. "based on your library as of 2026-09-15 03:00") — so you always know how current the results are. get_item_fulltext, get_item_notes, and get_item_annotations don't need this since they read live, on demand.
10. Group / shared libraries
If you're a member of any Zotero group libraries (shown under "Group Libraries" in the Zotero sidebar), the server sees them automatically — they live in the same local zotero.sqlite as your personal library, just under a different internal ID. No extra configuration is needed.
By default, every tool searches and lists across all your libraries at once — personal and group/shared combined — so "search my library for X" already covers shared libraries too. Call list_libraries to see exactly what's available:
"What Zotero libraries can you see?"This returns each library's library_id, name, and type (user for your personal library, group for a shared one). Pass that library_id to list_collections, list_items, search_metadata, search_library, get_tags, get_library_stats, or rebuild_search_index to scope a call to just one library — e.g. "only search my 'Lab Reading Group' group library." Item-level tools (get_item, get_item_fulltext, get_item_notes, get_item_annotations, compare_items) work the same regardless of which library an item is in — just its key is enough.
Full-text/notes search coverage for group libraries depends on the search index having been built for them, same as your personal library — rebuild_search_index and scripts/build_index.py cover every library by default, and the weekly automatic reindex (next section) picks up new shared items too. If you notice pdfs_indexed/notes_indexed looking low for a group library right after joining it or after a teammate adds new items, run python scripts/build_index.py --full once, or ask Claude to call rebuild_search_index.
Note on permissions: this server only reads whatever your own Zotero desktop app already has synced locally, exactly as Zotero enforces for you — it doesn't grant, request, or bypass any group-library permissions. If a group's items aren't showing up, check that your Zotero library sync (Settings → Sync) includes that group and that it has finished syncing.
11. Keeping the index fresh
Manually
cd ~/ClaudeFolders/ServerMCP/zotero-mcp
source .venv/bin/activate
python scripts/build_index.pyAutomatically, once a week
A weekly launchd job (com.zoteromcp.buildindex.plist, included in this project) handles this for you. launchd is the standard macOS scheduler — more reliable here than cron, and it catches up a missed run if your Mac was asleep at the scheduled time.
Edit the paths in the plist first — open
com.zoteromcp.buildindex.plistand replace everyYOUR_USERNAMEwith your actual Mac username (runwhoamiin Terminal if unsure), so the four absolute paths match your real project location.Install it:
cp ~/ClaudeFolders/ServerMCP/zotero-mcp/com.zoteromcp.buildindex.plist ~/Library/LaunchAgents/ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.zoteromcp.buildindex.plist(If
bootstraperrors saying the job is already loaded, that's fine — it means a previous attempt already registered it. If your macOS version doesn't recognizebootstrap, use the olderlaunchctl load -w ~/Library/LaunchAgents/com.zoteromcp.buildindex.plistinstead.)Test it immediately rather than waiting for Sunday 3am:
launchctl start com.zoteromcp.buildindex sleep 5 cat ~/ClaudeFolders/ServerMCP/zotero-mcp/cache/reindex.logYou should see the same progress output
build_index.pyprints when run manually.Check it's scheduled:
launchctl list | grep zoteromcp
The job runs every Sunday at 03:00 local time by default. To change the day/time, edit the Weekday/Hour/Minute values in the plist (Weekday 0 = Sunday, 1 = Monday, etc.) and re-run the bootstrap/load command.
To remove it later: launchctl bootout gui/$(id -u)/com.zoteromcp.buildindex then delete the file from ~/Library/LaunchAgents/.
12. Troubleshooting
"Zotero database not found" — check
ZOTERO_DATA_DIRin.envpoints at the folder that directly containszotero.sqlite."database is locked" — rare; the server automatically falls back to a temporary snapshot copy when this happens. If you see it constantly, close Zotero while indexing.
PDFs "not found on disk" — usually means a sync tool hasn't finished downloading that file yet, or the attachment is a "linked file" pointing somewhere else.
test_connection.pyreports how many PDFs resolved.OCR not running / errors mentioning tesseract — confirm
tesseract --versionworks in Terminal in general (not just inside the venv); pytesseract just shells out to it.Claude Desktop doesn't show the server as connected — double-check the
commandpath inclaude_desktop_config.jsonis absolute (starts with/Users/..., not~), that the JSON is valid (see step 8a), and that you fully quit (Cmd+Q) and reopened the app — closing just the window can leave the old process running in the background.Server shows connected but tool calls fail on launch — check
~/Library/Logs/Claude/mcp-server-zotero.logfor a Python traceback. If it's a relative-path/working-directory issue (e.g. something resolves to/instead of the project folder), the app doesn't guarantee a working directory when launching the server —config.pyalready resolves.envpaths against the project root rather than the process's cwd to avoid exactly this, but it's the first thing to suspect if you ever add a new relative path setting.Indexing is slow — normal for a large library, especially with OCR on scanned PDFs. It only needs to happen once per file; subsequent runs skip unchanged files.
A collection you deleted in Zotero still shows up somewhere — shouldn't happen: the server excludes any collection with a pending trash tombstone (Zotero's
deletedCollectionstable) fromlist_collectionsand from an item's collection list. If you do see a stale one, it's worth re-checking this logic against your Zotero version's schema.
13. Getting help
If something doesn't match this tutorial exactly — a different Claude Desktop version, a different macOS version, an unusual Zotero setup (group libraries, WebDAV-synced attachments, etc.) — the most useful things to share when asking for help are: the output of test_connection.py, and the tail of ~/Library/Logs/Claude/mcp-server-zotero.log after a restart attempt.
License
MIT — see LICENSE.
Available Tools
13 toolscompare_itemsA
Fetch metadata (and optionally truncated full text) for several items
in one call, so you can compare/synthesize across them — arguments,
methods, findings, etc. Items can come from different libraries
(personal and/or group/shared) in the same call — each entry's
library_id says which one it's from. Set include_fulltext=True to
also pull each item's PDF text (capped at max_chars_per_item per
item to keep the combined response manageable); leave it False to
compare on metadata, abstracts, tags and annotations only.
| Name | Required | Description | Default |
|---|---|---|---|
| item_keys | Yes | ||
| include_fulltext | No | ||
| max_chars_per_item | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It discloses that fulltext is truncated, capped per item by max_chars_per_item, and that combined response size is intentionally managed. It also reveals that items can span libraries and that each entry's library_id identifies its source. It could mention error behavior or permissions, but the core behavior is transparent.
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 dense but mostly efficient. The main action is front-loaded, and the follow-up sentences explain the optional behavior and cross-library capability. A slight restructuring into shorter sentences would improve scannability, but every sentence contributes useful 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?
For a batch metadata/fulltext fetching tool with no output schema, the description covers the essential invocation context: what is fetched, when fulltext is included, how it is truncated, and that items can span libraries. It does not describe the response shape or error handling, but an agent has enough to invoke and interpret the call 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 0%, so the description fully compensates. It explains item_keys (several items), include_fulltext (whether to pull PDF text), and max_chars_per_item (per-item cap to keep response manageable). It also clarifies that when fulltext is off, comparison is on metadata, abstracts, tags, and annotations only.
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: 'Fetch metadata (and optionally truncated full text) for several items in one call.' It clearly separates this from single-item tools like get_item and get_item_fulltext by emphasizing multi-item batching and comparison/synthesis. The purpose is unmistakable and actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool: when you need to compare or synthesize across several items, including across libraries. It also differentiates the two modes (with or without fulltext). However, it does not explicitly state exclusions, such as 'use get_item for a single item' or 'use get_item_fulltext when you need full text for one item.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_itemA
Get full metadata for one item: all bibliographic fields, creators,
tags, the collections it belongs to, its abstract, and a summary of its
child notes and attachments (so you know what's available to fetch next
with get_item_fulltext / get_item_notes / get_item_annotations). Works
for items in any library — personal or group/shared — the response's
library_id says which one this item lives in.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden. It does meaningfully disclose that child content is returned only as a summary, that the tool works across personal and group/shared libraries, and that the response's library_id identifies the owning library. It does not cover error/edge cases or auth, but the read-only nature is clear from the wording.
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?
Two dense sentences front-load the core action and then add return-scope and library-scope details. The parenthetical is slightly long, but every clause contributes behavior or routing value; there is 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 one-parameter metadata fetch with no output schema, the description covers what the response contains, what it deliberately omits, how library ownership is conveyed, and which sibling tools to use next. An agent has enough to invoke it correctly and interpret 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 schema provides only the parameter name and type with zero description coverage, so the description must compensate. It adds that the parameter selects exactly one item and that the item may live in any library, with library_id in the response identifying the library. It does not explain the item_key format, but for a single self-describing required parameter this is adequate.
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 ('Get full metadata for one item') and enumerates exactly what is included: bibliographic fields, creators, tags, collections, abstract, and a summary of child notes and attachments. It also distinguishes itself from sibling get_item_fulltext / get_item_notes / get_item_annotations by framing them as next steps rather than the metadata call.
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 clearly implies this tool is for retrieving a single item's metadata rather than listing or searching, and it adds explicit follow-up routing to fulltext/notes/annotations tools. It does not state explicit when-not-to-use conditions or compare against list_items/search_metadata, so it falls just short of full guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_item_annotationsA
Get every PDF annotation (highlights, notes, underlines) the user has made on an item's PDF: the highlighted/underlined text, any comment the user added, the highlight color, and the page label — in reading order. This surfaces what the user found important, which is often more useful for synthesis than the raw PDF text.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses the output scope (all annotations, reading order) and the value proposition, but it does not mention whether this is a read-only operation, whether it requires specific permissions, whether it can fail (e.g., item has no PDF), or whether it returns an empty list. It is not misleading, but it leaves some behavioral gaps.
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 two sentences, front-loads the core action and output, and the second sentence adds a valuable use-case rationale without bloat. Every sentence earns its place.
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 single-parameter read tool with no output schema, the description is quite complete: it lists the output fields, ordering, and the use case. It lacks explicit failure/edge-case behavior (e.g., no PDF, no annotations) and does not name sibling tools, but given the low complexity, it is nearly 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 0%, so the description must compensate. It explains what item_key refers to implicitly ('an item's PDF') but does not define item_key format, how to obtain it, or any constraints. The description adds context about the item's PDF but the parameter itself is only documented by its name and type. Baseline 3 is appropriate because the single parameter is self-explanatory in context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Get') and resource ('every PDF annotation on an item's PDF'), enumerates the annotation types (highlights, notes, underlines) and the exact data returned (highlighted text, comment, color, page label, reading order). It clearly distinguishes itself from siblings like get_item_fulltext and get_item_notes by focusing on user annotations rather than raw text or notes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use it: when the user's annotations are more useful for synthesis than raw PDF text. It contrasts with raw PDF text, which helps an agent choose between this and get_item_fulltext. However, it does not explicitly name sibling alternatives or state when not to use it, so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_item_fulltextA
Extract the full text of an item's PDF attachment(s), with [p. N]
page markers so you can cite specific pages back to the user. Optionally
restrict to a page range with page_start/page_end (1-indexed,
inclusive) — useful for a long document where you only need a section.
max_chars caps the response size (defaults to a safe per-item limit);
the result says so and suggests narrowing the page range if truncated.
This extracts on demand — it does not require the search index to have
been built first.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes | ||
| page_end | No | ||
| max_chars | No | ||
| page_start | No |
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 on-demand nature, absence of a search-index prerequisite, page-marker format, and truncation behavior with max_chars. It does not cover multi-PDF aggregation or error/permission cases, but the core side effects and result caveats are present.
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 sentences front-load the core purpose and then layer parameter and behavior details without redundant filler. Each sentence adds value, so it is efficiently structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no annotations and no output schema, the description covers what the tool returns (full text with page markers), how to constrain output, and how truncation is signaled. It is slightly incomplete about the exact shape when an item has multiple PDF attachments and about failure modes, but it is enough for correct invocation.
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 descriptions are 0%, so the prose must compensate, and it does: page_start/page_end are documented as 1-indexed and inclusive, max_chars is documented as a cap with a default and truncation cue. item_key is left obvious from the tool name, but all optional parameters receive meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific action verb ('Extract') and names the exact resource ('an item's PDF attachment(s)'), and it adds page-marker behavior that distinguishes it from metadata/notes/search siblings. An agent can confidently choose this over get_item, get_item_notes, or get_item_annotations.
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 explains when a page range is useful and clarifies that extraction is on-demand and does not depend on the search index, which routes usage away from rebuild_search_index/search_library. It does not explicitly name alternative tools for metadata or notes, but the context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_item_notesA
Get all notes attached to an item (child notes), or the content of a
standalone note if item_key is itself a note. Note content is
converted from Zotero's internal HTML to plain text.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses that content is converted from HTML to plain text, which is a notable behavioral trait. However, it doesn't specify the return structure, error cases, or whether notes are sorted, leaving some gaps. The conversion detail adds value beyond basics.
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 concise, two sentences, with no fluff. It front-loads the primary function of getting child notes, then clarifies the standalone note case and the conversion behavior. Each sentence adds value, fitting within typical token limits.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with a single parameter and no output schema, the description covers the essential semantics: what it does, the special case, and a key transformation. It lacks details on return format, but given the simplicity, it is fairly complete. The conversion to plain text is a useful addition.
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 0%, meaning the description must explain the item_key parameter. The description does not elaborate on the parameter format or special cases beyond the fact that it can be a standalone note's key. This adds some meaning but is minimal, so a 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 clearly states what the tool does: it retrieves notes attached to an item, and also handles standalone notes. It uses a specific verb 'Get' and a clear resource 'item notes'. It distinguishes between two cases, which helps clarify its purpose, though it does not explicitly compare to sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use the tool: when you need notes attached to an item or the content of a standalone note. It does not explicitly state when not to use it or mention alternatives, but the context of retrieving notes is clear, and siblings like get_item_annotations are distinct enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_library_statsA
Overall library stats: item/collection/tag counts and full-text
index coverage (how many PDFs/notes are searchable via
search_library's fulltext/notes scopes right now), including
fulltext_index.index_last_updated. Omit library_id to get
combined totals across your personal library and every group/shared
library, with a by_library breakdown (see list_libraries for
names); pass a specific library_id to scope to just one. Mention
the index_last_updated timestamp to the user whenever you report
these stats or otherwise rely on the full-text index.
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses aggregation behavior, the meaning of full-text coverage, and the presence of index_last_updated. It does not explicitly state read-only or side-effect-free, but the 'stats' framing makes that reasonably clear. The instruction to surface the timestamp adds useful behavioral context.
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, each with distinct value: the stat contents, the parameter behavior, and the user-facing timestamp instruction. Front-loaded with the core purpose. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema and no annotations, the description provides enough conceptual detail about return contents (counts, coverage, timestamp, breakdown) and cross-references list_libraries for names. An agent can call the tool and correctly interpret the results.
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 0%, so the description must compensate. It fully explains the library_id parameter: omitting it yields combined totals across personal and shared libraries with a by_library breakdown, while passing it scopes to one library. This is rich semantic meaning that the bare schema (nullable integer default null) completely lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific noun phrase 'Overall library stats' and enumerates the exact resources involved (items, collections, tags, full-text index coverage). It also ties the coverage to search_library's fulltext/notes scopes, which distinguishes it from sibling tools like list_items or list_collections.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete parameter guidance: omitting library_id returns combined personal/shared totals with a by_library breakdown, while passing a specific id scopes to one library. It also points to list_libraries for names and explicitly instructs to mention the index_last_updated timestamp. However, it does not explicitly state when NOT to use this tool in favor of a sibling, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_tagsA
List tags. If item_key is given, list that item's own tags. If
omitted, list every tag used anywhere in the library with how many
items carry it, most-used first — handy for discovering the user's own
taxonomy before filtering list_items/search_library by tag. When
item_key is omitted, defaults to aggregating across every library
(personal plus all group/shared libraries); pass library_id (see
list_libraries) to scope to just one.
| Name | Required | Description | Default |
|---|---|---|---|
| item_key | No | ||
| library_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral burden, and it does so well: it discloses default aggregation across all libraries, item-count reporting, most-used-first ordering, and the optional library scope. It does not explicitly label the operation as read-only, but 'List tags' plus the aggregate semantics make the behavior clear.
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 the core action, then efficiently branches into the two parameter modes. Each sentence adds necessary information, and the downstream-use tip is directly relevant, not 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?
Given no annotations and no output schema, the description fully equips an agent to call the tool correctly: parameter behavior, default scope, return details (counts, ordering), and related tool routing are all covered. There is no critical missing context.
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 0%, so the description must fully compensate, and it does. It explains item_key filters to an item's tags, omission triggers global aggregation, and library_id scopes the aggregation to a single library with a pointer to list_libraries. This adds meaningful semantics beyond the bare schema names.
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 ('List tags') and then disambiguates two modes: item-specific tags when item_key is given, and global tag aggregates when omitted. This makes it easy for an agent to understand what get_tags does and to distinguish it from sibling tools like list_items or get_item.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use each mode and explicitly positions get_tags as a prerequisite for 'discovering the user's own taxonomy before filtering list_items/search_library by tag.' It also names list_libraries for scoping. It stops short of an explicit when-not-to-use statement, but the usage intent is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_collectionsA
List every collection (and sub-collection) in the Zotero library as a
flat list with parent links, so Claude can build or walk the collection
tree. Each entry has key, name, library_id, and parent_key (null
for top-level collections). Defaults to every library (your personal
library plus all group/shared libraries); pass library_id (see
list_libraries) to scope to just one.
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does so well: it discloses the flat-list format, the per-entry fields, that parent_key is null for top-level collections, and the default all-library scope. It does not mention pagination, ordering, or error behavior, but for a simple read-only list tool this is adequate transparency.
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 the main purpose, then elegantly packs output fields, default behavior, and a cross-reference into a compact structure. Every sentence earns its place and no detail 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?
Given there is no output schema, the description still explains the return entry fields and hierarchy semantics, plus the single parameter. It is complete enough for an agent to call the tool correctly and interpret results. Minor omissions like pagination or result ordering do not materially hurt usability.
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 schema has 0% description coverage, so the description must compensate. It does: it explains that library_id is optional, controls scope, and points to list_libraries for valid values. This adds meaning beyond the bare schema definition of an optional integer/null parameter.
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 ('List') with a precisely scoped resource ('every collection (and sub-collection)') and even explains the intended use ('so Claude can build or walk the collection tree'). It also clarifies the output shape with parent links, making it easy to distinguish from list_items and 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?
The description explicitly explains the default behavior ('Defaults to every library') and how to narrow it ('pass library_id (see list_libraries) to scope to just one'). It does not name alternatives to exclude, but it does reference list_libraries for valid parameter values, which is strong practical guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_itemsA
List items (papers, books, etc. — not notes/attachments) in the
library, most recently modified first. Filter by collection_key
(from list_collections), tag (exact tag name), and/or item_type
(e.g. 'journalArticle', 'book', 'conferencePaper'). Use limit/offset
to page through a large library. Defaults to every library (personal
plus all group/shared libraries); pass library_id (see
list_libraries) to scope to just one. Returns key, title, library_id,
item_type, creators_summary, year, dateAdded, dateModified for each
item.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | ||
| limit | No | ||
| offset | No | ||
| item_type | No | ||
| library_id | No | ||
| collection_key | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden and does so thoroughly: it discloses sort order ('most recently modified first'), default scope ('every library ... personal plus all group/shared libraries'), exclusions ('not notes/attachments'), and the exact return fields. This goes well beyond the bare schema and gives an agent an accurate behavioral model.
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 dense but every sentence adds necessary information: behavior, ordering, filters, paging, scope, and output shape. It is front-loaded with the core purpose and avoids redundant restatement of the tool name or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema and no annotations, the description gives enough context for correct invocation: it defines the item universe, filter semantics, pagination strategy, library scoping behavior, and return fields. An agent can select and call this tool without needing to inspect sibling tools or make risky assumptions.
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 0%, but the description compensates by explaining every parameter: tag, limit, offset, item_type, library_id, and collection_key all receive semantic meaning. It also provides concrete examples like 'journalArticle' and tells the agent where to get valid collection_key and library_id values.
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: 'List items ... in the library', and immediately clarifies scope by excluding notes/attachments and defining item categories. It also distinguishes itself from siblings like get_item and search_metadata by describing the list behavior and return fields.
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 explicitly explains when to use filters, how to page with limit/offset, and how to scope to a specific library via library_id. It also references sibling tools list_collections and list_libraries as sources for parameter values, giving clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_librariesA
List every Zotero library this server can see: your personal
library ("My Library", library_id 1) plus every group/shared library
you belong to, with its library_id, name, and type. Pass a group's
library_id as the library_id argument to list_collections,
list_items, search_metadata, get_tags, search_library, or
get_library_stats to scope to that shared library specifically —
otherwise those tools already search across every library by
default. Item-level tools (get_item, get_item_fulltext,
get_item_notes, get_item_annotations, compare_items) work the same
regardless of which library an item lives in — just pass its key.
| 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 must disclose behavioral traits. It does not explicitly state that the tool is read-only or non-destructive, but the nature of listing is inherently read-only. It does not mention potential side effects or limitations, but the absence of parameters and side effects allows a 4.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized and front-loaded with the core purpose. It is four sentences, but each adds essential context about usage with siblings. It could be slightly more concise, but every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no parameters, no output schema, and no annotations, so the description is the only source of information. It fully explains what the tool does, what it returns, and how to use the result with other tools. Nothing essential 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?
The tool has zero parameters, and the description clearly explains the output (library_id, name, type) which is the primary useful information. Schema coverage is 100% but with no parameters, the description adds meaning by explaining what the returned data contains.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists all Zotero libraries visible to the server, including personal and group libraries, and specifies what it returns (library_id, name, type). It distinguishes itself from sibling tools by explaining how library_id is used by other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance: it explains when to use this tool (to discover library IDs) and how to use the result with other tools. It also clarifies that item-level tools don't need library scoping, preventing misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rebuild_search_indexA
(Re)build the full-text search index used by search_library's
'fulltext' and 'notes' scopes. Indexes every library — personal plus
every group/shared library — by default, so shared libraries become
full-text searchable automatically; pass library_id (see
list_libraries) to index just one. Incremental by default
(full=False): only new or changed PDFs/notes since the last run are
processed, which is fast. Pass full=True to wipe and rebuild
everything from scratch. For a large library, prefer running
python scripts/build_index.py in a terminal the first time, since
that gives you progress output; this tool is best for small
incremental top-ups from within a conversation.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No | ||
| library_id | No |
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 so thoroughly. It discloses incremental-by-default behavior, the full=True wipe-and-rebuild mode, cross-library indexing, and the practical tradeoff about progress output for large libraries.
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 the tool's core purpose and is dense with useful behavior, usage, and parameter guidance. Every sentence contributes actionable information without 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 complex operation with no output schema and no annotations, the description is remarkably complete. It covers default behavior, parameter effects, performance characteristics, and when an alternative approach is preferable, leaving little ambiguity for an agent deciding how to invoke it.
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 0%, so the description must explain the parameters itself. It explains both parameters: library_id scopes indexing to a single library and references list_libraries, while full controls incremental versus full rebuild. This fully compensates for the schema's lack of descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific action—rebuilding the full-text search index—and connects it to the exact scopes ('fulltext' and 'notes') of the sibling search_library tool. This makes the tool's role unmistakable and distinguishes it from other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: it is best for small incremental top-ups within a conversation, and for large libraries it recommends running 'python scripts/build_index.py' in a terminal instead. It also clarifies when to use library_id versus the default all-libraries behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_libraryA
Search across the whole library. scope is one of:
'metadata' (titles/authors/abstracts, always available),
'fulltext' (inside PDF text, requires the index to have been built —
see rebuild_search_index / scripts/build_index.py),
'notes' (inside your notes, same index requirement), or
'all' (default: every scope, merged).
Searches across every library — your personal library and all
group/shared libraries — by default; pass library_id (see
list_libraries) to scope to just one.
Each fulltext/notes result includes a library_id, a snippet, and the
exact page (for PDFs) so you can cite it, plus the parent item_key to
fetch more with get_item / get_item_fulltext.
IMPORTANT: whenever you report fulltext or notes results to the user,
mention the index_last_updated timestamp included in the response
(e.g. "based on your library as of ") — those two scopes
come from a separately built search index, not a live query, so the
user should know how current it is. metadata-only results don't need
this since they're always live.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| scope | No | all | |
| library_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does so thoroughly. It discloses that fulltext/notes rely on a separately built index rather than live queries, that results include index_last_updated, and that the agent must surface that timestamp to users. This exceeds what annotations would typically provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence adds necessary operational context. It front-loads the core purpose, then structures scope, library scoping, result shape, and the index freshness caveat in a logical order.
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 no output schema, the description still conveys what results contain, how to fetch more details, when to expect live versus indexed data, and what to tell the user about freshness. An agent has enough context to invoke the tool correctly and interpret its output.
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 0%, so the description must compensate. It thoroughly explains scope values and library_id behavior, and it implies query semantics through the overall search purpose. It does not elaborate on limit, but the schema title and default make it self-explanatory.
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: 'Search across the whole library', and clarifies the operation by enumerating the four scope modes. It distinguishes itself from siblings by covering metadata, fulltext, notes, and all-library scope rather than a simple metadata or item listing.
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 context for when to use the tool: use it for cross-library search, pass library_id to narrow to one library, and use rebuild_search_index when fulltext/notes are unavailable. It does not explicitly say 'use search_metadata instead for metadata-only queries', but it does explain the metadata scope and points to related tools for fetching results.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_metadataA
Fast search over item titles, abstracts, and creator names (does NOT
search inside PDFs or notes — use search_library or search_fulltext for
that). Good for quickly locating an item you already know roughly.
Searches your personal library and every group/shared library by
default; pass library_id (see list_libraries) to scope to one.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| library_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and discloses important behavioral traits: it is fast, searches personal plus all group/shared libraries by default, and deliberately excludes PDFs and notes. It doesn't describe return shape or match semantics, but the key scope and limitation boundaries are clear.
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 with no filler: each sentence adds necessary scope, exclusions, alternatives, or invocation guidance. The most important limitation is front-loaded with 'does NOT search inside PDFs or notes.'
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 search tool with no output schema and no annotations, the description covers the main operational needs: search fields, default scope, scoping parameter, and alternatives. It omits a few details like limit semantics and result-return expectations, but nothing critical blocks correct use.
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 0%, but the description compensates by defining the query semantics (titles, abstracts, creator names) and explaining library_id (scope to one library, see list_libraries). The limit parameter is not described in prose, though its schema default is visible.
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: 'search over item titles, abstracts, and creator names.' It also explicitly distinguishes itself from full-text search, so an agent can tell what this tool does and does not cover.
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 guidance ('quickly locating an item you already know roughly') and clear alternatives for content search. Minor deduction because it names 'search_fulltext', which is not present in the sibling list, making the alternative routing slightly unreliable.
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.
13 tool updates
v0.1.0- First observed
compare_items - First observed
get_item - First observed
get_item_annotations - First observed
get_item_fulltext - First observed
get_item_notes - First observed
get_library_stats - First observed
get_tags - First observed
list_collections - First observed
list_items - First observed
list_libraries - First observed
rebuild_search_index - First observed
search_library - First observed
search_metadata
TDQS
Scored across 13 tools
Most tools have clearly distinct purposes (list libraries/collections/items, get item content, search, rebuild index, stats). However, search_metadata and search_library with scope='metadata' overlap, and compare_items partially duplicates get_item + get_item_fulltext as a batch operation. Detailed descriptions mitigate but do not eliminate the ambiguity.
All 13 tools follow a consistent verb_noun snake_case pattern: list_* for enumerating multiple entities, get_* for retrieving specific content, search_* for the two search tools, plus rebuild_search_index, compare_items, and get_library_stats. The naming is highly predictable and readable.
13 tools is well within the ideal range for this server's scope. Each tool addresses a distinct part of the Zotero read-only workflow (library discovery, collection/item listing, metadata retrieval, fulltext/notes/annotations extraction, search, indexing, comparison, and stats), with no obvious bloat.
The tool set provides complete read-only coverage of the Zotero research workflow: discover libraries and collections, list and filter items, fetch full metadata and child content, search across all scopes, maintain the search index, compare multiple items, and retrieve library statistics. There are no dead ends for the stated purpose.
Maintenance
Related MCP Connectors
Academic literature search, retrieval, and private library management on top of OpenAlex.
Federated search of books and papers, BibTeX/RIS citations, open-access retrieval and reading.
Read-only access to your Citlyze workspace: AI search visibility, citations, and recommendations.
Zotero MCP server for Claude and ChatGPT: search, citations, safe writes, PDF passages and pages.
Related MCP Servers
- AlicenseBqualityDmaintenanceIntegrates with Zotero's local API to search your reference library, retrieve bibliographic details, and extract full text from PDF attachments.41MIT
- AlicenseAqualityDmaintenanceIntegrates with Zotero's local API to search, retrieve, read PDFs, and add items by DOI from your Zotero library.53MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to search, read, and manage Zotero references locally with customizable research workflows.933 PyPI4MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to search, access, and interact with your Zotero research library, including semantic search, metadata retrieval, PDF annotations, and library management.MIT