Metadata-Version: 2.4
Name: zotero-headless
Version: 0.6.0
Summary: Headless Zotero-compatible runtime with CLI, HTTP API, MCP, local desktop interoperability, web sync, qmd-backed semantic search, and support for your agent tool of choice
Author: Robin Rademacher
License-Expression: MIT
Project-URL: Homepage, https://github.com/robinradx/zotero-headless
Project-URL: Repository, https://github.com/robinradx/zotero-headless
Project-URL: Issues, https://github.com/robinradx/zotero-headless/issues
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.1.8
Requires-Dist: questionary>=2.1.0
Requires-Dist: rich>=14.0.0
Requires-Dist: typer>=0.24.0
Dynamic: license-file

# zotero-headless

<p align="center">
  <img src="./assets/zotero-headless.png" alt="zotero-headless logo" width="220" />
</p>

`zotero-headless` is an open-source headless Zotero-compatible runtime with:

- a CLI
- an HTTP API
- an MCP server
- a clean-room headless store
- Zotero web sync
- local Zotero desktop interoperability
- qmd-backed semantic search that is automatically refreshed from library changes
- compatibility with the agent tool of your choice through API or MCP

All three main interfaces are first-class:

- CLI
  - good for humans, scripts, and shell automation
- HTTP API
  - good for apps, services, agents, and direct integrations
- MCP
  - good for agent tools that already speak MCP

The project is built for two use cases:

- desktop interoperability
  - work against an existing local Zotero profile
  - import, poll, and apply the currently supported subset of changes back to the local desktop database
- headless/server runtime
  - run `zotero-headless-daemon` on a machine without the Zotero GUI
  - expose API and MCP for automation, retrieval, background sync jobs, and agent integrations

## Status

This is still pre-release, but it is no longer just a sketch. The codebase already includes:

- headless SQLite store plus change log
- `zotero-headless` CLI
- `zotero-headless-daemon` runtime
- `zotero-headless-mcp` stdio server
- local HTTP API
- Zotero web sync for user and group libraries
- local Zotero desktop import, polling, and narrow apply/writeback support
- remote attachment upload/download for the currently supported stored-file and snapshot-style paths
- Better BibTeX-oriented citekey compatibility
- qmd export plus semantic search over Markdown derived from headless state, with automatic refresh on dataset changes
- MCP setup helpers for common agent tools
- runtime observability endpoints and background sync status

## What It Is For

Typical end-user use cases:

- run a headless Zotero-compatible service on a server
- query or mutate libraries through CLI, API, or MCP
- sync against Zotero web libraries without requiring the Zotero GUI to be running
- work against a local desktop Zotero profile when local interoperability is needed
- query library content through qmd-backed semantic search flows without manually rebuilding the qmd index after normal sync/write activity

This repository also contains contribution and architecture material because the project is still evolving, but the repo is not meant only for contributors.

## Why The Repo Includes Vendored Zotero Code

This repository intentionally includes a vendored Zotero source snapshot under `vendor/`.

That is here for contributor visibility and debugging, not because the whole project is just a Zotero wrapper. The current architecture is a clean-room headless runtime with adapters around Zotero desktop and Zotero web sync, but understanding the upstream desktop/runtime behavior still matters for:

- local database interoperability
- daemon/bootstrap experiments
- sync semantics
- attachment handling
- reproducible debugging for contributors

For this project, keeping that context available is more useful than hiding it in a separate private mirror.

## Repository Layout

- `src/zotero_headless/`
  - main runtime, CLI, API, MCP, sync, and adapter code
- `tests/`
  - regression coverage for the runtime, sync, adapter, and tooling surfaces
- `docs/`
  - architecture notes and implementation planning
- `vendor/`
  - vendored Zotero source snapshot used for reference and compatibility work

Local-only workspace material should go in ignored directories such as:

- `.codex/`
- `.agents/`
- `.notes/`
- `.tmp/`

## Install

Recommended install methods:

```text
uv tool install zotero-headless
```

or

```text
pipx install zotero-headless
```

From source:

```text
git clone https://github.com/<owner>/zotero-headless.git
cd zotero-headless
PYTHONPATH=src python3 -m zotero_headless capabilities
```

