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 is an open-source headless Zotero-compatible runtime with:
- a CLI
- an HTTP API
- an MCP server
- a clean-room canonical store
- Zotero web sync
- local Zotero desktop interoperability
- qmd-backed semantic search over exported library content
- 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-daemonon a machine without the Zotero GUI - expose API and MCP for automation, retrieval, background sync jobs, and agent integrations
- run
Status
This is still pre-release, but it is no longer just a sketch. The codebase already includes:
- canonical SQLite store plus change log
zotero-headlessCLIzotero-headless-daemonruntimezotero-headless-mcpstdio 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 and semantic search over Markdown derived from canonical state
- 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
- export/query library content through qmd-backed semantic search flows
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
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 and then falls back to prompts for anything still missing.
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:
- ask for your local Zotero data directory
- 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
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
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 canonical-discover
zotero-headless sync canonical-pull --library user:123456
zotero-headless sync canonical-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 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
What Is Implemented vs. What Is Still Narrow
Implemented:
- canonical 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zotero_headless-0.1.0.tar.gz.
File metadata
- Download URL: zotero_headless-0.1.0.tar.gz
- Upload date:
- Size: 92.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29fb6eda5afe7379e88f30be29c1f9d1e61f80faa9cb62c082ef164f70daf5f1
|
|
| MD5 |
61aadc9297f70887a36b7b04b71d95b4
|
|
| BLAKE2b-256 |
23401586dff7ab4f37051320ea0e052c9eb85a508a604f46f81e356bb4007aef
|
Provenance
The following attestation bundles were made for zotero_headless-0.1.0.tar.gz:
Publisher:
publish.yml on robinradx/zotero-headless
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zotero_headless-0.1.0.tar.gz -
Subject digest:
29fb6eda5afe7379e88f30be29c1f9d1e61f80faa9cb62c082ef164f70daf5f1 - Sigstore transparency entry: 1244383991
- Sigstore integration time:
-
Permalink:
robinradx/zotero-headless@ee3c2e7cc90f8326f6f1feca33bfa388e24f639e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/robinradx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ee3c2e7cc90f8326f6f1feca33bfa388e24f639e -
Trigger Event:
push
-
Statement type:
File details
Details for the file zotero_headless-0.1.0-py3-none-any.whl.
File metadata
- Download URL: zotero_headless-0.1.0-py3-none-any.whl
- Upload date:
- Size: 79.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdf17bff42193bc574b934eaf7a43f66a90d8d84ff311024d8a8e49049380cf1
|
|
| MD5 |
7f8147301dce5c0e07ebda545796d7f6
|
|
| BLAKE2b-256 |
7110c210f18f1c81ed1fef54d016d281c04459dc464465bac234cda9e7f9a03b
|
Provenance
The following attestation bundles were made for zotero_headless-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on robinradx/zotero-headless
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zotero_headless-0.1.0-py3-none-any.whl -
Subject digest:
bdf17bff42193bc574b934eaf7a43f66a90d8d84ff311024d8a8e49049380cf1 - Sigstore transparency entry: 1244384001
- Sigstore integration time:
-
Permalink:
robinradx/zotero-headless@ee3c2e7cc90f8326f6f1feca33bfa388e24f639e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/robinradx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ee3c2e7cc90f8326f6f1feca33bfa388e24f639e -
Trigger Event:
push
-
Statement type: