Organize local PDF folders into Zotero collections with Google Drive links — entirely from the command line. No Claude required; Claude Desktop integration is available as an optional enhancement.
Full user manual (step-by-step installation, usage guide, troubleshooting): docs/manual.md · PDF
AI skills (Claude Code slash commands + prompts for other AI platforms): docs/skills/README.md
Mac and Linux users can follow this guide directly.
Windows users should run everything inside WSL2 (Windows Subsystem for Linux — recommended, since your local PDF folders remain accessible) or Docker. Native Windows is not supported: rclone's authentication flow and the shell environment assumed by this tool are Unix-based.
You need accounts and credentials from two services before installing anything. Collect them now and keep them somewhere safe — a password manager or a private text file — because you will enter them into the setup wizard.
- Create a free account at zotero.org if you don't have one.
- Log in → Settings → Feeds/API.
- Note your User ID — the numeric value shown at the top of that page.
- Click Create new private key, tick Read/Write access for your library, and copy the key before closing the page.
A personal Google account or an institutional Google Workspace account both work.
- Open Google Drive and create (or navigate to) the folder where uploaded PDFs should live.
- Copy the folder ID from the URL:
Everything after
https://drive.google.com/drive/folders/<FOLDER_ID>/folders/is your folder ID.
For users who already have rclone configured and both sets of credentials ready:
git clone https://github.com/aitgcodes/zotero-organizer.git
cd zotero-organizer
pip install -r requirements.txt # or use conda — see Detailed installation
python scripts/setup.py # enter your credentials when prompted
python scripts/organize.py /path/to/pdfs --collection "MyCollection"First time? Follow the Detailed installation below.
Python 3.10 or later is required (python --version to check).
Option A — conda (recommended; also enables the optional MCP server):
conda env create -f environment.yml
conda activate claudoteroOption B — pip into any existing environment:
pip install -r requirements.txtrclone handles uploads to Google Drive.
Install:
# macOS
brew install rclone
# Debian / Ubuntu
sudo apt install rclone
# or via the official installer:
curl https://rclone.org/install.sh | sudo bashCreate a Google Drive remote:
rclone configAt the prompts:
- Choose
n— new remote - Name:
gdrive(or any name you prefer; you will enter it during setup) - Storage type:
drive client_idandclient_secret: leave blank (press Enter)- Scope:
1— full access - Use auto config:
y— a browser window opens to authenticate your Google account
Test the connection (replace <FOLDER_ID> with your Drive folder ID):
rclone ls gdrive: --drive-root-folder-id=<FOLDER_ID>An empty result with no error means rclone can reach the folder.
git clone https://github.com/aitgcodes/zotero-organizer.git
cd zotero-organizerpython scripts/setup.pyThe wizard prompts for each value; press Enter to accept the default shown in brackets.
| Prompt | What to enter |
|---|---|
ORGANIZE_HOME |
Press Enter — defaults to the project folder |
ZOTERO_USER_ID |
Your numeric Zotero user ID |
ZOTERO_API_KEY |
Your Zotero API key (input is hidden) |
GOOGLE_DRIVE_FOLDER_ID |
The folder ID from the Drive URL |
RCLONE_REMOTE |
Your rclone remote name (default: gdrive) |
After entry the wizard validates connectivity to Zotero and Google Drive and prints a
summary. Re-run setup.py at any time to update a single value.
python scripts/organize.py /path/to/your/pdfs --collection "Plasmons"The tool runs three stages in sequence, prompting before each transition. Your PDF folder is never modified.
Stage 1 — Scan
Walks the folder recursively, extracts DOIs via a four-step fallback (PDF metadata →
page-1 text → Semantic Scholar title search → raw header), detects duplicates, and
writes collections/Plasmons/scan.json.
Stage 2 — Build collection structure
| Choice | What happens |
|---|---|
[1] Scaffold |
Writes taxonomy.yaml — review and edit it, then re-run |
[2] Auto |
Uses subfolder names as collection names directly — no editing |
[3] LLM/Claude |
Same as scaffold, intended for Claude or another LLM to suggest organization |
The scaffold option is recommended for first-time use: it gives you a reviewable file before anything is written to Zotero or Drive.
Stage 3 — Dry run → real run
Prints exactly what would be created and uploaded. Confirm to proceed. Progress is
saved to state.json; an interrupted run resumes from where it left off.
Open collections/Plasmons/taxonomy.yaml in any text editor:
base_collection: "Plasmons"
collections:
- name: "Quantum-Plasmonics"
parent: null
- name: "Hydrodynamic-Modeling"
parent: "Quantum-Plasmonics" # creates a nested sub-collection
assignments:
"SubfolderA/paper.pdf":
collection: "Quantum-Plasmonics"
tags: ["review", "2024"]
flagged: # PDFs whose DOI could not be resolved
- file: "SubfolderB/mystery.pdf"
search_query: "Plasmon resonance nanoparticle ..."
collection: "Quantum-Plasmonics"
tags: []
books: # non-journal items (books, theses, documents)
- file: "Books/textbook.pdf"
type: book # book | thesis | document
title: "Nanoplasmonics"
authors: [{first: "Stefan", last: "Maier"}]
year: "2007"
extra: {publisher: "Springer"}
collection: "Books-Notes"
tags: []For flagged entries, search Semantic Scholar using the search_query text, then
either add the correct DOI to scan.json or move the entry into assignments.
Run the same command again:
python scripts/organize.py /path/to/your/pdfs --collection "Plasmons"New files are detected automatically. A taxonomy_patch.yaml is written containing
only the additions. Merge the patch into your main taxonomy.yaml, then re-run.
If the Drive folder already has content from a previous run, the batch pauses:
⚠ Drive folder 'Plasmons/' already contains 3 item(s).
[1] Merge — add new files, skip existing <-- default
[2] Overwrite — replace existing files
[3] New folder — upload to 'Plasmons_20260610/' instead
[4] Abort
| Flag | Effect |
|---|---|
--mode scaffold|auto|taxonomy |
Skip the Stage 2 prompt |
--no-ss |
Skip Semantic Scholar search (faster, works offline) |
--full-scan |
Force a complete rescan even if scan.json exists |
--skip-scan |
Skip Stage 1 entirely |
--dry-run |
Stop after dry-run validation |
--workspace <path> |
Override the default workspace location |
zotero-organizer/
collections/
Plasmons/
scan.json ← Stage 1 output; updated incrementally on re-runs
taxonomy.yaml ← edit this to customise collections and tags
taxonomy_patch.yaml ← new files found on re-scan; merge into taxonomy.yaml
batch_run.py ← generated runner script
state.json ← progress tracker; delete to start fresh
The underlying scripts can be called directly when you need more control.
Stage 1 — Scan:
python scripts/scan_pdfs.py /path/to/pdfs --collection "Plasmons" \
--output collections/Plasmons/scan.json [--no-ss]Stage 2 — Build taxonomy:
# Scaffold:
python scripts/generate_batch.py --mode scaffold collections/Plasmons/scan.json \
--output collections/Plasmons/taxonomy.yaml
# Auto (no editing):
python scripts/generate_batch.py --mode auto collections/Plasmons/scan.json \
--output collections/Plasmons/batch_run.py
# From edited taxonomy.yaml:
python scripts/generate_batch.py --mode taxonomy \
collections/Plasmons/taxonomy.yaml collections/Plasmons/scan.json \
--output collections/Plasmons/batch_run.pyStage 3 — Run the batch:
python collections/Plasmons/batch_run.py --dry-run # validate only
python collections/Plasmons/batch_run.py # full run
python collections/Plasmons/batch_run.py --mode drive-only # Drive upload onlyAfter Stage 1, share taxonomy.yaml with Claude and ask it to suggest thematic
groupings, collection names, and tags. Save the edited file and re-run; choose
option [1] (use existing taxonomy) at the Stage 2 prompt.
zotero_mcp.py exposes the full workflow as callable tools inside Claude Desktop,
enabling an interactive per-PDF workflow.
Additional requirement: Claude Desktop and the
conda environment (which includes mcp).
Configure — add to Claude Desktop's config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"zotero": {
"command": "/path/to/conda/envs/claudotero/bin/python",
"args": ["/path/to/zotero-organizer/zotero_mcp.py"],
"env": {
"ZOTERO_USER_ID": "your_numeric_user_id",
"ZOTERO_API_KEY": "your_api_key",
"GOOGLE_DRIVE_FOLDER_ID": "your_folder_id",
"RCLONE_REMOTE": "gdrive"
}
}
}
}Find the Python path with: conda activate claudotero && which python
Restart Claude Desktop after saving.
Available tools:
| Tool | Description |
|---|---|
extract_doi_from_local_pdf |
Extract DOI from a local PDF |
create_collection |
Create a Zotero collection (with optional parent) |
add_item_by_doi |
Resolve DOI via Semantic Scholar, create Zotero item |
upload_pdf_to_drive |
Upload PDF to Drive via rclone |
add_url_attachment |
Attach a Drive URL to a Zotero item |
get_drive_folder_id |
Return the configured Drive folder ID |
get_collection_structure |
List sub-collections and items of a collection |
add_tags_to_item |
Add tags to an existing Zotero item |