Skip to main content

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

Project description

zotero-headless

zotero-headless logo

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:

uv tool install zotero-headless

or

pipx install zotero-headless

From source:

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

Main entrypoints:

zotero-headless
zotero-headless-daemon
zotero-headless-mcp

Short aliases:

zhl
zhl-daemon
zhl-mcp

Quick Start

Run the setup flow:

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.

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:

zhl config autodiscover

You can also reconfigure specific parts later:

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:
uv tool install zotero-headless
  1. Run guided setup. On a standard desktop install, autodiscovery should usually find your Zotero data directory automatically:
zhl setup start
  1. Install the MCP server into Codex:
zhl setup add codex --scope user
  1. Install the Codex skill pack:
zhl skill install codex
  1. Start using whichever interface fits the task:
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:
uv tool install zotero-headless
  1. 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:
zhl setup start
  1. Start the daemon runtime with background sync:
zhl-daemon serve --host 0.0.0.0 --port 8787 --sync-interval 300
  1. Use the API or MCP depending on the integration:
curl -s http://127.0.0.1:8787/capabilities
zhl-mcp
  1. 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:
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:

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:

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

Run the API directly without the daemon wrapper:

zhl api serve --host 127.0.0.1 --port 8787

Run the MCP server:

zhl-mcp

Check version and update:

zhl version
zhl update --check
zhl update
zhl --json doctor

Release maintenance:

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

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

Inspect capabilities and daemon state:

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:

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:

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:

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

MCP/client setup:

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:

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

Still intentionally narrow:

  • some local desktop writeback edge cases
  • full Zotero file-sync parity across every attachment mode and conflict case
  • broader packaging/release polish

Documentation

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

zotero_headless-0.4.1.tar.gz (105.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zotero_headless-0.4.1-py3-none-any.whl (89.7 kB view details)

Uploaded Python 3

File details

Details for the file zotero_headless-0.4.1.tar.gz.

File metadata

  • Download URL: zotero_headless-0.4.1.tar.gz
  • Upload date:
  • Size: 105.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for zotero_headless-0.4.1.tar.gz
Algorithm Hash digest
SHA256 629ecd4d64969e212b9ff8d371ce721d023b95c44907b52b30874ffbd79fffe0
MD5 1bd5fea31b76d8ff348845a8a69559b5
BLAKE2b-256 298f23f8642a959f58a1a9efd8bece800ca054da6c760dc1101c216f70ff40a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotero_headless-0.4.1.tar.gz:

Publisher: publish.yml on robinradx/zotero-headless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file zotero_headless-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for zotero_headless-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 36bfa22bc16b7ea0ae1466ef9a4c2f5faa57523e96258345b4df3e8b74b6cd9f
MD5 0cdf9e7bcedb80c8e907533319c7f1ed
BLAKE2b-256 2cd53ce15281402f80c04e47e6fe5108612df152a1b2ca8409d2ce6ee6c4c42c

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotero_headless-0.4.1-py3-none-any.whl:

Publisher: publish.yml on robinradx/zotero-headless

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page