Metadata-Version: 2.4
Name: researchshelf
Version: 0.10.0
Summary: Self-hosted, server-first library for research papers — team libraries, shared annotations, discovery inboxes, audiobook narration, LaTeX writing, and agent access.
Project-URL: Homepage, https://github.com/aryan-pan/researchshelf
Project-URL: Documentation, https://github.com/aryan-pan/researchshelf/blob/main/docs/SPEC.md
Project-URL: Changelog, https://github.com/aryan-pan/researchshelf/releases
License: AGPL-3.0-only
License-File: LICENSE
Keywords: latex,library,papers,pdf,reference-manager,research,self-hosted,zotero
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Requires-Dist: email-validator>=2.0
Requires-Dist: fastapi>=0.111
Requires-Dist: httpx>=0.27
Requires-Dist: pypdf>=4.0
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: uvicorn[standard]>=0.30
Provides-Extra: dev
Requires-Dist: mcp>=1.2; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: tts
Requires-Dist: kokoro>=0.9; extra == 'tts'
Requires-Dist: numpy; extra == 'tts'
Description-Content-Type: text/markdown

# ResearchShelf

Self-hosted, server-first library for research papers — Audiobookshelf/Plex
for PDFs, Zotero-adjacent but built for your own hardware. Point it at a
folder of papers and it indexes, enriches, and serves them to your whole
team, in any browser, on any device.

**Why not just Zotero?** Zotero is a great desktop client, but shared group
libraries route through zotero.org (storage quotas, your files on their
servers), and its sync server isn't practically self-hostable. ResearchShelf
is the opposite shape: one server you own is the source of truth, browsers
are the clients, and a shared lab library with per-user roles is a core
primitive — not an add-on.

![Home — continue reading, projects, libraries](https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/home-desktop.png)

## Features

**Library**
- Team libraries with `viewer`/`editor`/`owner` roles per member; the
  filesystem stays the source of truth. Folders are managed for you inside
  the data directory, or point at any folder on the server (with a
  create-it-if-missing prompt); both editable later
- Scans and imports run as **background jobs** — navigate away, come back,
  the progress banner picks up where it was
- Folder scanning: sha256 dedup, title/author from PDF metadata, DOI + arXiv
  id detection, optional Crossref enrichment
- **Upload PDFs from the browser** (button or drag-and-drop) — they land in
  the library folder and run through the same metadata pipeline as a scan
- **Fetch by identifier**: paste a DOI, arXiv id, or link and ResearchShelf
  pulls the metadata (arXiv/Crossref, with optional Elsevier/Springer/IEEE
  API-key fallbacks) and downloads the PDF from arXiv or the paper's
  open-access location (via Unpaywall). Paywalled papers can be kept as
  **metadata-only entries** — cite them, tag them, note them — and the PDF
  attached later with one click
- **Attachments on entries**, Zotero-style: supplements, slides, data, code
  archives — any file type, per paper
- **Zotero import**: point it at your Zotero data directory, or upload a zip
  of it from the browser — no export step. PDFs, metadata, your Zotero
  notes, and the collection hierarchy all come along, and collections are
  filterable in the library view
- **Full-text search inside the PDFs** (SQLite FTS5) with match snippets —
  "which paper mentioned X?" is one search box away, alongside metadata search
- **Saved searches**: keep any query + filter combination as a named chip
  and jump back to it in one click
- **Tags** with one-click filtering (imported from Zotero too)
- **Collections** you can create, rename, nest, and delete — add papers
  from their pages, filter the library by them (Zotero imports arrive as
  collections too)
- **Duplicate detection & merge**: entries sharing a DOI, arXiv id, or
  title (the preprint-vs-published case) are found and merged — notes,
  highlights, tags, collections, and reading progress move to the kept
  entry, and a distinct PDF survives as an attachment
- **Library insights**: papers-per-year chart, top venues and authors, and
  your reading progress at a glance
