zotbridge
Provides tools for interacting with a Zotero reference library, including searching items, retrieving metadata and full-text PDFs, accessing annotations and highlights, exporting citations, and optionally adding or updating items and managing tags.
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., "@zotbridge search my library for 'machine learning' and export APA citations"
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.
zotbridge
An MCP (Model Context Protocol) server that connects a Zotero reference library to Claude Desktop, ChatGPT and any other MCP-compatible client.
Once connected, an assistant can search your library, read the full text of your PDFs, pull out the highlights you made in the Zotero reader, and hand back correctly formatted APA 7th references with live DOI links. With writing enabled it can also add items from a DOI, attach notes and manage tags.
zotbridge talks to Zotero two ways and picks whichever is available: the local HTTP API served by the Zotero desktop application, and the Zotero web API.
Contents
Related MCP server: zotero-manager-mcp
Why another Zotero MCP server
Larger Zotero MCP projects exist and do more. zotbridge takes the opposite position on purpose: a small, readable tool surface that an agent can hold in context at once, with citation output shaped for academic writing rather than for generic retrieval.
Three design choices follow from that.
A small surface. Twenty tools, each doing one thing. Agents choose badly among forty near-synonymous tools, and every tool description consumes context before any work begins.
Citations as a first-class output. zotero_export_citations returns APA
7th entries with hyperlinked DOIs, alphabetised, plus BibTeX and CSL-JSON.
Every metadata response already carries a ready-made reference and in-text
citation, so an assistant citing your library quotes the library rather than
reconstructing a reference from memory.
Writes that are hard to trigger by accident. Write tools refuse to run
unless ZOTBRIDGE_ALLOW_WRITES=true, they require a separately granted local
key, and every one of them accepts dry_run=true. Edits send the item's
current version back with the request, so a concurrent change in the desktop
application fails the write instead of overwriting it.
Requirements
Python 3.10 or later
Zotero 7 or later for local access, or a Zotero web API key
PyMuPDF for PDF text extraction (installed by the
pdfextra)
Installation
git clone https://github.com/Einstein628/zotbridge.git
cd zotbridge
python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
# macOS or Linux
source .venv/bin/activate
pip install -e ".[pdf]"Verify the installation:
zotbridge --version
zotbridge --check--check resolves the transport, contacts the library and prints what it
found. Run it whenever something stops working; it is the fastest way to tell
a configuration problem from a client problem.
Connecting Zotero
Local access, recommended
Open Zotero 7.
Go to Edit -> Settings -> Advanced.
Tick Allow other applications on this computer to communicate with Zotero.
Leave Zotero running.
zotbridge then reads the library at http://localhost:23119/api with no API
key, no rate limit and no dependence on your library being synced to
zotero.org.
Web access, as a fallback
Sign in at https://www.zotero.org/settings/keys and create a new key.
Grant it read access, and write access only if you intend to use the write tools.
Note the numeric userID shown on the same page.
Put both in your
.env:
ZOTERO_API_KEY=your_key_here
ZOTERO_LIBRARY_ID=1234567
ZOTERO_LIBRARY_TYPE=userWith ZOTBRIDGE_TRANSPORT=auto (the default), zotbridge tries the desktop
application first and falls back to the web API when it is closed.
Connecting a client
Claude Desktop
Edit the MCP configuration file:
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonmacOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"zotbridge": {
"command": "C:\\path\\to\\zotbridge\\.venv\\Scripts\\zotbridge.exe",
"env": {
"ZOTBRIDGE_TRANSPORT": "auto",
"ZOTBRIDGE_ALLOW_WRITES": "false"
}
}
}
}On macOS or Linux the command is /path/to/zotbridge/.venv/bin/zotbridge.
Restart Claude Desktop; zotbridge appears in the tools menu.
ChatGPT
ChatGPT connects to MCP servers over HTTP rather than stdio, so the server needs a reachable URL. Run it with the streamable HTTP transport:
python -c "from zotbridge.server import create_server; \
create_server().run(transport='streamable-http', host='127.0.0.1', port=8765)"Then expose http://127.0.0.1:8765/mcp through a tunnel and register that URL
as a connector in ChatGPT's settings. Because this opens your library to
anything that reaches the URL, keep ZOTBRIDGE_ALLOW_WRITES=false for remote
use and take the tunnel down when you have finished.
Other MCP clients
Any client that can launch a stdio MCP server will work. The command is
zotbridge (or python -m zotbridge); pass configuration through the
environment. docs/client-setup.md covers Claude Code, Cursor and the
streamable-HTTP setup in more detail, including its security implications.
Tool reference
Search and discovery
Tool | Purpose |
| Free-text search over metadata, or over full text with |
| Field-specific search with a publication-year range |
| Collections with keys and item counts |
| Tags in the library, optionally filtered |
| Most recently added items |
| Transport in use, write permission, reachability |
Retrieval
Tool | Purpose |
| Full metadata, plus a ready-made APA 7th reference |
| Notes and attachments belonging to an item |
| Contents of one collection |
| Text of the item's best attachment, with optional page selection |
Citations
Tool | Purpose |
| APA 7th, in-text, BibTeX or CSL-JSON for named items |
| The same for an entire collection |
Annotations
Tool | Purpose |
| Highlights and comments on an item, grouped by page, filterable by colour |
| Search annotation text across the library |
Writing
Tool | Purpose |
| Request a local write key from the desktop application |
| Look a DOI up on Crossref and save the item |
| Create an item from metadata you supply |
| Attach a child note to an item |
| Edit fields, with version checking |
| Add or remove tags |
docs/tools.md documents every parameter.
Worked examples
Ask the assistant:
Search my library for cassava haploid induction work published since 2015, then give me the APA 7th references for the three most relevant.
Read the PDF of item
ABCD1234and list the primer design decisions the authors justify explicitly.
Pull my yellow highlights from
EFGH5678and group them by theme.
Export the whole DMP gene family collection as BibTeX for my LaTeX manuscript.
Configuration
Every setting is an environment variable; .env.example lists them all.
Variable | Default | Meaning |
|
|
|
|
| Desktop API base |
| — | Web API key |
| — | Numeric user or group ID |
|
|
|
| — | Local write key from |
|
| Master switch for every write tool |
|
| Network timeout in seconds |
|
| Cap on returned PDF text |
|
| Log verbosity, written to stderr |
Writing to the library
Writing is off by default, and turning it on takes three deliberate steps.
Set
ZOTBRIDGE_ALLOW_WRITES=true.Call
zotero_authorize_localand approve the dialogue Zotero raises. Copy the key intoZOTBRIDGE_LOCAL_API_KEY. (Local write support arrived in Zotero 10. On earlier desktop versions, configure the web API with a write-enabled key and setZOTBRIDGE_TRANSPORT=webinstead.)Restart the client so it picks up the new environment.
Then exercise anything new with dry_run=true first. A dry run prints the
exact payload and changes nothing.
Back up zotero.sqlite before letting an agent write to a library you care
about.
Development
pip install -e ".[dev]"
pytest
ruff check src testsThe test suite mocks the HTTP layer with respx, so it needs neither a
running Zotero nor network access.
src/zotbridge/
├── config.py environment-driven settings
├── client.py transport resolution, requests, error typing
├── formatting.py Markdown, APA 7th, BibTeX, CSL-JSON
├── pdf.py PDF text extraction
├── server.py MCPServer construction
└── tools/ one module per tool familyTroubleshooting
"Neither transport is available." Zotero is closed or the local API is
switched off, and no web credentials are set. Open Zotero, tick the setting
under Settings -> Advanced, and rerun zotbridge --check.
Zotero answers 403. The "Allow other applications" setting is off, or a write was attempted without a local key.
The client shows no tools. The command path in the client configuration is
wrong. Use the absolute path to the zotbridge executable inside your virtual
environment, and remember that JSON requires doubled backslashes on Windows.
PDF text comes back empty. The attachment is a scan with no text layer. zotbridge does not perform OCR; run the PDF through OCR in Zotero or another tool first.
Highlights are missing. Only annotations made in the Zotero PDF reader are stored as Zotero items. Highlights made in an external viewer live inside the PDF file and are not exposed by the API.
docs/troubleshooting.md goes further.
Licence
MIT. See LICENSE.
zotbridge is an independent project and is not affiliated with or endorsed by the Corporation for Digital Scholarship, which develops Zotero.
Available Tools
20 toolszotero_add_by_doiAdd an item to Zotero from a DOIA
Look a DOI up on Crossref and save the resulting item to Zotero.
Args: doi: The DOI, with or without the https://doi.org/ prefix. collection_key: Optional collection to file the new item in. tags: Optional comma-separated tags to apply. dry_run: Fetch and show the metadata without writing anything.
Returns: A confirmation carrying the new item key and an APA 7th rendering of what was saved.
Inputs: doi (required), tags, dry_run, collection_key. Returns: result
zotero_add_itemCreate a Zotero item from supplied metadataA
Create an item from a metadata object you supply directly.
Use this for sources without a DOI, such as a thesis, a technical report or a dataset.
Args: item_json: A JSON object with at least "itemType" and "title". Creators take the Zotero shape: [{"creatorType": "author", "firstName": "J.", "lastName": "Odipio"}] collection_key: Optional collection to file the item in. dry_run: Show the payload without writing.
Returns: The new item key and its APA 7th rendering.
Inputs: item_json (required), dry_run, collection_key. Returns: result
zotero_advanced_searchSearch specific Zotero fieldsARead-only
Search by individual metadata fields and filter by publication year.
Field filters other than item_type and tag are applied client-side after a broad server-side query, which keeps the tool usable on both the local and the web transport.
Args: title: Substring to match in the title. creator: Substring to match in any creator's name. publication: Substring to match in the journal or book title. year_from: Earliest publication year to include. year_to: Latest publication year to include. item_type: Restrict to one Zotero item type. tag: Restrict to items carrying this tag. limit: Maximum number of items to return (1-100).
Returns: A Markdown list of matching items with their item keys.
Inputs: tag, limit, title, creator, +4 more. Returns: result
zotero_authorize_localRequest a local write key from ZoteroA
Ask the Zotero desktop application for a local API key.
Zotero raises a confirmation dialogue on the desktop. Approve it, then
copy the returned key into ZOTBRIDGE_LOCAL_API_KEY and restart the
MCP client. Read access does not need this; writes over the local
transport do.
Returns: The granted key and the next step, or an explanation of why the request failed.
Inputs: no named parameters. Returns: result
zotero_create_noteAttach a note to a Zotero itemA
Attach a child note to an existing item.
Args: item_key: Key of the parent item. content: Note text. Plain text is converted to simple HTML paragraphs; if the text already contains HTML tags it is sent unchanged. tags: Optional comma-separated tags for the note. dry_run: Show the payload without writing.
Returns: The new note's key, or the payload if dry_run is set.
Inputs: content (required), item_key (required), tags, dry_run. Returns: result
zotero_export_citationsExport citations for selected itemsARead-only
Render one or more items as formatted citations.
Args: item_keys: One item key, or several separated by commas. style: "apa" for an alphabetised APA 7th reference list with hyperlinked DOIs, "apa-intext" for parenthetical citations, "bibtex" for a .bib fragment, or "csl-json" for CSL-JSON.
Returns: The formatted citations, ready to paste into a manuscript or a reference manager.
Inputs: item_keys (required), style. Returns: result
zotero_export_collection_bibliographyExport a whole collection as a bibliographyARead-only
Render every item in a collection as a bibliography.
Args: collection_key: Eight-character collection key. style: "apa", "apa-intext", "bibtex" or "csl-json". limit: Maximum number of items to include (1-500).
Returns: The formatted bibliography.
Inputs: collection_key (required), limit, style. Returns: result
zotero_get_annotationsGet highlights and comments on an itemARead-only
Retrieve the annotations stored on an item's PDF attachments.
Highlights, underlines, sticky notes and image annotations are all returned, grouped by page, with any comment you attached to them.
Args: item_key: Key of the parent item or of an attachment. annotation_type: Restrict to "highlight", "underline", "note" or "image". colour: Restrict to one highlight colour, given as a hex value such as "#ffd400". Useful when colour encodes a coding scheme.
Returns: A Markdown report of the annotations, ordered by page.
Inputs: item_key (required), colour, annotation_type. Returns: result
zotero_get_childrenList an item's notes and attachmentsARead-only
List the notes and attachments belonging to an item.
Args: item_key: Eight-character Zotero item key of the parent item.
Returns: A Markdown list of child items with their own keys, which zotero_get_annotations and zotero_get_fulltext accept.
Inputs: item_key (required). Returns: result
zotero_get_collection_itemsList the items in a collectionARead-only
List the items held in one collection.
Args: collection_key: Eight-character collection key from zotero_list_collections. limit: Maximum number of items to return (1-200).
Returns: A Markdown list of items with their keys.
Inputs: collection_key (required), limit. Returns: result
zotero_get_fulltextExtract the full text of an item's PDFARead-only
Extract readable text from the best attachment of an item.
The attachment is downloaded through the Zotero API and parsed locally, so the text is the document's own, not a summary.
Args: item_key: Key of the parent item, or of the attachment itself. pages: Optional 1-based page selection such as "1-8" or "2,5,9-11". Omit to read the whole document. max_chars: Override the configured character cap for this call.
Returns: The extracted text, preceded by a short provenance header.
Inputs: item_key (required), pages, max_chars. Returns: result
zotero_get_itemGet one Zotero itemARead-only
Fetch the full metadata of a single item by its Zotero key.
Args: item_key: Eight-character Zotero item key, as returned by the search tools. include_abstract: Include the abstract in the output.
Returns: Markdown metadata for the item, followed by a ready-made APA 7th reference and in-text citation.
Inputs: item_key (required), include_abstract. Returns: result
zotero_get_recentList recently added Zotero itemsARead-only
List the most recently added items in the library.
Args: limit: Number of items to return (1-50).
Returns: A Markdown list of items, newest first.
Inputs: limit. Returns: result
zotero_list_collectionsList Zotero collectionsARead-only
List the collections in the library with their keys and item counts.
Args: limit: Maximum number of collections to return.
Returns: A Markdown list of collections, showing nesting where a collection has a parent.
Inputs: limit. Returns: result
zotero_list_tagsList Zotero tagsARead-only
List tags used in the library, optionally filtered by substring.
Args: contains: Only return tags containing this substring. limit: Maximum number of tags to return.
Returns: A comma-separated list of tag names.
Inputs: limit, contains. Returns: result
zotero_manage_tagsAdd or remove tags on a Zotero itemA
Add or remove tags on an item without touching its other fields.
Args: item_key: Key of the item. add: Comma-separated tags to add. remove: Comma-separated tags to remove. dry_run: Show the resulting tag set without writing.
Returns: The item's tag list after the change.
Inputs: item_key (required), add, remove, dry_run. Returns: result
zotero_searchSearch the Zotero libraryARead-only
Search the library and return a compact list of matching items.
Args: query: Free-text query. Matched against titles, creators and years by default. search_mode: "titleCreatorYear" for metadata only (fast), or "everything" to include note text and indexed PDF full text. item_type: Restrict to one Zotero item type, for example "journalArticle", "book", "thesis" or "preprint". tag: Restrict to items carrying this tag. Prefix with "-" to exclude a tag. collection: Collection key or collection name to search within. limit: Maximum number of items to return (1-100).
Returns: A Markdown list, one line per item, ending in the item key needed by zotero_get_item and the citation tools.
Inputs: query (required), tag, limit, item_type, +2 more. Returns: result
zotero_search_annotationsSearch across annotations in the libraryARead-only
Search the text of highlights and comments across the whole library.
Args: query: Substring to look for in highlighted text and comments. limit: Maximum number of matching annotations to return (1-100).
Returns: A Markdown list of matches, each naming the annotation key and the attachment it belongs to.
Inputs: query (required), limit. Returns: result
zotero_statusReport zotbridge connection statusARead-only
Report which transport is in use and whether writes are permitted.
Call this first when something is not working; the answer names the setting to change.
Returns: A short Markdown status report.
Inputs: no named parameters. Returns: result
zotero_update_itemEdit fields on a Zotero itemADestructive
Change one or more metadata fields on an existing item.
The item's current version is read first and supplied as
If-Unmodified-Since-Version, so a concurrent edit in the Zotero
desktop application causes the write to fail rather than silently
overwrite.
Args: item_key: Key of the item to edit. fields_json: A JSON object of field names to new values, for example {"volume": "12", "pages": "45-58"}. dry_run: Show the change without writing.
Returns: A before-and-after summary of the fields that changed.
Inputs: item_key (required), fields_json (required), dry_run. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 20 tools
Each tool targets a distinct operation: search vs. field-based search, item retrieval vs. annotation retrieval vs. annotation search, citation export for items vs. collections, etc. Even the two add tools are clearly separated (manual JSON vs. DOI lookup). No two tools appear to serve the same purpose.
All tools follow a consistent 'zotero_verb_noun' pattern with snake_case throughout. Verbs like get, add, list, create, update, search, export, and manage are used predictably, and there are no mixed conventions or arbitrary abbreviations.
With 20 tools, the surface is comprehensive but not bloated; it covers searching, retrieval, editing, annotation, citation export, collection/tag management, and authentication. The count is on the higher end of reasonable for a full-featured Zotero client, but each tool addresses a distinct need.
The toolset covers most core workflows (add, get, update, search, read fulltext, annotations, citations), but notable gaps exist: there is no delete item, create/delete collection, move item between collections, or attachment upload. These missing operations could leave agents unable to complete lifecycle management tasks.
Maintenance
Related MCP Connectors
Remote MCP server for full read/write access to a Zotero library
Zotero MCP server for Claude and ChatGPT: search, citations, safe writes, PDF passages and pages.
Read-only MCP server exposing a user ORANO library to their own AI agent.
Hosted MCP for LearnOS: search courses, lesson citations, flashcards and notes from any MCP client.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceMCP server for interacting with a Zotero library via the local API. Enables searching, retrieving, creating, updating, and deleting Zotero items, managing collections and tags, and generating citations.-
- AlicenseNot gradedqualityBmaintenanceEnables Zotero 10 library management via MCP, allowing search, metadata retrieval, annotation and collection management, and optional write operations (notes, tags, metadata, items) through a local Streamable HTTP endpoint.MIT
- AlicenseNot gradedqualityCmaintenanceEnables interaction with a locally running Zotero Desktop library via MCP, including search, collections, tags, BibTeX export, citations, attachment full-text lookup, and explicitly confirmed write operations.MIT
- AlicenseNot gradedqualityBmaintenanceEnables searching, reading, annotating, and managing a Zotero library from any MCP client.AGPL 3.0