Skip to main content
Glama
README.md
# 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

- [Why another Zotero MCP server](#why-another-zotero-mcp-server)
- [Requirements](#requirements)
- [Installation](#installation)
- [Connecting Zotero](#connecting-zotero)
- [Connecting a client](#connecting-a-client)
  - [Claude Desktop](#claude-desktop)
  - [ChatGPT](#chatgpt)
  - [Other MCP clients](#other-mcp-clients)
- [Tool reference](#tool-reference)
- [Worked examples](#worked-examples)
- [Configuration](#configuration)
- [Writing to the library](#writing-to-the-library)
- [Development](#development)
- [Troubleshooting](#troubleshooting)
- [Licence](#licence)

---

## 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 `pdf` extra)

## Installation

```bash
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:

```bash
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

1. Open Zotero 7.
2. Go to **Edit -> Settings -> Advanced**.
3. Tick **Allow other applications on this computer to communicate with
   Zotero**.
4. 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

1. Sign in at <https://www.zotero.org/settings/keys> and create a new key.
2. Grant it read access, and write access only if you intend to use the write
   tools.
3. Note the numeric **userID** shown on the same page.
4. Put both in your `.env`:

```ini
ZOTERO_API_KEY=your_key_here
ZOTERO_LIBRARY_ID=1234567
ZOTERO_LIBRARY_TYPE=user
```

With `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.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

```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:

```bash
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 |
| --- | --- |
| `zotero_search` | Free-text search over metadata, or over full text with `search_mode="everything"` |
| `zotero_advanced_search` | Field-specific search with a publication-year range |
| `zotero_list_collections` | Collections with keys and item counts |
| `zotero_list_tags` | Tags in the library, optionally filtered |
| `zotero_get_recent` | Most recently added items |
| `zotero_status` | Transport in use, write permission, reachability |

### Retrieval

| Tool | Purpose |
| --- | --- |
| `zotero_get_item` | Full metadata, plus a ready-made APA 7th reference |
| `zotero_get_children` | Notes and attachments belonging to an item |
| `zotero_get_collection_items` | Contents of one collection |
| `zotero_get_fulltext` | Text of the item's best attachment, with optional page selection |

### Citations

| Tool | Purpose |
| --- | --- |
| `zotero_export_citations` | APA 7th, in-text, BibTeX or CSL-JSON for named items |
| `zotero_export_collection_bibliography` | The same for an entire collection |

### Annotations

| Tool | Purpose |
| --- | --- |
| `zotero_get_annotations` | Highlights and comments on an item, grouped by page, filterable by colour |
| `zotero_search_annotations` | Search annotation text across the library |

### Writing

| Tool | Purpose |
| --- | --- |
| `zotero_authorize_local` | Request a local write key from the desktop application |
| `zotero_add_by_doi` | Look a DOI up on Crossref and save the item |
| `zotero_add_item` | Create an item from metadata you supply |
| `zotero_create_note` | Attach a child note to an item |
| `zotero_update_item` | Edit fields, with version checking |
| `zotero_manage_tags` | 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 `ABCD1234` and list the primer design decisions the
> authors justify explicitly.

> Pull my yellow highlights from `EFGH5678` and 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 |
| --- | --- | --- |
| `ZOTBRIDGE_TRANSPORT` | `auto` | `auto`, `local` or `web` |
| `ZOTBRIDGE_LOCAL_BASE_URL` | `http://localhost:23119/api` | Desktop API base |
| `ZOTERO_API_KEY` | — | Web API key |
| `ZOTERO_LIBRARY_ID` | — | Numeric user or group ID |
| `ZOTERO_LIBRARY_TYPE` | `user` | `user` or `group` |
| `ZOTBRIDGE_LOCAL_API_KEY` | — | Local write key from `zotero_authorize_local` |
| `ZOTBRIDGE_ALLOW_WRITES` | `false` | Master switch for every write tool |
| `ZOTBRIDGE_TIMEOUT` | `30` | Network timeout in seconds |
| `ZOTBRIDGE_MAX_FULLTEXT_CHARS` | `50000` | Cap on returned PDF text |
| `ZOTBRIDGE_LOG_LEVEL` | `INFO` | Log verbosity, written to stderr |

## Writing to the library

Writing is off by default, and turning it on takes three deliberate steps.

1. Set `ZOTBRIDGE_ALLOW_WRITES=true`.
2. Call `zotero_authorize_local` and approve the dialogue Zotero raises. Copy
   the key into `ZOTBRIDGE_LOCAL_API_KEY`. (Local write support arrived in
   Zotero 10. On earlier desktop versions, configure the web API with a
   write-enabled key and set `ZOTBRIDGE_TRANSPORT=web` instead.)
3. 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

```bash
pip install -e ".[dev]"
pytest
ruff check src tests
```

The 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 family
```

## Troubleshooting

**"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](LICENSE).

zotbridge is an independent project and is not affiliated with or endorsed by
the Corporation for Digital Scholarship, which develops Zotero.

TDQS

A4.2/5.0

Scored across 20 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness3/5

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

ActivityMaintained
ResponsivenessNo issues