
Zotero Paper Reader
- 566 installs
- 8 repo stars
- Updated December 5, 2025
- fuzhiyu/researchprojecttemplate
zotero-paper-reader is a Claude Code skill that automatically extracts structured insights, summaries, and citations from academic papers stored in Zotero for developers who need agent-driven literature review without ma
About
zotero-paper-reader is an AI & Agent Building skill from fuzhiyu/researchprojecttemplate that reads academic papers in a Zotero library and returns structured insights, summaries, and citations for coding agents. Listed on skills.sh with 1 install and rank 2662, the skill targets developers automating literature review, building research assistants, or feeding paper metadata into downstream note systems. Reach for zotero-paper-reader when a Zotero collection must be summarized, key claims extracted, or bibliographic citations prepared without hand-annotating each PDF. The workflow assumes papers are already imported into Zotero and focuses on agent-readable structured output rather than reference manager setup.
- Reads and parses PDFs from your local Zotero library
- Generates structured summaries, key findings, and citation metadata
- Outputs ready-to-use research notes and literature review drafts
- Works as a reusable agent capability for systematic literature review
Zotero Paper Reader by the numbers
- 566 all-time installs (skills.sh)
- Ranked #1,629 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 24, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fuzhiyu/researchprojecttemplate --skill zotero-paper-readerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 566 |
|---|---|
| repo stars | ★ 8 |
| Last updated | December 5, 2025 |
| Repository | fuzhiyu/researchprojecttemplate ↗ |
How do you summarize academic papers from a Zotero library?
Automatically extract structured insights, summaries, and citations from academic papers stored in Zotero.
Who is it for?
Developers building research automation pipelines who keep papers in Zotero and need agent-generated summaries and citations.
Skip if: Users without a Zotero library configured, teams needing full PDF OCR for unscanned papers, or non-academic document summarization outside Zotero.
When should I use this skill?
A user asks to summarize, extract insights from, or cite papers stored in Zotero.
What you get
Structured paper insights, narrative summaries, and formatted citation extracts per Zotero item
- Structured paper insights
- Summary text
- Citation extracts
By the numbers
- Listed with 1 install on skills.sh
- skills.sh rank 2662 in catalog listing
Files
Zotero Paper Reader
Overview
Read and analyze academic papers directly from the Zotero library. This skill handles the complete workflow from searching Zotero to converting PDFs to readable markdown format.
When to Use
Use this skill when the user requests to:
- "Read this paper from Zotero"
- "Find and read [paper title]"
- "Access [author name] paper"
- "Get the paper about [topic]"
- "Convert [paper] from Zotero to markdown"
Workflow
Step 1: Search Zotero Library
Use the Zotero MCP tools to search for the paper:
# Search by title, author, or keywords
mcp__zotero__zotero_search_items(query="paper title or author", limit=5)Present the search results to the user if multiple papers are found. Get the item_key from the selected paper.
Step 2: Get PDF Attachment
Retrieve the PDF attachment information:
# Get child items (attachments) for the paper
mcp__zotero__zotero_get_item_children(item_key="ITEM_KEY")Look for the attachment with type: application/pdf and note its Key (attachment key) and Filename.
Step 3: Get PDF File (Local or Download)
Use the bundled script to get the PDF - it automatically tries local storage first, then downloads if needed:
uv run python .claude/skills/zotero-paper-reader/scripts/get_zotero_pdf.py ATTACHMENT_KEYThe script workflow: 1. First searches local Zotero storage (~/Zotero/storage/ATTACHMENT_KEY/) 2. If found locally, returns that path 3. If not found locally, downloads from Zotero web library using API 4. Retrieves original filename from Zotero metadata and downloads to /tmp/[original_filename]
Security note: The Zotero API key and library configuration are read directly from Notes/.env by the Python script and never exposed to the LLM. Required environment variables: ZOTERO_API_KEY, ZOTERO_LIBRARY_TYPE, ZOTERO_LIBRARY_ID.
Step 4: Convert to Markdown
Use the mistral-pdf-to-markdown skill to convert the PDF:
uv run python .claude/skills/mistral-pdf-to-markdown/scripts/convert_pdf_to_markdown.py \
"PATH_TO_PDF" \
"Notes/PaperInMarkdown/CLEAN_FILENAME.md"Filename convention: Create a clean filename from the paper metadata:
- Format:
Author_Year_Title.md - Example:
Du_et_al_2023_Are_Intermediary_Constraints_Priced.md - Replace spaces with underscores, remove special characters
Step 5: Read and Analyze
Read the converted markdown file:
# For large papers, read in sections
Read(file_path="Notes/PaperInMarkdown/FILENAME.md", offset=1, limit=500)Since academic papers are often large (>25k tokens), read strategically:
- Start with abstract and introduction (first 300-500 lines)
- Use Grep to search for specific sections if needed
- Read specific sections based on user interest
Provide the user with: 1. Paper metadata (title, authors, publication year) 2. Brief summary of the abstract 3. Main findings or sections of interest 4. Offer to read specific sections or search for particular content
Example Usage
User request: "Read the paper 'Are Intermediary Constraints Priced' from my Zotero library"
Workflow: 1. Search: mcp__zotero__zotero_search_items(query="Are Intermediary Constraints Priced") 2. Get attachment: mcp__zotero__zotero_get_item_children(item_key="KPRQ2DLZ") 3. Get PDF: uv run python .claude/skills/zotero-paper-reader/scripts/get_zotero_pdf.py 2HSELEHX
- Returns local path if available, or downloads and returns temp path
4. Convert: uv run python .claude/skills/mistral-pdf-to-markdown/scripts/convert_pdf_to_markdown.py [PDF_PATH] Notes/PaperInMarkdown/Du_et_al_2023_Are_Intermediary_Constraints_Priced.md 5. Read: Read(file_path="Notes/PaperInMarkdown/Du_et_al_2023_Are_Intermediary_Constraints_Priced.md", limit=500) 6. Summarize and offer to dive deeper into specific sections
Notes
- The skill works with both local and web Zotero libraries
- For web libraries, requires
ZOTERO_API_KEY,ZOTERO_LIBRARY_TYPE, andZOTERO_LIBRARY_IDinNotes/.env - For local libraries, Zotero local API must be enabled in Zotero preferences
- Requires Mistral API key in
Notes/.envfor PDF conversion - Converted markdown files include extracted images in
images/subfolder - Large papers (>40k tokens) should be read in sections to avoid context limits
Resources
scripts/
get_zotero_pdf.py- Unified script that tries local storage first, then downloads from web API if needed
#!/usr/bin/env python3
"""
Get PDF file from Zotero library - tries local first, then downloads from web if needed.
This script first attempts to locate the PDF in local Zotero storage.
If not found locally, it downloads from the Zotero web library using the API.
"""
import sys
import os
import subprocess
from pathlib import Path
def find_local_pdf(attachment_key):
"""
Try to find PDF in local Zotero storage.
Args:
attachment_key: The attachment item key
Returns:
Path to PDF if found, None otherwise
"""
zotero_storage = Path.home() / "Zotero" / "storage"
if not zotero_storage.exists():
return None
storage_folder = zotero_storage / attachment_key
if not storage_folder.exists():
return None
pdf_files = list(storage_folder.glob("*.pdf"))
if pdf_files:
return str(pdf_files[0])
return None
def get_api_key():
"""
Read Zotero API key from the .env file.
Returns:
API key string, or None if not found
"""
env_file = Path("Notes/.env")
if not env_file.exists():
print(f"Error: .env file not found at {env_file}", file=sys.stderr)
return None
with open(env_file) as f:
for line in f:
line = line.strip()
if line.startswith("ZOTERO_API_KEY="):
return line.split("=", 1)[1]
print("Error: ZOTERO_API_KEY not found in .env file", file=sys.stderr)
return None
def get_library_config():
"""
Read library configuration from .env file.
Returns:
Tuple of (library_type, library_id) or (None, None) if not found
"""
env_file = Path("Notes/.env")
if not env_file.exists():
print(f"Error: .env file not found at {env_file}", file=sys.stderr)
return None, None
library_type = "user" # default
library_id = None
with open(env_file) as f:
for line in f:
line = line.strip()
if line.startswith("ZOTERO_LIBRARY_TYPE="):
library_type = line.split("=", 1)[1]
elif line.startswith("ZOTERO_LIBRARY_ID="):
library_id = line.split("=", 1)[1]
if not library_id:
print("Error: ZOTERO_LIBRARY_ID not found in .env file", file=sys.stderr)
return None, None
return library_type, library_id
def get_original_filename(attachment_key):
"""
Get the original filename from Zotero API.
Args:
attachment_key: The attachment item key
Returns:
Original filename or None if not found
"""
api_key = get_api_key()
if not api_key:
return None
library_type, library_id = get_library_config()
if not library_id:
return None
# Construct API URL for item metadata
if library_type == "group":
api_url = f"https://api.zotero.org/groups/{library_id}/items/{attachment_key}"
else:
api_url = f"https://api.zotero.org/users/{library_id}/items/{attachment_key}"
# Get metadata with curl
cmd = [
"curl", "-s",
"-H", f"Zotero-API-Key: {api_key}",
"-H", "Zotero-API-Version: 3",
api_url
]
try:
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
import json
data = json.loads(result.stdout)
filename = data.get("data", {}).get("filename")
return filename
except:
return None
def download_pdf(attachment_key):
"""
Download PDF from Zotero web library.
Args:
attachment_key: The attachment item key
Returns:
Path to downloaded file if successful, None otherwise
"""
api_key = get_api_key()
if not api_key:
return None
library_type, library_id = get_library_config()
if not library_id:
return None
# Get original filename
original_filename = get_original_filename(attachment_key)
if not original_filename:
# Fallback to attachment key if we can't get the filename
original_filename = f"{attachment_key}.pdf"
print(f"Warning: Could not get original filename, using {original_filename}", file=sys.stderr)
output_path = f"/tmp/{original_filename}"
# Construct API URL
if library_type == "group":
api_url = f"https://api.zotero.org/groups/{library_id}/items/{attachment_key}/file"
else:
api_url = f"https://api.zotero.org/users/{library_id}/items/{attachment_key}/file"
# Use curl to download with redirect following
cmd = [
"curl", "-L", "-s",
"-H", f"Zotero-API-Key: {api_key}",
"-H", "Zotero-API-Version: 3",
api_url,
"-o", str(output_path)
]
try:
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
# Verify the file was downloaded and is a valid PDF
# PDFs should be at least 1KB (typical minimum is much larger)
if Path(output_path).exists():
file_size = Path(output_path).stat().st_size
if file_size < 1024:
print(f"Error: Downloaded file is too small ({file_size} bytes), likely an error response", file=sys.stderr)
return None
return str(output_path)
else:
print(f"Error: Download failed - file not created", file=sys.stderr)
return None
except subprocess.CalledProcessError as e:
print(f"Error downloading PDF: {e}", file=sys.stderr)
return None
def get_pdf(attachment_key):
"""
Get PDF file - tries local first, downloads if needed.
Args:
attachment_key: The attachment item key
Returns:
Path to PDF file (local or downloaded to /tmp)
"""
# First, try to find locally
local_path = find_local_pdf(attachment_key)
if local_path:
print(f"Found local PDF: {local_path}")
return local_path
# If not found locally, download from web
print("PDF not found locally, downloading from web library...", file=sys.stderr)
downloaded_path = download_pdf(attachment_key)
if downloaded_path:
print(f"Downloaded PDF: {downloaded_path}")
return downloaded_path
else:
return None
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_zotero_pdf.py <attachment_key>")
sys.exit(1)
attachment_key = sys.argv[1]
pdf_path = get_pdf(attachment_key)
if pdf_path:
print(pdf_path)
sys.exit(0)
else:
print("Error: Could not find or download PDF", file=sys.stderr)
sys.exit(1)
Related skills
FAQ
What does zotero-paper-reader extract from papers?
zotero-paper-reader automatically extracts structured insights, narrative summaries, and citations from academic papers stored in Zotero. Developers use the output for faster literature review inside coding agents.
Does zotero-paper-reader require papers to already be in Zotero?
zotero-paper-reader assumes academic papers are already imported into a Zotero library. The skill focuses on reading and summarizing stored items rather than configuring the reference manager from scratch.