Skip to main content
Glama
paperboy

paperboy

paperboy

CI License: MIT paperboy MCP server

Claude finding three papers, filing them in Zotero, and sending two to a Kindle

An MCP server that delivers research papers and books to your e-reader, with Zotero as the source of truth. Ask Claude for a reading list, then say "queue them and send to my Kindle." (MCP is the plugin protocol Claude uses: this program runs on your machine or your cloud project, and Claude calls its tools during conversation.) Works locally in Claude Code and Claude Desktop; a Cloud Run deployment adds claude.ai and the Claude mobile app, so papers can be sent from a phone (docs/deploy.md).

Two things are kept separate: delivering a paper to your device and cataloguing a work in Zotero. You can do either. A book you own, a paywalled reference, or a PDF you already have can all be tracked in the library without being staged for the e-reader.

Zotero itself is optional: without it you can still search and send papers one-off. The reading queue, collections, and duplicate protection across sessions need it.

How it works

You ask Claude for papers; it uses paperboy's tools to find, queue, and deliver them. What that looks like in practice:

  • Papers you queue land in a Reading Queue collection in Zotero, created on demand.

  • Once a paper reaches your device, paperboy tags it sent-to-ereader in Zotero, so it skips papers already sent to your e-reader, even in a later conversation. (Without Zotero there's no memory between sends, so this protection needs it.)

  • Claude can file papers into your topical collections too. It proposes one from the paper's topic and your existing collection names, and asks you when the fit is unclear. A paper can sit in several collections at once, so filing never disturbs the queue.

  • Papers are found by arXiv id, DOI, or title (a close-enough title match, so a reading list Claude wrote in the chat can be sent as-is). When a title matches only loosely, paperboy offers the closest candidate to confirm rather than guessing or failing silently.

  • Books work too: add_book resolves an ISBN, a book DOI, or a title into a proper Zotero book item (publisher, edition, ISBN, pages). Books are catalogued, not delivered.

  • Have the PDF already (a working paper, lecture notes, an open-access textbook)? attach_pdf files it in Zotero with the PDF attached, using metadata you give it, and can send it to the e-reader in the same step.

  • When a reference can't be resolved, the receipt says why and which tool fits, rather than sending you hunting for a better URL. A paper with no open-access PDF is a normal library record, not an error.

Zotero holds all of this, so the server keeps no state of its own: no database to run, and safe to redeploy at any time.

Delivery backends

Backend

Devices

How

email (default)

Kindle, PocketBook, anything with an email intake

SMTP to the device address. Kindle constraints enforced: 25 attachments / 50 MB per email; sender must be on the Approved Personal Document E-mail List

dropbox

Kobo (native Dropbox sync on the device)

Uploads via the Dropbox API. Kobo only syncs Apps/Rakuten Kobo/, so use a Full Dropbox-scoped app with DROPBOX_FOLDER="/Apps/Rakuten Kobo"; an App-folder-scoped app can't reach it

Related MCP server: Academic Paper Search MCP Server

Tools

Tool

What it does

search_papers

Search OpenAlex (general) or arXiv (source="arxiv"); results carry a ref and an open_access_pdf flag

recommend_papers

Discover related or new papers: citation-graph recommendations (Semantic Scholar) seeded from your Zotero library, plus keyword discovery from interests Claude distills out of the conversation. Excludes papers you already have

send_papers

One-off send by arXiv id, DOI, URL, or title (also records in Zotero if configured)

queue_papers

Add papers to the Zotero Reading Queue without sending (optionally filed into topical collections)

add_to_library

Track papers in Zotero without queueing or sending them (owned, paywalled, or read-later); a missing OA PDF is not treated as a failure

add_book

Catalogue a book by ISBN, book DOI, or title as a Zotero book item (Crossref, Open Library, Google Books); never delivered

attach_pdf

Ingest a PDF you already have (grey literature, open-access textbooks) with the PDF attached and metadata you supply; optionally send it

list_collections

List Zotero collections so Claude can propose where to file a paper, or ask you

file_papers

File queued papers into a topical collection (created on demand; queue membership unaffected)

unfile_papers

Remove papers from one collection (for misfiled items); the papers themselves and their queue/sent state are untouched

list_queue

Show the queue with per-item status (unsent / sent / no-open-access-pdf)

remove_from_queue

Delete queue items by exact ref or title

send_queue

Send every unsent queue item (auto-split under email limits), then tag as sent

setup_status

Report what's configured and what's missing (no secrets) so Claude can guide setup

Setup

Run the interactive wizard. It asks which e-reader you have, walks through only the credentials that device needs, and validates each one as you enter it: SMTP login test, Zotero key check with automatic library ID lookup, full Dropbox OAuth exchange.

uv sync && uv run paperboy setup

docs/setup.md is the step-by-step version: where to find each credential in Zotero, Amazon, Gmail, and Dropbox, with links to the official page for every step.

How much setup you need depends on the device:

You have

Credentials needed

Kindle

2 — Send-to-Kindle address + an SMTP app password

PocketBook

2 — Send-to-PocketBook address + an SMTP app password

Kobo

a Dropbox app (key/secret + one OAuth approval) + a contact email

+ Zotero queue (optional)

1 — a Zotero API key (library ID auto-detected)

If you'd rather set up by hand, cp .env.example .env and fill it in; every variable is documented there. Then register with Claude Code:

claude mcp add paperboy -- uv run --directory /path/to/paperboy paperboy

--directory matters: the server loads .env from its working directory (set PAPERBOY_ENV=/path/to/.env to point elsewhere).

If paperboy is added but half-configured, ask Claude to "check my paperboy setup". The setup_status tool reports what's missing and what to do next, without passing secrets through the chat.

Remote use

You were given a URL and a token

If someone shared their deployment with you, this is your whole setup:

claude mcp add --transport http paperboy <URL>/mcp \
  --header "Authorization: Bearer <token>"

The URL needs the /mcp path suffix. Treat the token like a password: it lets you act fully as the owner: send email from their address, deliver to their e-reader, and read and edit their Zotero library. There is no reduced-permission mode; if that's not what you both want, deploy your own instance.

Deploy your own

One script creates a locked-down, single-tenant Cloud Run service. You need the gcloud CLI and a Google Cloud account with billing enabled, so a card has to be on file. What you pay depends on how much you use it: a personal deployment sending papers now and then usually falls inside Cloud Run's free tier and costs nothing, but heavy or shared use can go past it. The script sets a $1/month budget alert so you hear about it if your bill ever starts to climb.

uv run paperboy setup && ./deploy/deploy.sh my-paperboy-project

How a client connects to the deployed server depends on the client:

  • Claude Code and the API use a bearer token the script generates. You paste it into an Authorization header, as in the section above.

  • claude.ai and the Claude mobile app usually can't send a bearer token from their connector dialog, so they sign in with Google instead. That needs a one-time Google OAuth client, which you create in your own Cloud project (a Web-application client with one redirect URI). Sign-in is restricted to your own email address, and because paperboy only asks Google for your email, the sign-in doesn't expire.

docs/deploy.md has the full procedure for both, including the OAuth console steps, the security model, and cost bounds. The deploy script also prints the exact OAuth steps with your project's URLs already filled in, so you're not copying them from here.

Development & contributing

uv for packaging, ruff (Google style), ty, pytest behind an enforced 80% coverage gate. uv sync && uv run pre-commit install, then uv run pytest. The suite runs entirely offline; no credentials needed to develop.

Contributions are welcome. CONTRIBUTING.md covers setup, testing conventions, and what makes a change easy to merge; SECURITY.md covers how to report vulnerabilities (privately, please).

Roadmap

  • reMarkable delivery backend (real cloud API)

  • arXiv HTML → EPUB via pandoc for reflowable reading (opt-in per paper; conversion is lossy for dense math, so PDF stays the default)

  • Kindle highlights → Zotero notes round-trip (My Clippings.txt parser with fuzzy title matching)

Prior art & acknowledgments

Ideas paperboy builds on: the tag-driven Zotero→Kindle idea from stakats/zotero-to-kindle (circa 2011, by one of Zotero's original directors); wahiggins3/send-to-kindle-mcp; openags/paper-search-mcp; and 54yyyu/zotero-mcp, the model for our setup wizard; paperboy leaves library management to it.

Thank you to arXiv for use of its open access interoperability. Paper metadata and open-access links come from OpenAlex, Crossref, and Unpaywall, all run as open scholarly infrastructure. Recommendations via the Semantic Scholar Recommendations API (Allen Institute for AI). Library management via the Zotero web API. Built on FastMCP, pyzotero, and httpx.

paperboy was built with Claude Code.

Available Tools

14 tools
add_bookA

Catalogue a book in Zotero by ISBN, book DOI, or title.

Resolves an ISBN-10/13, a book DOI, or a title (Crossref, then Open Library, then Google Books) into a proper Zotero book item with publisher, edition, ISBN, series, and page count — never a journalArticle, which would corrupt citations. Books are catalogued, NOT queued or delivered: to put an open-access textbook PDF on the e-reader, use attach_pdf with item_type="book". Pass collections to file it topically (created on demand). A title that matches only loosely returns the closest candidate for you to confirm rather than guessing. Deduplicates by ISBN.

Inputs: identifier (required), collections. Returns: result

add_to_libraryA

Track papers in Zotero WITHOUT staging them for e-reader delivery.

The "just catalogue it" path, distinct from queue_papers: resolves each ref (arXiv id, DOI, URL, or title) and files it into the library (and any collections, created on demand) but never adds it to the Reading Queue and never sends it. A paper with no open-access PDF is catalogued normally here — it is a library record, not a delivery failure, so nothing is tagged no-oa-pdf. Use this for references you want to keep (owned, paywalled, or read-later) rather than deliver. Duplicates report the collections they already live in. Relay any unresolvable refs to the user.

Inputs: refs (required), collections. Returns: result

attach_pdfA

Ingest a PDF you already have into Zotero, with the PDF attached.

For grey literature and works no registry indexes (author-circulated preprints, lecture notes, working papers, open-access textbooks). Downloads the PDF from an http(s) URL (or reads a local path when running locally), creates a Zotero item of item_type from the metadata YOU supply, and attaches the PDF to it — no registry lookup, so a title/authors the model already knows is enough. item_type is one of journalArticle, book, bookSection, report, thesis, preprint, conferencePaper, manuscript, document (default journalArticle); use 'book' for an open-access textbook PDF. Files into collections (created on demand) and, unlike queue_papers, does NOT queue. With send=True it also delivers the PDF to your e-reader in the same call. Requires Zotero.

Inputs: title (required), authors (required), url_or_path (required), doi, +4 more. Returns: result

file_papersA

File already-queued papers into a Zotero collection.

Only papers ALREADY in the Reading Queue can be filed — to file a fresh paper, queue_papers it first, or pass collections=[...] to queue_papers to queue and file in one step. The collection is created on demand, but only if at least one paper matches (a call that files nothing leaves no empty collection behind). Items stay in the queue — Zotero items can live in many collections — so delivery state is unaffected. Refs match like remove_from_queue: exact arXiv id, DOI, URL, or title.

Inputs: refs (required), collection (required). Returns: result

list_collectionsA

List the user's Zotero collections (name, item count, parent).

Check this before queueing or sending new papers: if a topical collection clearly fits the paper, pass it via collections=[...]; if several could fit or none do, ask the user where to file — never guess silently. Naming a new collection in other tools creates it on demand.

Inputs: no named parameters. Returns: result

list_queueA

List the Zotero Reading Queue with delivery status per item.

Status is 'unsent', 'sent', or 'no-open-access-pdf'. Use this to show the user their queue, before send_queue (which flushes every unsent item), or to find refs for remove_from_queue. Each entry also carries key, the Zotero item key — the one id that stays unique when the queue holds duplicate entries of the same paper; every ref-taking tool accepts it. added dates are UTC.

Inputs: no named parameters. Returns: result

queue_papersA

Add papers to the Zotero Reading Queue without sending them.

Accepts arXiv ids, DOIs, URLs, or paper titles. Papers already in the queue are reported as such, not re-added. collections optionally files the papers into topical Zotero collections (created on demand) as well — check list_collections and ask the user when placement is unclear. Unresolvable papers are reported back — relay those to the user.

Inputs: refs (required), collections. Returns: result

recommend_papersA

Discover papers the user may want to read — old or new.

Blends two signals: citation-graph recommendations (Semantic Scholar) seeded from seed_refs, or — by default — the user's Zotero library (what they queue and read IS their interest profile); and keyword discovery (OpenAlex) from interests — pass 2-4 short phrases distilled from the current conversation. recent_only=True favors newly published work; False searches the all-time pool (computer science only, an upstream limit). Papers already in the user's library are excluded (the queue plus the 100 most recently added items). max_results is capped at 20.

Returns {"picks": [...], "problems": [...]}: picks carry refs for send_papers / queue_papers, and each pick has a via field saying why it appeared — 'interest-keyword' (matched a stated interest), 'related-to-seeds' (citation graph of explicit seeds), or 'related-to-library' (citation graph of the OWNER'S Zotero library; can look off-topic to anyone else). When interests are given they lead the results. problems reports any discovery arm that failed or seed that didn't resolve — ALWAYS relay problems, or a stated interest may silently go uncovered. Present picks and let the user choose; don't send unasked.

Inputs: interests, seed_refs, max_results, recent_only. Returns: object

remove_from_queueA

Remove papers from the Zotero Reading Queue by ref or title.

Matches each ref (arXiv id, DOI, URL, or exact title) against queue items. Items filed into other collections keep their library record (and sent-state) and only leave the queue; items that live nowhere else are moved to Zotero's Trash (restorable in the Zotero app for ~30 days). Trashed items no longer count for duplicate protection — sending those again later will deliver them again. Nothing is ever deleted from the e-reader, and nothing is ever permanently deleted from Zotero.

A ref that matches more than one queue item (duplicate titles or a duplicated record) removes nothing; the receipt lists each candidate's Zotero item key, which is accepted as a ref and is unique even for exact duplicates. Relay the choice to the user — never pick for them.

Inputs: refs (required). Returns: result

search_papersA

Search for papers across the scholarly literature.

source is 'all' (OpenAlex: journals, conferences, and preprint servers including arXiv — broad coverage, but ranking can miss on arXiv-native topics) or 'arxiv' (arXiv's own search — better for recent preprints or when 'all' returns off-topic results); unknown values fall back to 'all'. max_results is clamped to the 1-25 range. Each result has a ref (arXiv id, DOI, or exact title) to pass to send_papers / queue_papers. open_access_pdf means an OA PDF link was found; delivery can still fail if the link is dead (arXiv-hosted papers are the most reliable).

Inputs: query (required), source, max_results. Returns: result

send_papersA

Send papers to the e-reader by arXiv id, DOI, or title.

refs accepts arXiv ids ('2401.12345', 'arXiv:...'), arXiv abs/pdf URLs, DOIs, doi.org URLs, and paper titles — the same ref values search and recommendation results carry. Publisher landing-page URLs are NOT resolvable — use the DOI or title. Version suffixes ('2401.12345v2') are ignored; the latest arXiv version is delivered. collections optionally files the papers into topical Zotero collections (created on demand) in addition to the Reading Queue — check list_collections and ask the user when placement is unclear.

Refs are deduplicated, and papers already tagged as sent in Zotero are skipped unless force=True. Large batches are split automatically to fit the 25-attachment / 50 MB per-email limits. dry_run=True previews what would be sent, with estimated sizes, without downloading or delivering — use it before big sends.

Papers without an open-access PDF are not sent; if Zotero is configured they are still queued (tagged no-oa-pdf) so they can be delivered manually later. Without Zotero there is NO cross-call duplicate protection — re-sending the same ref ships another copy. Relay the full receipt — sizes, skips, and failures — to the user.

Inputs: refs (required), force, dry_run, collections. Returns: result

send_queueA

Send EVERY unsent paper in the Zotero Reading Queue.

This flushes the whole queue — for specific papers use send_papers. Items tagged sent or no-oa-pdf are skipped; items whose PDF turns out to be unavailable are tagged no-oa-pdf so they are not retried forever. Batches are split under the email limits automatically. Check list_queue first when unsure what will go.

Inputs: no named parameters. Returns: result

setup_statusA

Report which paperboy features are configured and what's missing.

Returns configuration state only — never secret values. Use this to guide the user through finishing setup. Credentials themselves must be entered by running 'paperboy setup' in a terminal, never pasted into the chat.

Inputs: no named parameters. Returns: object

unfile_papersA

Remove papers from one Zotero collection, and nothing else.

The inverse of file_papers, for misfiled items: membership in the named collection is dropped, while the item, its other collections (including the Reading Queue), and its sent-state are untouched. The Reading Queue itself is refused as a target — leaving the queue is remove_from_queue's job, with its keep-or-trash safeguards. To move a paper between collections, file it into the new one and unfile it from the old. Refs match like remove_from_queue: exact arXiv id, DOI, URL, or title, against the collection's items; an ambiguous ref (matching several items) removes nothing.

Inputs: refs (required), collection (required). Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.5/5.0

    Scored across 14 tools

    Disambiguation5/5

    Each tool targets a distinct workflow step: search, recommend, queue, send, catalogue, attach PDF, manage collections, and check status. Overlapping actions like send_papers vs send_queue or add_to_library vs queue_papers are explicitly differentiated in their descriptions.

    Naming Consistency4/5

    Most tool names follow a clear verb_noun snake_case pattern (search_papers, send_papers, queue_papers, list_collections). Minor deviations like remove_from_queue and setup_status break the simple two-word pattern but remain stylistically consistent.

    Tool Count5/5

    14 tools is well-scoped for a scholarly literature manager spanning discovery, queueing, delivery, cataloguing, and collection organization. Each tool serves a clear purpose with no obvious redundancy or bloat.

    Completeness5/5

    The toolset covers the full workflow lifecycle: find papers, add to queue, send to e-reader, catalogue items, organize collections, and inspect setup state. Only deep library editing operations like metadata updates or permanent deletion are absent, which is reasonable for this domain.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      A
      maintenance
      The ArXiv MCP Server bridges the gap between AI models and academic research by providing a sophisticated interface to arXiv's extensive research repository. This server enables AI assistants to perform precise paper searches and access full paper content, enhancing their ability to engage with scientific literature.
      19
      10,735 PyPI
      3,258
      Apache 2.0
    • A
      license
      C
      quality
      D
      maintenance
      Enables real-time search and retrieval of academic paper information from multiple sources, providing access to paper metadata, abstracts, and full-text content when available, with structured data responses for integration with AI models that support tool/function calling.
      3
      119
      AGPL 3.0
    • A
      license
      A
      quality
      B
      maintenance
      Connects your Zotero research library with Claude and other AI assistants via the Model Context Protocol, allowing you to search your library, access content, discuss papers, get summaries, and analyze citations.
      35
      8,531 PyPI
      5,884
      MIT