Main entrypoints:

```text
zotero-headless
zotero-headless-daemon
zotero-headless-mcp
```

Short aliases:

```text
zhl
zhl-daemon
zhl-mcp
```

## Quick Start

Run the setup flow:

```text
zhl setup start
```

`setup start` tries autodiscovery first, uses standard local Zotero paths automatically when it finds them, and only prompts for values that are still missing.

Interactive commands now default to human-readable output. If you want script-friendly payloads for setup, version, update, doctor, or daemon status commands, add `--json`.

The CLI is now split by audience:

- human/operator flows live on the main top-level commands like `setup`, `doctor`, `version`, and `daemon`
- strict programmatic CLI usage lives under `zhl raw ...`

Use `zhl raw ...` when you want non-interactive, automation-friendly command paths that mirror the underlying data operations closely.

Autodiscovery looks for:

- standard Zotero data directories such as `~/Zotero`
- common Zotero desktop binary locations
- already-saved API credentials and remote-library selections

Then the wizard will:

- tell you when it autodiscovers a standard local Zotero setup, and only ask for local desktop paths when it cannot infer them or when you explicitly reconfigure them
- use explicit confirmation prompts such as `[y/N]` and `[Y/n]` where a yes-or-no decision is needed
- ask for your Zotero API key only when web sync is needed
- discover your personal library and available group libraries
- let you choose which remote libraries to configure
- store a default remote library for later use

That means it also works for:

- a Linux server where this is the only Zotero-related install
- rerunning setup later to add or remove group libraries
- switching to a different Zotero account in true headless mode
- changing local Zotero paths without redoing the whole setup

You can inspect what autodiscovery sees without changing config:

```text
zhl config autodiscover
```

You can also reconfigure specific parts later:

```text
zhl setup account
zhl setup libraries
zhl setup local
```

### Example: Codex On A Desktop With Zotero Installed

Use this when you already have Zotero Desktop on your machine and want `zotero-headless` to interoperate with that local profile, while also making MCP and skills available in Codex.

1. Install the CLI:

```text
uv tool install zotero-headless
```

2. Run guided setup. On a standard desktop install, autodiscovery should usually find your Zotero data directory automatically:

```text
zhl setup start
```

3. Install the MCP server into Codex:

```text
zhl setup add codex --scope user
```

4. Install the Codex skill pack:

```text
zhl skill install codex
```

5. Start using whichever interface fits the task:

```text
zhl local import
zhl qmd query "papers about retrieval augmented generation"
zhl api serve --host 127.0.0.1 --port 8787
zhl-mcp
```

Typical result:

- local Zotero desktop data is imported and can be polled/applied
- Codex can connect through MCP
- Codex can also call the HTTP API directly when that is the better fit
- qmd-backed semantic search stays in sync automatically as headless data changes

### Example: Standalone Headless Linux Server

Use this when the machine does not run Zotero Desktop and `zotero-headless` is the only Zotero-related runtime on the box.

1. Install the CLI:

```text
uv tool install zotero-headless
```

2. Run guided setup. Autodiscovery will likely find little on a clean server, so the wizard will prompt for your Zotero API key and remote libraries:

```text
zhl setup start
```

3. Start the daemon runtime with background sync:

```text
zhl-daemon serve --host 0.0.0.0 --port 8787 --sync-interval 300
```

4. Use the API or MCP depending on the integration:

```text
curl -s http://127.0.0.1:8787/capabilities
zhl-mcp
```

5. If you want Codex or another agent client to connect to that server-hosted install, add MCP setup and install the matching skill pack on the client machine:

```text
zhl setup add codex --scope user
zhl skill install codex
```

Typical result:

- personal and group libraries sync from Zotero Web
- the daemon hosts API, MCP, background sync, and semantic search workflows
- agent clients can use either MCP or the HTTP API
- no local Zotero GUI or desktop profile is required

For non-interactive automation, you can still initialize configuration directly:

```text
python -m zotero_headless config init \
  --data-dir "$HOME/Zotero" \
  --api-key "$ZOTERO_API_KEY" \
  --user-id 123456 \
  --remote-library-id user:123456 \
  --remote-library-id group:654321 \
  --default-library-id user:123456
```