- Search plus triage-first filters (Inbox / Reading / Read)
- **Citation graph** of the whole library: references fetched from Semantic
  Scholar, extracted from the PDFs themselves by an optional **GROBID**
  sidecar (offline, covers papers S2 doesn't know), or supplied by agents;
  edges resolved by DOI/arXiv match, nodes colored by your reading status
  and sized by in-library citations
- Team activity feed: papers added, who started or finished what, and new
  highlights and comments

![Library with insights, tags, and full-text search](https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/library-insights.png)

**Reading & notes**
- In-browser PDF reader (pdf.js) with selectable text and reading-progress
  sync — start on the desktop, continue on your phone from the same page
- **Listen to papers like audiobooks**: one click narrates a paper with
  text-to-speech (bibliography skipped) and embeds a player on its page —
  playback speed control, and it picks up where you left off. In the
  reader, **click any sentence to listen from that point** (or select text
  → "🎧 From here"). Voices are the good kind: Kokoro running locally on
  your server (open weights, nothing leaves your machine), or OpenAI /
  ElevenLabs via API key
- **Download the highlighted PDF**: everyone's highlights embed as
  standard PDF annotations (comments in the popup notes) — they open in
  Acrobat, Zotero, or any tablet reader
- **Group highlights & comments, Google-Docs style**: select text to
  highlight it (four colors) or start a comment thread; everyone in the
  library sees them, replies in-line, and clicks a thread to jump to the
  passage. Threads update **live** while the reader is open — a teammate's
  highlight or reply appears within seconds, no reload
- Per-paper private Markdown notes that export as one literature-review
  document per library — your highlights (with page numbers and thread
  comments) are woven in next to each paper's notes
- **Connections panel** on every paper: what it cites and what cites it,
  within your library
- One-click `\cite{key}` and BibTeX copy per paper; always-current
  library exports in **BibTeX, CSL-JSON, and RIS** — pandoc/citeproc,
  Zotero, and EndNote all import them

**Writing — LaTeX in the browser**
- **Manuscripts**: an Overleaf-style editor with syntax highlighting,
  compile-on-demand (Ctrl/⌘-Enter), and a live PDF preview beside the text
- **Cite from your shelf**: the ❝ button searches your libraries and
  inserts `\cite{citekey}`; `references.bib` is generated from your
  libraries at compile time, so citations just resolve — no .bib wrangling
- **Project files & packages, Overleaf-style**: upload figures, custom
  `.sty`/`.cls`, extra `.tex` includes, or your own `references.bib` (it
  replaces the generated one); with tectonic as the engine, any LaTeX
  package is fetched automatically, and missing packages are reported by
  name with install guidance
- Compiles run server-side on tectonic, latexmk, or pdflatex — whichever
  the server has (a friendly message tells the admin if none is installed)

**Projects — workspaces with a discovery inbox**
- A project holds a written brief, keywords, and papers pulled from any of
  your libraries
- Its inbox fills with newly published matches for the keywords from **two
  sources — arXiv and OpenAlex** (which covers journals and conferences
  beyond preprints), refreshed automatically when you open the project —
  accept or dismiss like email
- **Subscribe the inbox to RSS/Atom feeds** — journal alerts, conference
  pages, lab blogs — and their new entries arrive alongside keyword matches
- A per-project **citation graph** shows how the collected papers relate to
  each other
- Agent-ready: `GET /api/projects/{id}/brief` renders the whole project as
  Markdown for an LLM, and agents post suggestions back idempotently

![Shared highlights and comment threads in the reader](https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/reader-annotations.png)

![Library citation graph](https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/library-graph-desktop.png)

![Project inbox with suggestions](https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/project-inbox-desktop.png)

<p>
  <img src="https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/library-mobile.png" width="32%" alt="Library on a phone" />
  <img src="https://raw.githubusercontent.com/aryan-pan/researchshelf/main/docs/screenshots/paper-mobile.png" width="32%" alt="Paper detail with notes on a phone" />
</p>

See [docs/SPEC.md](docs/SPEC.md) for the architecture, multi-user model,
agent contract, and roadmap (Zotero-connector capture, GROBID, citation
graph, semantic search, MCP server).

## Quickstart

Runs on Linux, macOS, and Windows (Python 3.11+); CI tests all three.
`RESEARCHSHELF_HOST` / `RESEARCHSHELF_PORT` / `RESEARCHSHELF_DATA_DIR`
override the defaults (`0.0.0.0`, `8340`, `./data`).

### Install from PyPI

The web UI is bundled in the package — no Node needed:

```sh
uv tool install researchshelf     # or: pip install researchshelf / pipx install researchshelf
researchshelf
# open http://localhost:8340 and create the admin account
```

Optional extras: `researchshelf[mcp]` (agent MCP server),
`researchshelf[tts]` (local Kokoro narration). Wheels are also attached to
each [GitHub release](../../releases/latest).

### Docker

```sh
docker compose up --build
```

Mount your paper folders into the container (see `docker-compose.yml`) and
reference the mounted path when creating a library.

### From source

```sh
# backend (Windows: .venv\Scripts\researchshelf and .venv\Scripts\pytest)
uv venv && uv pip install -e ".[dev]"
.venv/bin/researchshelf            # serves on :8340
.venv/bin/pytest                   # run the test suite

# frontend (once, or after UI changes — output is served by the backend)
cd web && npm install && npm run build

# frontend dev server with hot reload, proxying /api to :8340
cd web && npm run dev
```

**Administration & customization** — all from the UI at `/settings`
- The first account is the root admin; manage users (create, promote,
  password reset) without touching the server
- **API keys** for agents and integrations: named, shown once, revocable,
  with ready-to-copy snippets for the Zotero connector relay and LLM agent
  access
- **Make it yours**: theme (auto/light/dark), five accent colors, compact
  density, default library view — saved to your account, applied everywhere
- **Server settings**: rename the server (full rebrand of header and
  sign-in), open or close self-registration, toggle external calls
  (Crossref, arXiv, OpenAlex) on or off, and set a contact email — sent with
  OpenAlex/Unpaywall requests to get your server into their faster
  "polite" pools
- **Integrations**: point at a GROBID instance for offline reference
  extraction (`docker compose --profile grobid up`), add publisher API
  keys (Elsevier, Springer Nature, IEEE Xplore) as metadata fallbacks for
  fetch-by-DOI, and pick a **text-to-speech provider** for paper narration
  — Kokoro locally (`pip install "researchshelf[tts]"` + `espeak-ng`), or
  OpenAI / ElevenLabs by API key
- **Transparency**: server info (data directory, database size, contents)
  and a live history of background jobs, right in settings

## Capturing papers with the Zotero browser extension

The official [Zotero Connector](https://www.zotero.org/download/connectors)
extension can save straight into ResearchShelf. Run the relay on the machine
where your browser runs (it speaks the connector's local protocol on port
23119 and forwards captures to your server):

```sh
researchshelf-connector --server http://your-server:8340 \
  --token $TOKEN --library 1
```

`--token` takes a ResearchShelf API key (create one in Settings → API
keys); the browser extension itself needs no configuration — it finds the
relay on your machine automatically.

Click the Zotero button on a publisher or arXiv page: the PDF plus the
translator's clean metadata (title, authors, venue, DOI, tags) land in your
library, and the page snapshot is kept as an attachment on the entry. Quit Zotero itself first — both listen on the same port. Captures
without a PDF become metadata-only entries you can attach the file to later.

**A note on publisher access.** ResearchShelf only downloads PDFs from
legal open-access sources (arXiv and Unpaywall's indexed OA locations).
For paywalled papers your institutional access already covers, the
connector is the supported route: your browser is logged in, the publisher
serves you the PDF, and the connector files it — no credentials ever touch
the server. Publisher APIs (Elsevier, IEEE, Springer) are supported as
metadata/abstract sources via API keys in server settings — they never
serve subscription PDFs, by the publishers' design. ResearchGate has no
public API, and account-credential scraping is against its terms, so it
isn't supported.

## Letting an agent work a project

The **MCP server** turns your library into native tools for Claude (or any
MCP-capable agent): searching papers (including full text), reading project
briefs, posting suggestions into project inboxes, adding papers by DOI, and
reading/writing your notes. Install it where the agent runs and point it at
your server with an API key from Settings:

```sh
pip install "researchshelf[mcp]"
claude mcp add researchshelf -- researchshelf-mcp \
  --server http://your-server:8340 --token $API_KEY
```

Then ask Claude things like *"read project 1's brief and suggest five recent
papers it's missing"* — suggestions land in the project inbox for you to
accept or dismiss, deduplicated by DOI/arXiv id. Every action is scoped to
what the API key's user can already see and do.

Prefer plain HTTP? The same loop works with two curl calls:

```sh
TOKEN=...   # from POST /api/auth/login

# 1. Read the project context (brief, keywords, papers, your notes)
curl -s localhost:8340/api/projects/1/brief -H "authorization: Bearer $TOKEN"

# 2. Post candidate papers back — duplicates by DOI/arXiv id are ignored
curl -s -X POST localhost:8340/api/projects/1/suggestions \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '[{"title":"Reformer: The Efficient Transformer","arxiv_id":"2001.04451",
        "source":"llm","reason":"LSH attention is directly in scope."}]'
```

Suggestions land in the project inbox for you to accept or dismiss.
Interactive API docs live at `/docs`.

## License

[AGPL-3.0](LICENSE): free to use, self-host, modify, and share — and
anyone who runs a modified version as a service must make their changes
available under the same license.
