Zotero MCP Server vs. Beaver: Two Approaches to AI-Assisted Literature Work
Anyone working with Zotero and AI usually knows only the plugin route: Beaver and similar extensions run directly inside the Zotero client and answer questions about your own library. For larger research projects – a dissertation, a systematic review, a book with hundreds of sources – this approach reaches its limits. The alternative: an MCP server (Model Context Protocol) that exposes Zotero as a toolset to an external AI assistant, rather than running as a plugin inside the Zotero window. This article compares both architectures.
Developed by Michael Logies in collaboration with Kimi Code, August 2026. Updated August 2026: architecture analysis of zotero-mcp-server by DeepSeek V4 Flash (Qwen Code).
📋 Contents
- Why MCP instead of a plugin? – context, model choice, batch processing
- Architecture – Zotero host and AI client separated via SSH
- Required software
- What Kimi Code can do via the MCP server – capabilities overview
- Tools compared – zotero-mcp-server, Zoteus, cli-anything-zotero, pyzotero
- OpenAlex and semantic search – fetching full texts after discovery
- Addendum: pyzotero – for batch processing
- Benchmark results – all four tools, sequential + parallel
- ↳ Reproduction: 11 Aug vs. 16 Aug 2026
- Prompt strategy – standing instructions instead of repetition
- Cost and privacy
- Tips and pitfalls – including local API vs. zotero.org
- Comparison: plugin vs. MCP server
- When is the MCP route worth it?
- Benchmark script to reproduce
- Tools and links
Why MCP instead of a plugin?
Beaver and comparable plugins are excellent for quick questions inside Zotero. For a multi-day or multi-week research project, however, I wanted something different:
- Continuous context. A chapter or research section is not a single chat turn, but a sequence of searches, readings, drafts, corrections, and build steps. An MCP client (in my case Kimi Code) keeps the project context across many sessions and returns to the same sources when needed.
- Free choice of model. The MCP layer is model-agnostic. I can switch between Kimi K2.6, Kimi K2.7-code, Claude Code Sonnet, and others without changing anything on the Zotero side.
- Custom pre- and post-processing. The MCP server returns raw text and metadata that can be fed through custom Python scripts, Pandoc filters, or build pipelines.
- Batch processing. With
pyzoteroas a supplement, thousands of entries can be read or updated where the MCP layer would be too slow.
The price is more setup effort: the MCP route is not point-and-click, but more of a developer pipeline. In practice, however, the effort is smaller than it sounds here: in this project, Kimi Code installed and configured the four tools (zotero-mcp-server, Zoteus, cli-anything-zotero and pyzotero) about 90 % automatically. The user mainly had to provide the Windows prerequisites – Zotero with the local API enabled, SSH access, and optionally an SMB share – while the Linux side, MCP integration, and most connection details were set up automatically. If you have a coding agent such as Kimi Code or Claude Code installed, you can hand it this page and say: “Set this up for me the same way.”
Architecture
+-------------------------------------------------+
| Linux machine (AI client) |
| - MCP client (e.g. Kimi Code) |
| - own analysis/build pipeline (Python, possibly |
| Pandoc/XeLaTeX) |
| - project memory (Markdown + optional Memory-MCP)|
+-------------------------------------------------+
|
| SSH (stdio)
v
+-------------------------------------------------+
| Windows PC (Zotero 9) |
| - zotero-mcp-server |
| - local Zotero API enabled |
| - full PDF library |
+-------------------------------------------------+Important settings on the Windows side:
- Enable local API in Zotero: Activate Edit → Preferences → Advanced → “Allow other applications on this computer to communicate with Zotero”. Internally this sets both options
extensions.zotero.httpServer.enabledandextensions.zotero.httpServer.localAPI.enabledtotrue. - Alternatively, set these two values directly in the advanced configuration (
about:config). - Set environment variable
$env:ZOTERO_LOCAL = "true"before starting the server. In PowerShell this looks like:
In a one-line SSH command, variables are set before the server call:$env:ZOTERO_LOCAL = "true" zotero-mcp-server$env:ZOTERO_LOCAL="true"; $env:ZOTERO_API_KEY="..."; $env:ZOTERO_LIBRARY_ID="..."; $env:ZOTERO_LIBRARY_TYPE="user"; zotero-mcp-server
The server is started from the Linux client via SSH. All MCP communication runs through this SSH connection (stdio), so no additional ports need to be opened. The complete setup on the Linux side – MCP client, Python environment, and especially the SSH connection to the Windows host – was done by Kimi Code itself.
Stable HTTP Configuration (from August 2026)
Update 17.8.2026: The architecture was changed from SSH stdio to HTTP transport. The Zotero MCP server temporarily ran as a permanent HTTP service on port 8000, which was more stable and faster. As of late August 2026, the zotero-mcp-server is no longer used – see the tool comparison section for details.
Why HTTP instead of SSH?
The original architecture used SSH stdio transport. The MCP server was started via an SSH connection that could break due to inactivity or network problems. Since August 2026, the server runs as an HTTP service:
- Permanent availability: Server starts automatically with Windows via a VBS file in the startup folder
- No CMD windows: The VBS file starts the server hidden in the background
- Stable connection: No SSH overhead, no timeouts, no breaking sessions
- Faster: Direct HTTP connection instead of SSH tunnel
- Multi-client: Multiple AI agents can access simultaneously
New Architecture
+-------------------------------------------------+
| Linux machine (AI client) |
| - MCP client (Qwen Code, Kimi Code, Claude) |
| - own analysis/build pipeline |
| - project memory (Memory MCP) |
+-------------------------------------------------+
|
| HTTP (port 8000)
v
+-------------------------------------------------+
| Windows PC (Zotero 9) |
| - zotero-mcp-server (HTTP server port 8000) |
| - local Zotero API enabled |
| - complete PDF library |
| - autostart via VBS file |
+-------------------------------------------------+Setup on Windows
1. Create batch file (with PowerShell, not with echo!):
'set ZOTERO_LOCAL=true',
'set ZOTERO_API_KEY=YOUR_API_KEY',
'set ZOTERO_LIBRARY_ID=YOUR_LIBRARY_ID',
'set ZOTERO_LIBRARY_TYPE=user',
'zotero-mcp-server serve --transport streamable-http --host 0.0.0.0 --port 8000' |
Out-File -FilePath C:\Users\YOUR_USER\start-zotero-mcp.bat -Encoding ASCII⚠️ Important: Always create batch files with PowerShell, not with echo. The Windows echo command adds spaces at the end of lines, which invalidates the API key and causes the following error:
Illegal header value b'Bearer DEIN_ZOTERO_API_KEY_HIER '2. VBS file for hidden start:
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "C:\Users\YOUR_USER\start-zotero-mcp.bat", 0, False3. Enable autostart: Copy VBS file to Windows startup folder:
C:\Users\YOUR_USER\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\4. Open firewall:
New-NetFirewallRule -DisplayName "Zotero MCP Server" -Direction Inbound -Protocol TCP -LocalPort 8000 -Action AllowConfiguration on Linux
Qwen Code settings.json:
{
"mcpServers": {
"zotero": {
"httpUrl": "http://192.168.0.28:8000/mcp"
}
}
}After configuration, reconnect the MCP server:
qwen mcp reconnect --allComparison: SSH vs. HTTP
| Criterion | Before (SSH) | After (HTTP) |
|---|---|---|
| Transport | SSH stdio | HTTP (port 8000) |
| Start | Manual via SSH | Autostart (VBS) |
| Stability | SSH breaks possible | Permanently stable |
| Visibility | CMD window on Windows | Hidden |
| Performance | SSH overhead | Direct, fast |
| Multi-client | Difficult | Easy |
Required software
Windows (Zotero host): Zotero 9 with local HTTP API enabled, zotero-mcp-server (installed via uv), an SSH server reachable from the Linux client.
Linux (AI/build host): an MCP client (e.g. Kimi Code), Pandoc and XeLaTeX if desired for a custom build pipeline, Python 3 with pyzotero, an SSH key for passwordless login to the Windows host.
Zotero Plugins for AI Collaboration
In addition to the MCP servers and CLI tools, the following Zotero plugins are active and essential for working with AI agents:
- Better BibTeX for Zotero (v9.0.61) – BibTeX/BibLaTeX export. Used for reference management when writing the book; AI agents can reference Zotero items via citation keys.
- Better Notes for Zotero (v3.3.3) – Advanced note management directly in Zotero.
- Beaver (v0.23.3) – Academic Research Agent for AI-assisted literature work within the Zotero client.
- Zoplicate (v5.0.9) – Duplicate detection and management.
- Translate for Zotero (v2.4.7) – Translation of PDFs, EPubs, metadata, and notes.
- Zutilo (v4.2.2) – Macros and keyboard shortcuts. Outputs each item's Zotero ID, which simplifies communication with AI agents.
What Kimi Code can do via the MCP server
Tools compared: four ways into Zotero
After the original zotero-mcp-server worked well but occasionally hit limits with large PDFs, notes, and import tasks, I tested two more tools in August 2026: Zoteus and cli-anything-zotero. Both also access the local Zotero instance, but solve different problems. The table below summarizes the results and adds pyzotero as a fourth pillar.
| Criterion | zotero-mcp-server | Zoteus | cli-anything-zotero | pyzotero |
|---|---|---|---|---|
| Type | Real MCP server | Real MCP server (30 tools) | CLI/SDK via SSH | Python library (no MCP server) |
| Windows installation | Python + zotero-mcp-server | Node.js + npx -y @oscardvs/zoteus | Python + pip install cli-anything-zotero + Zotero plugin | Python + pip install pyzotero |
| Zotero plugin required | No | No | Yes (JS Bridge) | No |
| Read backend | Local Zotero HTTP API | Local Zotero HTTP API | SQLite + local API + JS Bridge | Local Zotero HTTP API (local=True) or Web API |
| Write backend | Local API | Zotero Web API v3 | Local JS Bridge (no API key, no internet) | Web API only (local=True is read-only) |
| Stability | Occasional timeouts, AssertionError | Stable so far | Stable so far | Very stable |
| Search / metadata | Works | Very fast, very detailed | Works well | Very fast, ideal for batch operations |
| PDF full text | Returns excerpts; large PDFs truncated; block-wise reading possible | Since v1.2.0: Fallback downloads PDF and extracts text on-the-fly, even if Zotero hasn't indexed it. Delivers exact page locators. | Works via search-fulltext | Very fast as plain text, even large PDFs in one call |
| Read notes | Returned empty content in tests | Works | Works | Works |
| Write notes | Works | Theoretically via create_items | Works (note add) | Possible via Web API |
| Tags / metadata write | Works | Works (update_item) | Works (item tag, item update) | Possible via Web API |
| DOI import | Works directly (via zotero_add_item via DOI) | Since v1.2.0: zotero_import resolves DOIs via OpenAlex/Crossref – no translation server needed. ISBN/PMID still require translation server. | Works incl. PDF fetch | No |
| CSL citations | Limited | ~2,800 styles via citeproc | Works (item citation) | Limited |
| DOCX citations | No | No | Yes (static/dynamic) | No |
| Direct Zotero-JS access | No | No | Yes (zotero-cli js ...) | No |
| MCP integration in Kimi Code | Yes | Yes | No, only via SSH/CLI | No, only Python calls |
| Semantic search | Yes (ChromaDB index, ~13 s per query on ~120k documents); incremental updates via update-db --fulltext | Yes (own hybrid index, BM25 + vector); ~100 s per query measured on the 255k-passage full index (v1.9.0) — since fixed by the maintainer: two-stage search with binary codes, ~42× faster, live-verified in v1.12.0: 0.33–0.84 s per query (see the stress test and issue #30); plus OpenAlex-based external search | Yes, but external embedding endpoint needed | No |
As of August 2026. Ratings refer to the versions tested.
Technically these four tools are independent of each other. In practice, however, the multi-tool workflow only works reliably with cross-session memory: otherwise, every session would have to decide anew which tool is right for which task and which workarounds currently apply. I use a Memory-MCP server that stores the tool comparison, benchmarks, and rules across sessions. More on this on the Memory-MCP server page.
zotero-mcp-server
The zotero-mcp-server was our entry point into the MCP route. It works for most standard tasks, but proved somewhat wobbly in practice: individual calls hang, the server can crash under parallel requests, and large PDFs are truncated to about 50 pages by zotero_get_item_fulltext. Its remaining advantage is semantic search via the locally built index – provided that index has been successfully built on the Windows host. Block-wise reading of large PDFs via zotero_read_pdf_pages becomes less compelling once the Zotero storage is also available as an SMB share on the LAN.
HTTP transport (since Aug 17, 2026): The server temporarily ran as a permanent HTTP service on port 8000 (instead of SSH stdio). This eliminated SSH overhead: initialization was 40–77× faster (0.04 s vs. 3 s), and the connection was more stable – no SSH session drops, no CMD window. The data queries themselves were just as fast as over SSH, because the bottleneck is the Zotero Local API on the Windows host, not the transport.
No longer in use. The zotero-mcp-server was repeatedly unstable – crashes under parallel requests, hanging calls, unexplained timeouts. A source code analysis by DeepSeek V4 Flash identified fundamental architecture problems: a global RLock serializing all access, dependency on pyzotero's hardcoded 30s timeout, and missing session management. Zoteus is the full, stable replacement and has been used exclusively since August 2026.
Zoteus
Zoteus is an alternative MCP server with 30 tools (as of v1.15.0, September 2026). There is now also a written setup guide (zoteus.com/docs/connect-claude-to-zotero) covering Claude Desktop, claude.ai, Claude Code, and Cursor, VS Code, Zed, and Codex. In tests it was noticeably more stable and faster than the original server. Search, metadata retrieval, CSL citations, and simple write operations such as tags or trash work particularly well. A notable plus is integration with OpenAlex for semantic search – comparable to the same feature in Beaver.
Since v1.2.0 (Aug 18, 2026) Zoteus has received three major improvements that address exactly the weaknesses from our GitHub issues:
- DOI import without Docker:
zotero_importresolves DOIs directly via OpenAlex/Crossref, arXiv IDs via the arXiv API. No translation server needed anymore. - PDF fulltext fallback:
zotero_get_fulltextnow automatically downloads the PDF and extracts text on-the-fly when Zotero hasn't indexed it. Delivers exact page locators. - Auto-build for semantic search:
zotero_semantic_searchautomatically starts the index build on first use. No manual steps required.
ISBN/PMID/bibcode and URL scraping still require a translation server. Otherwise, Zoteus is now a full replacement for the old zotero-mcp-server for most use cases.
OpenAlex and semantic search
Through OpenAlex, Zoteus taps into an open, continuously updated scholarly graph. This enables semantic searches that are not limited to your own Zotero library: the agent can phrase a concept and get back related works even if they are not yet in the local collection. In our workflow this is handled via zotero_scholar; found DOIs are imported into Zotero on demand. Three routes are available: (1) Zoteus with zotero_import (since v1.2.0 via OpenAlex/Crossref, no translation server needed); (2) zotero_add_item from zotero-mcp-server, which resolves DOIs directly via CrossRef/DOI.org; (3) cli-anything-zotero with add doi ... --fetch-pdf, which fetches the PDF at the same time.
To separate imported items from existing ones, we tag them with imported by Kimi. That sounds trivial, but it makes follow-up much easier: every newly added title can be shown at a glance through this tag, its attachments can be checked selectively, and missing PDFs can be fetched in a targeted way. When you import a lot, this small convention quickly becomes the difference between “somewhere in the library” and “deliberately brought into the workflow”.
Fetching full texts
OpenAlex often provides a direct link to an open-access PDF. When it does, the agent fetches the PDF right away. If OpenAlex does not offer a direct full text, we fall back to cli-anything-zotero: the command add doi 10.xxxx/xxxxx --fetch-pdf imports the entry via DOI and simultaneously tries to download the PDF. Alternatively, the entry can be imported via zotero_add_item from zotero-mcp-server with the DOI; this is faster and requires no translation server, but unlike cli-anything-zotero it does not automatically fetch the PDF. In our tests this works reliably as long as a freely accessible source exists. If the attachment remains empty because the work is behind a paywall, two manual routes remain: the Zotero Connector in the browser or targeted retrieval through one's institution. Once the item lands in Zotero, it is tagged with imported by Kimi; that tag tells us later which full texts still need to be checked or added.
University VPN. If the Windows machine running cli-anything-zotero is connected via a university VPN, many paywalled journal articles can also be fetched automatically — provided the institution holds the appropriate licenses. The VPN tunnel must be active on the Zotero host, not on the Linux client. Some publishers require Shibboleth/SSO or cookies; in those cases the automatic download will not work and the Zotero Connector in the browser with VPN is the more reliable route.
cli-anything-zotero
cli-anything-zotero is not an MCP server, but a Python CLI toolkit that talks to Zotero directly via a JavaScript bridge. It therefore has to be called as a shell command over SSH. In return it is very powerful: DOI import including PDF download, full full-text search, reading and writing notes, tags, citations, and direct Zotero-JS access for operations that are not exposed by the API. The setup is more involved because a plugin has to be installed in Zotero, but once running it covers edge cases that MCP servers cannot reach.
Recommended combination
- Zoteus as the default MCP server for fast, stable read access (search, metadata, citations), DOI import, PDF fulltext (even non-indexed), and simple writes. Since v1.2.0 a full replacement for the old zotero-mcp-server.
- cli-anything-zotero as a specialty tool for import with PDF fetch, full-text search, notes, and complex tasks requiring direct Zotero access.
The zotero-mcp-server is no longer used. Due to repeated stability issues (crashes, timeouts, hanging calls) it has been replaced by Zoteus. A source code architecture analysis confirmed fundamental design flaws (global RLock, missing session isolation, pyzotero dependency). The HTTP transport (port 8000) helped against SSH drops but did not solve the underlying server problems.
Which tool for which task?
| Task | Recommended tool | Reason |
|---|---|---|
| Fast read access (metadata, search) | Zoteus | < 0.01 s, very stable, 30 tools |
| Batch operations (many items) | pyzotero local=True | 0.42 s for 5 parallel reads, 2.6× faster than Web API |
| PDF full-text reading | pyzotero local=True | 0.01 s for ~40,000 chars |
| Import (DOI + PDF fetch) | cli-anything-zotero | Only tool with --fetch-pdf |
| Notes / annotations | cli-anything-zotero | item notes, note add, item annotations |
| CSL citations (APA etc.) | Zoteus | < 0.01 s, ~2,800 styles via citeproc |
| Semantic search (own library) | Zoteus | Own index via zotero_semantic_search (auto-build since v1.2.0) |
| Semantic search (external sources) | Zoteus | OpenAlex-based via zotero_scholar |
| DOCX citations | cli-anything-zotero | Only tool with DOCX support |
| Parallel access (multi-agent) | pyzotero | MCP servers only safe sequentially |
As of 16 Aug 2026. All four tools were tested with benchmark scripts; times are median single measurements via SSH tunnel to the local Zotero API.
Addendum: pyzotero for batch processing
For mass read or write operations, the MCP layer can be slow. Kimi Code therefore set up the Python library pyzotero itself and uses it independently when that seems more sensible than the MCP route – for example for mass export of abstracts, uploading finished notes as Zotero child notes, or mass changes to tags and metadata.
| Operation | pyzotero local=True | MCP server |
|---|---|---|
| Read 1 item (metadata) | ~0.01 s | ~0.5–2 s (incl. SSH startup) |
| Read top 20 items | ~0.2 s | ~2–4 s |
| PDF full text as text (approx. 40,000 characters) | ~0.06 s | ~2.1 s |
| PDF full text as binary file (original PDF) | not from Linux VM (only redirect to local Windows file) | possible via attachment key |
Measured in August 2026 over an SSH tunnel to the local Zotero API on a Windows host. The MCP server has to be restarted for each call; the given times include this startup overhead. The pyzotero values refer to an already running Python session.
Important: pyzotero can basically be operated in two ways. Via the web API at zotero.org it talks to the synchronized library data – without full texts stored there, there are no PDF contents here. Alternatively, the parameter local=True can address the local Zotero HTTP API on the same machine; but this is currently read-only (as of August 2026, pyzotero 1.13.5). A corresponding open issue for write access exists on GitHub (#344). Kimi Code therefore continues to leave write access and bibliography export to the MCP server, because only it provides write operations in local mode.
PDF full texts: text yes, binary no. Via pyzotero local=True and the /fulltext endpoint, PDF full texts can be read extremely fast as plain text – for a PDF of about 40,000 characters it was about 0.06 s, for a 442-page textbook with over 1.6 million characters easily in one call. Direct download of the original PDF file via pyzotero.file() or the API endpoint /file fails from the Linux VM: Zotero responds with a redirect to file://localhost/C:/Users/..., i.e. a local Windows path. The Linux VM cannot resolve this path. If you need the original PDF, use SMB access to the Zotero storage instead.
When to use which tool? The following rule of thumb has emerged:
- Search, metadata, citations: Zoteus (fast, stable, MCP-integrated).
- Import, notes, full-text search, complex operations: cli-anything-zotero.
- Mass reads of metadata or PDF text:
pyzoterolocal=True. - Semantic search over an existing index: zotero-mcp-server.
- Original PDF files or graphics: SMB share on
/mnt/zotero.
Benchmark results
The following timings were measured in August 2026 on a local network: the AI client runs in a Linux VM, Zotero on a Windows host. All four tools were re-tested on 16 August 2026 with the same benchmark scripts – results are stably reproducible against the 11 August measurements (see reproduction note).
HTTP transport: zotero-mcp-server without SSH overhead (Aug 17, 2026)
Since August 17, 2026, the zotero-mcp-server runs as a permanent HTTP service on port 8000 (instead of SSH stdio). The comparison shows: Meta operations are 40–77× faster because SSH overhead is eliminated. The data queries themselves remain equally fast – the bottleneck is the Zotero Local API on the Windows host, not the transport path.
| Operation | HTTP (Aug 17) | SSH (Aug 16) | Conclusion |
|---|---|---|---|
| Initialization (session + tools) | 0.04 s | 3.0 s | 77× faster |
| List tools | 0.01 s | 0.5 s | 39× faster |
| List libraries | 0.02 s | 1.0 s | 41× faster |
| Read item metadata | 2.0 s | 2.0 s | same |
| List collections | 2.1 s | 2.1 s | same |
| Read full text | 4.1 s | 2.2 s | same/slower |
| Semantic search | 12.0 s | 2.0 s | slower |
Measured on Aug 17, 2026 via HTTP (port 8000) and Aug 16, 2026 via SSH tunnel, respectively. Two runs via HTTP showed identical times – no cache warmup effect measurable. Data queries are transport-independent because the Zotero Local API is the bottleneck.
Practical significance: For interactive work, HTTP transport primarily provided a noticeable advantage when establishing the connection – the session was available immediately, without SSH timeouts or drops. Once the session was established, the Zotero Local API set the pace. For fast read access, Zoteus remains the first choice (< 0.01 s). The zotero-mcp-server is no longer in use – see the tool comparison section for details.
Sequential benchmarks: all four tools compared
The same operations were tested across all four access paths. The table shows measured individual times in seconds. For each tool, the table documents exactly which operations were tested.
| Operation tested | zotero-mcp-server | Zoteus | cli-anything-zotero | pyzotero local=True |
|---|---|---|---|---|
| Init / connection (SSH + server start) | 4.47 s | 3.99 s | 3.35 s 1 | — 2 |
| Item metadata read (1 item) | 2.03 s | < 0.01 s | 1.14 s | 0.01 s |
| Children/attachments read | 4.08 s | — 3 | 1.06 s | 0.03 s |
| Full text/PDF read (~40,000 chars) | 2.16 s | < 0.01 s | 5.64 s 4 | 0.01 s |
| Search (5 results) | 7.63 s | < 0.01 s | — 5 | 0.19 s 6 |
| Collections list | 2.07 s | < 0.01 s | 1.11 s | — |
| Tags list | — | < 0.01 s | — | — |
| Citation (CSL, APA) | — | < 0.01 s | 1.25 s | — |
| Notes read | — | — | 1.06 s | — |
| Annotations read | — | — | 5.09 s | — |
1 cli-anything-zotero: app ping as proxy for init+connection. 2 pyzotero: no init cost (Python library, no server start). 3 Zoteus: zotero_get_item includes children. 4 cli-anything-zotero: search-fulltext searches all PDFs, not a single one. 5 cli-anything-zotero: no direct search API, only search-fulltext. 6 pyzotero: items(limit=20), not identical to MCP search.
Measured on 16 Aug 2026 via SSH tunnel to the local Zotero API on a Windows host. MCP servers (zotero-mcp-server, Zoteus) are started via SSH+PowerShell for each call; init time includes this overhead. pyzotero runs in an already-open Python session. "—" = operation was not tested for this tool in the benchmark.
Parallel access: pyzotero (5 concurrent threads)
While MCP servers must work sequentially (the old zotero-mcp-server crashes on parallel requests), pyzotero can handle concurrent access. Tested with 5 simultaneous threads:
| Test (5 threads) | Total time | Per thread | Success |
|---|---|---|---|
| Web API: 5× read (top limit=10) | 1.09 s | 0.82–0.97 s | 5/5 ✅ |
| Web API: 5× write (tag update, same item) | 1.79 s | 1.03–1.75 s | 5/5 ✅ |
| local=True: 5× read (top limit=10) | 0.42 s | 0.33–0.35 s | 5/5 ✅ |
pyzotero local=True is 2.6× faster than the Web API for parallel reads (0.42 s vs. 1.09 s). Threads run nearly identically (0.33–0.35 s), showing the local API handles concurrent access without contention. Write access via the Web API also works stably – pyzotero re-fetches the item in each thread (with current version) before updating, avoiding version conflicts.
Reproduction note
The sequential benchmark script (mcp_zotero_benchmark.py) was run on 11 Aug 2026 and 16 Aug 2026 with identical configuration. Results are stably reproducible:
| Operation | 11 Aug 2026 | 16 Aug 2026 | Deviation |
|---|---|---|---|
| MCP server: item metadata | 2.04 s | 2.03 s | ±0.01 s |
| MCP server: full text | 2.09 s | 2.16 s | +0.07 s |
| MCP server: search | 7.30 s | 7.63 s | +0.33 s |
| pyzotero local: item | 0.01 s | 0.01 s | ±0 |
| pyzotero local: top-20 | 0.20 s | 0.19 s | −0.01 s |
Deviations are within network latency and SSH handshake variation. MCP server init time varies more (2.98 s → 4.47 s) because PowerShell startup and SSH connection setup are non-deterministic. After init, all operations are stable.
Stress test: semantic search on a full index — Zoteus vs. zotero-mcp-server (Aug 28, 2026)
On August 28, 2026, Zoteus ran with a complete index for the first time: 10,184 items, 255,703 passages (222,514 of them from PDF full texts), embedding model text-embedding-3-large (3072 dimensions), SQLite backend (Node 24 with FTS5). A direct comparison against the ChromaDB index of the zotero-mcp-server (~120,000 documents, same embedding provider) across five identical queries produced an unexpected picture: for semantic search, the operationally much faster Zoteus is roughly 8× slower.
| Query | Zoteus v1.9.0 (SQLite, 255k passages) | zotero-mcp-server v0.11.0 (ChromaDB, ~120k documents) |
|---|---|---|
| "Zahnersatz Zeitplanung Honorar HKP" | 93.3 s | 13.5 s |
| "mindfulness in dental practice" | 102.0 s | 13.5 s |
| "cliodynamics mathematical modeling of history" | 94.7 s | 12.8 s |
| "KI-Modelle Benchmark Vergleich" | 94.4 s | 13.0 s |
| "Praxisverwaltungssystem Datensicherheit Cloud" | 104.5 s | 13.0 s |
A RAM measurement (sampling every 3 seconds across the entire runtime of a query) narrows down the cause: memory stays completely flat during the whole search (~110 MB working set, identical to the baseline). Zoteus does not load the vectors into RAM; it iterates all 255,000 vector rows one by one from SQLite and computes similarity in JavaScript — constant memory, CPU-bound, linear in corpus size. ChromaDB, by contrast, maintains a persistent ANN index (HNSW) and pays logarithmic rather than linear cost per query.
Update (29 August 2026): The Zoteus maintainer has since fixed this on main (shipping in v1.10.0): two-stage search with one-bit binary codes (Hamming scan) and exact reranking — measured 2.3 s instead of ~95 s at exactly this geometry, a factor of 42.
Update (31 August 2026, live-verified): Zoteus v1.12.0 is now installed on our Windows host: after a one-time ANN code build at the first query (266 s), all follow-up queries run at 0.33–0.84 s — better than the projected 42×. No re-embedding required.
Result quality is usable on both sides, but weighted differently: both found the central hits (e.g. the denture-scheduling note and the fixed-subsidy guideline), yet the top-5 overlap was only about 1/5 — Zoteus fuses via Reciprocal Rank Fusion (BM25 + vector) and surfaces deep full-text passages, while the zotero-mcp-server consistently returns five results with page locators.
Consequence for tool choice: it depends on the use case. For all read, write and administrative operations Zoteus remains 100–700× faster (< 0.01 s instead of 2–7 s) — for semantic search, the zotero-mcp-server with ChromaDB was the practical choice until Zoteus v1.12.0 was verified. Update (31 August 2026): Zoteus v1.12.0 is installed and live-verified (0.33–0.84 s per query) — semantic search now runs entirely on Zoteus; the zotero-mcp-server has been deactivated (ChromaDB files kept for rollback). Both findings were filed as feature requests with Zoteus — and have since been implemented by the maintainer on main (29 August 2026), shipping in v1.10.0: Robust fulltext extraction (#29) (local fallback extraction incl. PDF outline and EPUB) and ANN index for semantic search (#30) (two-stage search with one-bit binary codes: 2.3 s instead of ~95 s at our geometry).
Prompt strategy: standing instructions instead of repetition
For recurring Zotero tasks, a fixed standard instruction is worthwhile so it does not have to be reformulated for every query. Typical building blocks:
- Language and target audience (e.g. specialist audience with terminology that needs explanation)
- Evidence hierarchy and GRADE logic for scientific or medical literature
- active objection to factual errors instead of silent adoption
- precision rules for reporting numbers and data
- a fixed note workflow (research → full-text check → writing)
- formatting rules for Zotero notes (Markdown when creating, simplified HTML when updating)
Such an instruction can be stored in a memory server and automatically loaded at the beginning of every Zotero task – see the Memory-MCP server page.
Cost and privacy
- Zotero-MCP server: free and open source.
- AI models: billing via the respective API provider, depending on context length and number of calls.
- No upload to third parties: If the server runs locally on the Zotero host, full texts never leave your own network.
- Semantic search: The server can use local embeddings or external providers. The index is typically built once and then only read, not automatically recalculated.
Tips and pitfalls
Parallel access and HTTP transport
Since the HTTP transport (Aug 17, 2026), the zotero-mcp-server temporarily ran as a permanent service on port 8000 and was multi-client capable. As of late August 2026, the server is no longer used – see the tool comparison section for the replacement.
However, as a rule of thumb: for most tasks, sequential processing is faster because the Zotero Local API is the bottleneck, not the transport. Parallel calls are most beneficial with pyzotero over the Web API, where they were tested (5 parallel read accesses in 0.88 s).
Reading large PDFs page by page
If the Zotero storage is available as an SMB share on the LAN, Kimi Code prefers to read large PDFs directly from the filesystem. That is faster and delivers the complete document. The route via zotero_read_pdf_pages in the old zotero-mcp-server remains only a fallback when no SMB access is set up or when very specific single pages are needed. With pyzotero local=True the complete full text is usually obtained in one call (fulltext_item) – this worked without problems for a 442-page textbook with over 1.6 million characters.
Take build warnings seriously
Anyone building a document from researched sources (e.g. with Pandoc/Citeproc) should not ignore citation warnings: a message like [WARNING] Citeproc: citation ... not found almost always means a passage without a linked source.
Check availability at session start
It is worth automatically checking at every startup whether Zotero-MCP – and any Memory-MCP – are reachable before the actual work begins. This way an outage is not noticed only when a tool call fails in the middle of research.
Connection types: HTTP and SSH
The zotero-mcp-server temporarily ran as a permanent HTTP service on port 8000 from August 17, 2026, until it was replaced by Zoteus later that month. Zoteus and cli-anything-zotero are still started as subprocesses on the Windows host via SSH. Communication runs over stdio through the SSH connection. The SSH user ml can execute any command on the Windows machine – everything a normal Windows user is allowed to do.
The only restriction is the user rights of ml: this account intentionally has no administrator rights. System files, registry changes, and protected areas of the Windows file system are therefore inaccessible. Within normal user rights, however, the AI has full access – it can read and write files, start programs, and of course use the Zotero interface.
For pure Zotero operation this is sufficient: the MCP servers talk to the local Zotero API (port 23119, bound to localhost only), and for access to the original PDFs the SMB share of the Zotero storage is available. No additional port forwarding or separate access paths are needed for this.
Evaluating graphics and figures from PDFs
The four tested tools (zotero-mcp-server, Zoteus, cli-anything-zotero, pyzotero) cover most PDF tasks: reading full text, metadata, annotations, search. However, for graphics, maps, tables, or historical illustrations, text alone is not always sufficient. Direct file access via SMB adds capabilities the tools cannot provide:
| Capability | 4 tools | SMB share |
|---|---|---|
| PDF full-text reading | ✅ | ✅ |
| Metadata, annotations | ✅ | — |
| Extract embedded images | — | ✅ pdfimages |
| Render pages as PNG (image analysis) | — | ✅ pdftoppm |
| Read non-indexed PDFs | — 1 | ✅ |
| Original PDF as binary file | — 2 | ✅ |
1 Zoteus only returns full texts that Zotero has already indexed. 2 pyzotero file() fails from the Linux VM (redirect to local Windows path).
For direct file access, the Zotero folder is mounted as an SMB share:
/mnt/zotero-storage– the storage folders with PDFs and attachments, sorted by Zotero item key (e.g./mnt/zotero-storage/222BTP99/article.pdf).
⚠️ Important rule: Never write to Zotero databases!
Files like zotero.sqlite, beaver.sqlite, or other .sqlite databases in the Zotero folder must never be modified by AI agents. The SQLite database is fragile – a faulty write can corrupt it, and then all 10,000+ items become unfindable. This rule applies regardless of technical write permissions (SMB share, SSH access). Even if access is possible: databases read-only, never write.
The prerequisite is a read-only share of the Zotero folder (e.g. \SERVER-IP\Zotero) and a credentials file on the Linux client, e.g. ~/.smbcredentials. The password must not appear in plain text in commands or documentation, only in this local file:
username=YOUR_USERNAME
password=YOUR_PASSWORDTwo things can be done with this:
- Render pages as images: With
pdftoppm -png file.pdf prefixevery PDF page becomes a PNG file that the language model can analyze directly. - Extract embedded images: With
pdfimages -j file.pdf prefixthe raster images originally embedded in the PDF (JPEG, PNG, PPM) are extracted. This is more precise than page rendering when only individual figures are of interest.
Speed over SMB: Measured on a 3 MB PDF with several embedded images (August 2026, SMB mount on /mnt/zotero):
| Operation | Duration | Note |
|---|---|---|
| Read PDF file (3 MB) | ~0.005 s | Sequential read, SMB efficient |
Extract embedded images (pdfimages -j) | ~0.16 s | Very fast, as only image objects are read |
Render all pages as PNG (pdftoppm -png) | ~11.6 s | Slow over SMB, presumably due to many small reads; prior local copy hardly helps |
Times refer to single test runs; they vary with network load and PDF complexity.
This was tested with a text PDF (title page rendered) and an image-rich PDF in which historical depictions and maps were extracted and described. The route is especially useful when supplementary materials such as .docx files or original PDFs have to be processed from the filesystem rather than via the Zotero API.
Local API vs. zotero.org
The most important configuration pitfall concerns the question of where the MCP server gets its data. The server can run in local mode directly against the Zotero instance on the Windows host – or in web mode via the Zotero sync servers at zotero.org. For full texts this makes a dramatic difference.
- Prerequisite for local mode: Before starting the server, the environment variable
$env:ZOTERO_LOCAL = "true"must be set. Without this variable the server accesseszotero.org. - Full texts must be available locally. If – like me – you explicitly exclude PDF full texts from synchronization with
zotero.orgin the Zotero sync settings, the web API has no PDF contents. A call likezotero_get_item_fulltextwould then come up empty or deliver only abstracts. By default Zotero does synchronize full texts; the decision not to synchronize them must be made consciously during setup. pyzoterois not limited to zotero.org. With the parameterlocal=True,pyzoterocan also use the local Zotero HTTP API on the same machine. This is practical for read-only mass operations on metadata, but currently provides no write access. For write operations and annotations the route via the MCP server with local API enabled must be taken. PDF full texts as plain text can be retrieved very quickly viapyzotero local=True; downloading the original PDF file as binary fails from a remote Linux VM because Zotero returns a redirect to a local Windows path (file://localhost/...).- Zotero binds the local API to localhost. Port
23119is deliberately bound only to127.0.0.1. Direct access via the LAN IP does not work even with an open firewall. Anyone wanting to access from another machine on the LAN needs an SSH tunnel or a reverse proxy on the Zotero host.
As a rule of thumb: as soon as PDF content, annotations, or large books come into play, the server must run in local mode. Only for pure metadata operations is web mode acceptable.
Comparison: plugin vs. MCP server
| Aspect | Beaver / plugin | MCP server |
|---|---|---|
| Setup | Simple: install plugin, sign in | Complex: SSH, server, API keys |
| Model choice | Limited by the plugin | Any model supported by the MCP client |
| Context length | Chat-turn based | Project-long memory possible |
| Batch processing | Limited | Unlimited via pyzotero add-on |
| PDF access | Inside Zotero | Via local API over SSH |
| Best suited for | Quick questions, single papers | Long projects, custom pipelines |
For a single literature question, Beaver is faster to set up and faster to results. For a large project with hundreds of sources, the MCP pipeline is the more flexible choice.
When is the MCP route worth it?
The MCP approach pays off when one or more of these conditions apply:
- It is a long-form project (book, dissertation, systematic review).
- The same sources are needed in several formats – notes, text excerpts, bibliography.
- A custom build pipeline is desired (Pandoc, LaTeX, custom filters).
- PDFs should stay on a local machine while the AI runs on another host.
A concrete example of such a workflow – from thematic clustering to a finished book based on 198 sources – is described by the Cliodynamics book project.
Benchmark script to reproduce
The times given on this page were measured with a reproducible Python benchmark. The script compares the Zotero-MCP server over SSH, direct calls to the local Zotero HTTP API, and pyzotero with local=True. It is deliberately built to finish in under two minutes and to time out hanging MCP calls.
mcp_zotero_benchmark.py– the benchmark scriptrequirements-mcp-benchmark.txt– required Python packagesmcp_zotero_benchmark_report.md– example reportinstallationsanleitung-zotero-tools-windows.md– step-by-step guide for Zoteus and cli-anything-zotero on WindowsArbeitsbericht_Tool-Evaluation_2026-08-13.md– full evaluation reportvergleich-zotero-tools.md– short comparison of all toolsbenchmark_cli_anything_zotero.py– benchmark script for cli-anything-zoterobenchmark_cli_anything_zotero.json– example benchmark raw data
The downloadable files contain no personal credentials. SSH host, username, Zotero item key, and library ID are given as placeholders; your own mcp.json is read at runtime.
Tools and links
zotero-mcp-server: stevenyuyy.com/zotero-mcppyzotero: pyzotero.readthedocs.io- Zotero local API documentation: zotero.org
Zoteus: github.com/oscardvs/zoteuscli-anything-zotero: github.com/PiaoyangGuohai1/cli-anything-zotero- Zotero MCP Server: Architecture Analysis of an Unstable Codebase – DeepSeek V4 Flash investigates the root causes of instability
- 📄 Zotero-MCP Knowledge for AI Agents – Condensed hands-on experience, workflow rules, tool comparisons, and benchmark results as a Markdown file for import into any AI memory system
This workflow was built for a concrete project. Tool versions, server behavior, and API prices change quickly – test your own setup before production use.