Run the daemon runtime:

```text
zhl-daemon serve --host 127.0.0.1 --port 8787 --sync-interval 300
```

Run the API directly without the daemon wrapper:

```text
zhl api serve --host 127.0.0.1 --port 8787
```

Run the MCP server:

```text
zhl-mcp
```

Check version and update:

```text
zhl version
zhl update --check
zhl update
zhl --json doctor
```

Release maintenance:

```text
make release VERSION=0.4.0
```

API exposure works in two modes:

- `zotero-headless api serve`
  - standalone HTTP API process
- `zotero-headless-daemon serve`
  - daemon runtime that hosts the same HTTP API plus runtime state and background sync

Strict machine-oriented CLI examples:

```text
zhl raw sync discover
zhl raw sync pull --library user:123456
zhl raw item create user:123456 '{"itemType":"note","note":"Hello"}'
zhl raw local import
```

So no, the API is not only exposed on `zotero-headless-daemon`.

Inspect capabilities and daemon state:

```text
zotero-headless capabilities
zotero-headless daemon status
zotero-headless doctor
```

## Choosing An Interface

Use the CLI if you want:

- terminal-first workflows
- shell scripts
- direct local administration

Use the HTTP API if you want:

- app-to-app integration
- service orchestration
- direct agent integrations without MCP
- long-running daemon deployments

Use MCP if you want:

- native tool use inside MCP-capable agent clients
- easy installation into Codex, Claude Code, Cursor, Gemini, Cline, Windsurf, and similar tools

## Current Command Surface

Local desktop interoperability:

```text
zotero-headless local libraries
zotero-headless local import
zotero-headless local poll
zotero-headless local plan-apply --library local:1
zotero-headless local apply --library local:1
```

Remote sync:

```text
zotero-headless sync discover
zotero-headless sync pull --library user:123456
zotero-headless sync push --library user:123456
zotero-headless sync conflicts --library user:123456
```

qmd flows:

```text
zotero-headless qmd export
zotero-headless qmd query "retrieval augmented generation"
```

MCP/client setup:

```text
zotero-headless setup list
zotero-headless setup add codex --scope user
zotero-headless setup add claude-code --scope project
zotero-headless setup add claude-desktop --scope user
zotero-headless setup add cursor --scope project
zotero-headless setup add gemini --scope user
zotero-headless setup add cline --scope user
zotero-headless setup add antigravity --scope user
zotero-headless setup add windsurf --scope user
```

Agent skill helpers:

```text
zotero-headless skill add claude-desktop
zotero-headless skill install codex
zotero-headless skill install claude-code
zotero-headless skill install gemini-cli
zotero-headless skill install cline
zotero-headless skill install antigravity
zotero-headless skill install openclaw
zotero-headless skill install opencode
```

`zotero-headless skill add claude-desktop` generates a Claude skill archive on your Desktop. Upload that archive in the Skills section of Claude Desktop or on claude.ai.

## What Is Implemented vs. What Is Still Narrow

Implemented:

- headless store and mutation log
- runtime daemon, API, and MCP server
- Zotero web sync for remote libraries
- local desktop import and polling
- narrow local writeback/apply support for the supported item, collection, note, annotation, and attachment paths
- remote attachment handling for the currently supported stored-file and snapshot-style paths
- Better BibTeX-oriented citekey handling

## Documentation

- [ARCHITECTURE.md](./docs/ARCHITECTURE.md)
- [IMPLEMENTATION_PLAN.md](./docs/IMPLEMENTATION_PLAN.md)
- [ZOTERO_SOURCE_NOTES.md](./docs/ZOTERO_SOURCE_NOTES.md)
- [CONTRIBUTING.md](./CONTRIBUTING.md)

## References

- [Zotero Repository](https://github.com/zotero/zotero)
- [Zotero Web API Syncing](https://www.zotero.org/support/dev/web_api/v3/syncing)
- [Direct SQLite Database Access](https://www.zotero.org/support/dev/client_coding/direct_sqlite_database_access)
- [qmd README](https://github.com/tobi/qmd)
