Add Zotero integration (import, sync, add) - #124
Conversation
Add a new importer for Zotero's CSL-JSON export format, enabling `bip import --format zotero`. Includes PDF path resolution from Zotero's local SQLite database via --zotero-db flag. New files: - internal/importer/zotero.go — CSL-JSON parser (ParseZotero) - internal/importer/zotero_pdf.go — PDF path resolution from Zotero DB - internal/importer/zotero_test.go — 12 test cases - testdata/zotero_csl_sample.json — test fixture with 5 entries Modified: - cmd/bip/import.go — format dispatch, --zotero-db flag - skills/bip.lit.import/SKILL.md — Zotero workflow docs - skills/bip.lit/SKILL.md — Zotero section and quick reference
Add bidirectional Zotero integration via the Web API v3: - `bip zotero sync` — pull all items from Zotero into bip - `bip zotero add <DOI>` — add paper to both bip and Zotero via S2 New package internal/zotero/ with: - Rate-limited HTTP client with Backoff header support - Paginated item fetching with incremental sync support - Bidirectional mapper (Reference <-> ZoteroItemData) - PMID/PMCID/arXiv extraction from Zotero Extra field Config: zotero_api_key and zotero_user_id in ~/.config/bip/config.yml
Use processImports() instead of manual classification to avoid index-out-of-range when persisting to an empty refs file. The previous code grew existingRefs during classification but then re-read the (empty) persisted file for the write step.
- Sync now skips unchanged refs (was rewriting all 100+ on every sync) - bip zotero add falls back to CrossRef when S2 is rate-limited - Fix Zotero API item creation (omitempty on key/creator fields) - Add 14 unit tests for zotero package (mapper, errors, date parsing) - Add CrossRef DOI resolver (free, no API key needed)
|
Very nice! Zotero has an MCP, is this better than just using that? |
- Run gofmt on all new files (fixes CI lint failure) - Replace custom ExitZotero* constants with canonical exit codes (ExitConfigError, ExitError, ExitDataError) from exitcodes.go - Add duplicate check by ID in addition to DOI in zotero add - Unescape HTML entities in CrossRef abstracts (html.UnescapeString) - Increase rate limiter burst from 1 to 3 for smoother request flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
🤖 Pushed a fixup commit (94160b9) that addresses:
All tests pass ( |
- Add empty-page guard to GetItems and GetItemsSince pagination loops to prevent infinite looping if the API returns 0 items - Handle crypto/rand.Read error in generateWriteToken (panic on failure since it indicates a broken OS entropy source) - Use strings.Index instead of strings.LastIndex for extracting Zotero item keys from URLs (semantically correct, same behavior in practice)
|
Thanks! I found since this is an interaction of the |
|
I guess I'm just asking, if you're using Zotero, why would you need to use our tool at all and why not just use their MCP using your agent? |
|
Good point. I assumed that using the I am really just starting to understand this project would totally understand if this PR does not make much sense! |
Brings in Zotero CSL-JSON import, Web API sync, and bidirectional add commands. Includes P1 fixes (pagination guard, rand.Read error handling) from PR matsen#124 review.
|
Well, I would love it if you would try this out and show that Zotero works using this package. I was just curious. |
Zotero MCP Server as an Alternative to bip's Paper ManagementWe tested the 54yyyu/zotero-mcp server (~1.6k stars, actively maintained) as a direct MCP integration for Zotero, bypassing bip entirely for paper lookups. The server connects via Zotero's Web API and exposes ~35 tools: search, metadata retrieval, annotation extraction, collection management, tagging, citation export, semantic search, and even retraction checking via scite.ai. What Would Need to ChangeSkills (high effort):
CLAUDE.md instructions:
CLI commands (medium effort):
Pros of Zotero MCP
Cons of Zotero MCP
RecommendationThe Zotero MCP server is a valuable complement to bip, not a replacement. The sweet spot is:
The Zotero MCP's annotation extraction is the most compelling unique capability. Being able to ask "what did I highlight in this paper?" directly in a Claude conversation is genuinely useful and not something bip provides today. |
|
Thanks, Dominik! I encourage you to do with this PR what you like-- I would love it if you incorporated anything that you actually use so that it's dog-fooded. I can't do this work because I don't use Zotero. |
Summary
Adds full Zotero support to bip. Zotero is a free, open-source reference manager widely used in academia — the open-source counterpart to Paperpile. It stores papers locally with optional cloud sync, provides browser-based paper capture, and exposes both a Web API and a local SQLite database for programmatic access.
Three access methods
bip import --format zotero export.json— import from Zotero's CSL-JSON export format, with optional--zotero-dbflag for PDF path resolution from Zotero's local SQLite databasebip zotero sync— pull all items from Zotero's Web API into bip, with skip-if-unchanged deduplicationbip zotero add DOI:10.xxx— add paper to both bip and Zotero simultaneously; fetches metadata from S2 with automatic CrossRef fallback when rate-limitedHow this differs from the Paperpile integration
The Paperpile and Zotero backends take different approaches due to the different capabilities each tool exposes.
Paperpile embeds PDF attachment paths directly in its JSON export, so
bip import --format paperpilegets both metadata and file locations in one step. However, this requires a manual export-download-import cycle each time the library changes.Zotero's CSL-JSON export does not include file paths, since Zotero manages PDFs in its own storage directory (
~/Zotero/storage/<key>/<file>.pdf). To bridge this gap, the--zotero-dbflag reads Zotero's local SQLite database to resolve PDF paths as a separate enrichment step after parsing.Beyond the file import, Zotero's Web API enables live sync (
bip zotero sync) and bidirectional writes (bip zotero add), which aren't available in the current Paperpile integration because it relies on file exports rather than an API. The same API-based approach could be added for Paperpile in the future.Both importers produce identical
Referencestructs and feed into the same deduplication pipeline — onlySource.Type("paperpile"vs"zotero") differs. A mixed library with papers from both sources works without issues.New packages/files
internal/importer/zotero.go— CSL-JSON parser (ParseZotero), followsParsePaperpilepattern exactlyinternal/importer/zotero_pdf.go— PDF path resolution from Zotero's SQLite DB (~/Zotero/zotero.sqlite)internal/zotero/— Web API v3 client (rate-limited, paginated), bidirectional mapper (Reference↔ZoteroItemData), CrossRef DOI resolver, error typestestdata/zotero_csl_sample.json— 5-entry test fixtureModified
cmd/bip/import.go—--format zoterodispatch +--zotero-dbflaginternal/config/global.go—zotero_api_key,zotero_user_idconfig fieldsskills/bip.lit.import/SKILL.md,skills/bip.lit/SKILL.md— Zotero workflow docsConfig
For Zotero users, set
pdf_root: ~/Zotero/storagein.bipartite/config.ymlsince Zotero stores PDFs as~/Zotero/storage/<8-char-key>/<filename>.pdf.Test plan
go test ./...— all 28 packages passgo vet ./...— cleanbip zotero add DOI:...— added to both bip and Zotero via CrossRef fallback