Metadata-Version: 2.4
Name: zotero-fulltext
Version: 0.3.1
Summary: Fast fulltext Zotero MCP for Codex and Claude.
Author-email: Ulrich Atz <ulrich.atz@unibocconi.it>
License: MIT
License-File: LICENSE
Keywords: claude,codex,mcp,research,zotero
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Text Processing
Requires-Python: >=3.11
Requires-Dist: fastmcp<4.0.0,>=3.0.0
Description-Content-Type: text/markdown

[![MCP Server](https://badge.mcpx.dev?type=server&features=resources,tools)](https://modelcontextprotocol.io)
[![PyPI](https://img.shields.io/pypi/v/zotero-fulltext)](https://pypi.org/project/zotero-fulltext/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Access your [Zotero](https://www.zotero.org/) library with your favorite AI tool.

![Demo](demo.gif)

This MCP server for Zotero 8+ gives Claude and Codex access to your library via search and citekeys. It talks directly to Zotero's local API and aims to keep token usage low. Fulltext is fetched only on demand.

## Quick Start

1. Make sure Zotero 8+ is running with the [local API enabled](https://www.zotero.org/support/kb/connector_zotero_unavailable) (Settings → Advanced → "Allow other applications on this computer to communicate with Zotero").
2. Install the Claude Code plugin:

```
/plugin marketplace add statzhero/zotero-fulltext
/plugin install zotero@zotero-fulltext
```

3. Run `/mcp` to confirm the server is connected, then try a slash command:

```
/zotero:find sustainability reporting
```

Also works with [Claude Desktop](#claude-desktop), [Codex](#codex-plugin-with-skills), and as a [standalone MCP server](#configuration) without slash commands.

## Commands

The Claude Code plugin provides four slash commands:

| Command | What it does |
|---|---|
| `/zotero:find <query>` | Search the whole library |
| `/zotero:lookup <citekey>` | Exact citekey metadata lookup |
| `/zotero:read <citekey>` | Numbered fulltext paragraphs |
| `/zotero:within <citekey> <query>` | Search inside one paper's fulltext |

`find` searches the whole library. `lookup` is lightweight metadata confirmation. `read` returns the actual paper text. `within` searches only one item's indexed fulltext.

Without the plugin, all the same functionality is available through the MCP tools directly (see [Tools](#tools)).

## Installation

### Claude Code (plugin with slash commands)

```
/plugin marketplace add statzhero/zotero-fulltext
/plugin install zotero@zotero-fulltext
```

This installs the MCP server and slash commands. Run `/mcp` to confirm the server is connected.

### Claude Desktop

Requires [uv](https://docs.astral.sh/uv/getting-started/installation/) (install with `brew install uv` or `curl -LsSf https://astral.sh/uv/install.sh | sh`).

Open Claude Desktop → Settings → Developer → Edit Config, and add:

```json
{
  "mcpServers": {
    "zotero": {
      "type": "stdio",
      "command": "uvx",
      "args": ["zotero-fulltext"]
    }
  }
}
```

Save the file, then fully quit and reopen Claude Desktop (closing the window is not enough). Open a new chat and confirm the server is available.

### Codex (plugin with skills)

> **Note:** The Codex plugin marketplace is still rolling out. The install flow below may change.

```bash
codex plugin marketplace add statzhero/zotero-fulltext
codex plugin install zotero
```

This installs the MCP server and four skills (`find`, `lookup`, `read`, `within`).

### Codex (manual MCP only)

Requires [uv](https://docs.astral.sh/uv/getting-started/installation/) (install with `brew install uv` or `curl -LsSf https://astral.sh/uv/install.sh | sh`).

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.zotero]
command = "uvx"
args = ["zotero-fulltext"]
```

Restart Codex, then run `codex mcp list` to verify the server appears.

## Design

The server is intentionally simple and read-only. It relies on Zotero's own search index rather than building a second one.

- Startup builds a metadata index mapping citekeys to items and attachments.
- Library changes are tracked with Zotero version headers and incremental sync.
- Fulltext is fetched only on demand and cached in memory (TTL/LRU).
- All outputs are bounded by default: 10 search hits, 80 paragraphs with a character budget, 20 fulltext matches.
- Item results include `item_uri` and `fulltext_uri` so clients can attach standard `zotero://...` resources directly.
- If a lookup finds no citekey, it returns `found=false`. If a search finds nothing, it returns `results=[]`. There is no web fallback.

## Tools

The server exposes five MCP tools. The slash commands above are convenience wrappers.

**`lookup(citekey)`**

Exact citekey lookup. Citekeys are resolved in order:

1. Native Zotero 8 `citationKey`
2. Legacy Better BibTeX `Citation Key:` line in `Extra`
3. Deterministic generated fallback

If an item later gains a real citekey, the generated key is kept as an alias.

**`search(query, collection?, tag?, limit?)`**

Searches Zotero with `qmode=everything`, collapses attachment hits to parent items, and ranks exact citekey matches first.

**`collections()`**

Lists collections in the current library.

**`fulltext(citekey, offset?, limit?)`**

Fetches indexed attachment fulltext, splits it into numbered paragraphs, and returns a bounded slice (default: 80 paragraphs). Large extracted paragraphs are split into smaller chunks, and each response has a soft character budget. If the response includes `truncated=true`, request the same citekey again with `offset=next_offset` to continue reading.

Fulltext responses include paging metadata:

| Field | Meaning |
|---|---|
| `paragraph_count` | Total available paragraph chunks for the item |
| `returned_count` | Number of paragraph chunks in this response |
| `returned_chars` | Approximate text characters returned |
| `max_chars` | Character budget used for this response |
| `truncated` | Whether more paragraph chunks remain |
| `next_offset` | Offset to pass into the next `fulltext` call, or `null` when complete |

**`fulltext_search(citekey, query, before?, after?, limit?)`**

Searches within a single item's paragraphized fulltext and returns matching paragraphs with surrounding context.

## Environment Variables

By default the server connects to a local personal library with no authentication. Set these variables to change that:

| Variable | Default | Description |
|---|---|---|
| `ZOTERO_LIBRARY_TYPE` | `user` | `user` for personal libraries, `group` for group libraries |
| `ZOTERO_LIBRARY_ID` | `0` | Zotero user or group ID (required for group libraries) |
| `ZOTERO_API_KEY` | — | API key for authenticated or remote access |
| `ZOTERO_API_BASE_URL` | `http://127.0.0.1:23119/api` | Base URL for the Zotero API |
| `ZOTERO_MAX_PARAGRAPH_CHARS` | `1800` | Maximum characters per returned fulltext chunk |
| `ZOTERO_MAX_FULLTEXT_CHARS` | `60000` | Soft character budget for each `fulltext` response; continue with `next_offset` |

Example for a group library in Claude Code:

```json
{
  "mcpServers": {
    "zotero": {
      "type": "stdio",
      "command": "zotero-fulltext",
      "env": {
        "ZOTERO_LIBRARY_TYPE": "group",
        "ZOTERO_LIBRARY_ID": "12345"
      }
    }
  }
}
```

## Related Projects

Other MCP servers for Zotero, with different design goals:

- [54yyyu/zotero-mcp](https://github.com/54yyyu/zotero-mcp) — Feature-rich: read-write operations, optional semantic search via ChromaDB, Web API support. Heavier dependencies.
- [kujenga/zotero-mcp](https://github.com/kujenga/zotero-mcp) — Minimal read-only server with Web API support via pyzotero. No citekey resolution or in-document search.
- [kaliaboi/mcp-zotero](https://github.com/kaliaboi/mcp-zotero) — Cloud-only (Zotero Web API). Metadata browsing, no fulltext.

To remove an existing Zotero MCP server before switching:

```bash
claude mcp remove zotero
```

Or delete the `zotero` entry from `.mcp.json` / `claude_desktop_config.json` / `~/.codex/config.toml` manually.

## Requirements

- Zotero 8+ with the local API enabled
- Python 3.11+
- Better BibTeX (optional but recommended)

## License

MIT • Ulrich Atz ([ulrichatz](https://bsky.app/profile/ulrichatz.org))
