Skip to content

About

No description, website, or topics provided.

Resources

Contributing

Stars

7 stars

Watchers

0 watching

Forks

Repository files navigation

English | 中文

fastpaper CLI with Skill

A CLI tool that gives AI agents (Claude Code, Codex, Opencode, etc.) the ability to search, download, and read academic papers and scientific literature. Ships with a SKILL that teaches agents how to pick sources and construct commands.

One command, one source, zero configuration. Parallel multi-source search is handled by the agent spawning multiple processes.

Install

CLI

Homebrew (macOS / Linux)

brew install zhangyee/tap/fastpaper

Shell script (macOS / Linux)

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/zhangyee/fastpaper-cli/releases/latest/download/fastpaper-cli-installer.sh | sh

PowerShell (Windows)

powershell -ExecutionPolicy Bypass -c "irm https://github.com/zhangyee/fastpaper-cli/releases/latest/download/fastpaper-cli-installer.ps1 | iex"

Cargo

cargo install fastpaper-cli

Skill

Install the skill so your AI agent knows how to use fastpaper. Uses Vercel Skills, a tool that installs SKILL.md files into agents:

npx skills add zhangyee/fastpaper-cli --skill fastpaper

The SKILL.md teaches the agent how to pick sources by domain and construct commands. Use --format json for structured output. All JSON fields use null for missing values (never omitted), so the schema is stable.

Quick start

# Search arXiv
fastpaper search arxiv "transformer attention mechanism"

# Search with filters -- each source declares which ones it can honour
fastpaper search arxiv "large language model" --after 2024-01-01 --field cs.CL --limit 20

# Fetch a paper by DOI (source auto-detected)
fastpaper get 10.1038/nature12373

# Fetch by arXiv ID
fastpaper get 2301.08745

# Name the source explicitly to control which one answers
fastpaper get openalex 10.1038/nature12373

# Download a PDF into ./papers
fastpaper download arxiv 2301.08745

# ...or let the identifier pick the source
fastpaper download 2301.08745

# Read a PDF you already downloaded
fastpaper read papers/2301.08745.pdf

# Read a specific section of it
fastpaper read papers/2301.08745.pdf --section methods

# JSON output for scripting / AI agents
fastpaper search semantic "CRISPR gene editing" --format json

# Parallel multi-source search
fastpaper search arxiv "protein folding" --format json &
fastpaper search pubmed "protein folding" --format json &
fastpaper search semantic "protein folding" --format json &
wait

Sources

26 academic sources, each accessed independently per command.

read is not listed here: it works on a PDF already on disk, so it applies equally to anything the download column can fetch.

Source Full name search get download cite figures Domain
arxiv arXiv yes yes yes yes Physics, math, CS, statistics, EE, q-bio, q-fin, econ
pubmed PubMed yes yes Biomedical & life sciences (metadata only)
pmc PubMed Central yes yes yes Biomedical & life sciences (full text)
europepmc Europe PMC yes yes yes yes Life sciences superset of PMC; adds preprints, patents, guidelines
semantic Semantic Scholar yes yes yes yes All disciplines, AI-powered citation graph
crossref CrossRef yes yes DOI metadata, all disciplines
openalex OpenAlex yes yes yes Open metadata index, 200M+ works
dblp DBLP yes Computer science
core CORE yes yes yes Open access aggregator (needs CORE_API_KEY)
openaire OpenAIRE yes yes EU open science
doaj DOAJ yes yes Open access journals, all subjects (links go to publisher pages, not PDFs)
unpaywall Unpaywall yes OA link resolver (requires UNPAYWALL_EMAIL)
zenodo Zenodo yes yes yes All disciplines (datasets, software, papers)
hal HAL yes yes yes Multi-disciplinary, French national archive
osf OSF Preprints yes yes yes Social science, psychology, education, law preprints (~32 communities; title-only search)
inspire INSPIRE-HEP yes yes High-energy physics (reliable citation counts; files live on arXiv)
zbmath zbMATH Open yes yes Mathematics (published record + reviews; no abstracts or files)
eric ERIC yes yes yes Education research (files for the ERIC-hosted subset)
osti OSTI.GOV yes yes yes US Dept. of Energy technical reports
ntrs NASA NTRS yes yes yes NASA aerospace reports
datacite DataCite yes yes DOI registry for datasets, software and theses (complements crossref)
huggingface Hugging Face Papers yes yes AI community attention: daily / weekly / monthly lists ranked by upvotes (--top), rolling --trending; ids are arXiv ids
openreview OpenReview yes ML conference submissions with their decision (ICLR, NeurIPS, …); search only
jstage J-STAGE yes yes Japanese society journals (Japanese titles where they exist; no abstracts)
oapen OAPEN Library yes yes yes Peer-reviewed open access books, mostly humanities and social sciences
ads NASA ADS (SciX) yes yes yes yes Astronomy and physics, with a citation graph (requires ADS_API_TOKEN)

figures fetches the authors' original figure files rather than PDF pages — see below. Only arxiv (its e-print source package) and europepmc (its supplementary package) can provide them; a PMC ID or a DOI both route to europepmc.

Sources differ in which search filters they can honour, and in their per-request result caps. fastpaper sources --capabilities prints the full matrix along with each source's caveats.

Commands

Each command does exactly one thing: search finds papers, get reads metadata, download saves a PDF, read extracts text from a PDF on disk.

search -- Search papers

The source is required: a free-text query has no identifier shape to infer one from.

fastpaper search <SOURCE> [QUERY] [OPTIONS]

Options:
  -n, --limit <N>        Max results [default: 10]
      --offset <N>       Skip first N results [default: 0]
      --sort <FIELD>     Sort by: relevance, date, citations
      --order <DIR>      asc or desc [default: desc]
      --author <NAME>    Filter by author
      --after <DATE>     Papers published on or after YYYY-MM-DD
      --before <DATE>    Papers published on or before YYYY-MM-DD
      --year <YEAR>      Papers in a specific year
      --field <FIELD>    Field of study / category (e.g. cs.CL)
      --open-access      Only open access papers
      --patents          Patents only (europepmc)
      --trending         Hugging Face's rolling "hot right now" list instead of a search
      --top <PERIOD>     Hugging Face's list for one period, ranked by upvotes: a day (2026-09-18), an ISO week (2026-W38) or a month (2026-08)
  -f, --format <FMT>     table, json, jsonl, csv, bibtex [default: table]
  -o, --output <PATH>    Write results to file

Filters map onto each source's own API parameters. A source that cannot honour one fails with an error naming the filters it does support, and the sources that do have the one you asked for, rather than dropping it silently — a filter that vanishes yields results that look right and are not. The same goes for -n above a source's per-request cap. Run fastpaper sources --capabilities to see what each source accepts.

get -- Fetch metadata by identifier

One argument is an identifier and the source is inferred from its shape. Two arguments name the source explicitly.

fastpaper get <IDENTIFIER>
fastpaper get <SOURCE> <IDENTIFIER>

Routing: arXiv→arxiv, PMC→pmc, PMID→pubmed, DOI→crossref, S2:→semantic.

Note this differs from download, which sends a DOI to semantic — the two commands look at records from different sources. Crossref registers DOIs and carries the most authoritative metadata but serves no files, so get <DOI> returns pdf_url: null no matter what; download <DOI> resolves an open access copy through Semantic Scholar instead. Do not read pdf_url off get <DOI> to predict whether download will succeed.

download -- Download PDF

Same two forms as get. Saves as <identifier>.pdf.

fastpaper download <IDENTIFIER> [OPTIONS]
fastpaper download <SOURCE> <IDENTIFIER> [OPTIONS]

Options:
  -d, --dir <PATH>       Download directory [default: ./papers]
  -s, --max-size <SIZE>  Largest response body to accept, e.g. 10MiB, 200MB.
                         A bare number is bytes, so `--max-size 10` means ten
                         bytes. 0 means unlimited [default: 100MiB]
      --overwrite        Overwrite an existing file

figures -- Fetch a paper's original figure files

Same two forms as get. Fetches a paper's original figure files — an arXiv source package or a Europe PMC supplementary package — and saves them under a subdirectory named for the identifier you typed (/ flattened to _, same as download).

fastpaper figures <IDENTIFIER> [OPTIONS]
fastpaper figures <SOURCE> <IDENTIFIER> [OPTIONS]

Options:
  -d, --dir <PATH>       Download directory [default: ./papers]
  -s, --max-size <SIZE>  Largest archive to accept, e.g. 10MiB, 200MB.
                         A bare number is bytes. 0 means unlimited [default: 100MiB]
      --overwrite        Overwrite existing files

Only arxiv and europepmc can provide figures. A PMC ID or a DOI routes to europepmc; a DOI is resolved to a PMC ID via NCBI's ID converter first. Naming any other source fails with exit 1. A paper that has no figure files at a supported source fails with exit 4.

This does not extract images from the PDF, render anything, or parse figure numbers — it saves exactly what is in the archive, under the name it has there. Filenames do not correspond to figure numbers: for 2511.11035, the file 3.pdf is actually Figure 1.

Measured against a 39-paper corpus, verified end-to-end against the live services on 2026-08-20: 31 papers (79%) yielded figure files. The 8 misses were 4 HTTP 404s, 3 packages with no images (not open access), and 1 arXiv paper submitted as a PDF with no source package. All 22 DOIs in that corpus resolved to a PMC ID.

fastpaper figures 2511.11035 -d papers/       # -> papers/2511.11035/{1,2,3}.pdf
fastpaper figures PMC7075534 -d papers/       # -> papers/PMC7075534/*.jpg

cite -- Walk citation edges

Same two forms as get. Returns the papers on the other end of a citation edge, in the usual result shape. Three sources hold edges: semantic and openalex, plus ads for astronomy and physics, which takes an ADS bibcode and needs ADS_API_TOKEN. A bare DOI routes to openalex, which needs no API key, while arXiv and S2: identifiers route to semantic.

fastpaper cite <IDENTIFIER> [OPTIONS]
fastpaper cite <SOURCE> <IDENTIFIER> [OPTIONS]

Options:
      --direction <DIR>  incoming (papers citing this one) or outgoing
                         (papers it cites) [default: incoming]
  -n, --limit <N>        Max edges [default: 20]
  -o, --output <PATH>    Write to a file instead of stdout

read -- Read a local PDF

Takes a path, never the network. Download first, then read what landed.

fastpaper read <PATH> [OPTIONS]

Options:
      --section <SEC>    abstract, introduction, methods, results,
                         discussion, conclusion, references, full [default: full]
      --list-sections    List the sections found in the PDF instead of reading it
      --grep <PATTERN>   Search the text for a regular expression.
                         Case-insensitive; use (?-i) for a case-sensitive one
      --context <N>      Characters of context on each side of a match
                         (requires --grep) [default: 500]
      --max-matches <N>  Show at most N matches (requires --grep) [default: 10]
      --max-length <N>   Truncate output to N characters
  -o, --output <PATH>    Write to file
fastpaper download 2301.08745            # -> ./papers/2301.08745.pdf
fastpaper read papers/2301.08745.pdf --section abstract
fastpaper read papers/2301.08745.pdf --grep 'limitations?'

A PDF records no section structure, so --section reads the typography: a heading is a line that holds nothing else and is set no smaller than the body. That handles the layouts journals actually use — a numbered II.METHODS, a Materials and methods, Nature running results before methods — but a paper set unusually can leave a section unfound, and --section then exits 4 rather than guessing. --list-sections says what was found before you ask for one:

fastpaper read papers/2301.08745.pdf --list-sections
abstract       p1    @105       12.0pt  Abstract
introduction   p1    @1763      12.0pt  1Introduction
conclusion     p8    @30510     12.0pt  5Conclusion
references     p9    @31793     12.0pt  References

Four, on a paper that has more: one of its sections is headed Analysis, which --section does not take. The page is there so the reading can be checked against the print, which is the only way to know it is right.

When the list has no abstract or introduction, that is usually the paper rather than the reading — Nature and its family print the abstract with no heading over it at all. Read the opening instead, which is the same few thousand characters the section would have given you:

fastpaper read papers/2301.08745.pdf --max-length 3000

With --format json, a --section read also reports the heading its slice started at, as heading.text, heading.offset and heading.font_size. That is what a caller quoting a section can check the slice against.

Two things read does not do. It leaves running heads, folios and journal footers in the text, so a quoted passage can have one spliced through it — they are only reliably distinguishable from content on layouts we could measure, and guessing would delete real text silently. And it reports no confidence: a section that was found is not thereby the right one.

--grep searches the section --section selected, or the whole paper when it is not given, so "try the section, fall back to full text" is one command with one flag changed. Matches are reported with their character offset, and windows that overlap are merged rather than printed twice. No match exits 4.

--max-length bounds the output without erasing the hit: an excerpt too big for the budget is cut around its match rather than from its start, and only matches still shown are counted. When something was left out, the trailing line names the flag that did it — raising --max-matches does nothing when --max-length was what bound — and --format json carries the same answer in truncated_by.

sources -- List sources and capabilities

fastpaper sources [--capabilities]

completions -- Shell completions

fastpaper completions fish > ~/.config/fish/completions/fastpaper.fish
fastpaper completions zsh > ~/.zfunc/_fastpaper
fastpaper completions bash >> ~/.bashrc

Environment variables

All optional except where noted. 24 of 26 sources work with zero configuration.

Variable Purpose
FASTPAPER_DOWNLOAD_DIR Default download directory (otherwise ./papers)
FASTPAPER_MAX_DOWNLOAD_SIZE Largest response body download will accept, and the largest archive figures will accept (otherwise 100MiB); 0 means unlimited
FASTPAPER_EMAIL Contact address sent to CrossRef, OpenAlex and NCBI. Unset means the parameter is omitted, which all three accept
SEMANTIC_SCHOLAR_API_KEY Higher rate limit for Semantic Scholar
OPENALEX_API_KEY Larger free tier for OpenAlex, which has metered usage since 2026-02
CORE_API_KEY Higher rate limit for CORE
NCBI_API_KEY Higher rate limit for PubMed / PMC
UNPAYWALL_EMAIL Required for Unpaywall, and it must be a real address
ADS_API_TOKEN Required for ads; free from https://scixplorer.org/user/settings/token
HF_TOKEN Higher rate limit for Hugging Face (anonymous: 500 requests per 5 minutes)

Every source also takes FASTPAPER_<SOURCE>_URL to override its base URL — FASTPAPER_ARXIV_URL, FASTPAPER_PUBMED_URL and so on — which is what the tests point at a local mock server. Sources whose files live on a different host than their API have a second override for that host: FASTPAPER_ARXIV_PDF_URL, FASTPAPER_PMC_DL_URL (which points at the PMC Cloud Service on AWS Open Data, not the article pages), FASTPAPER_JSTAGE_PDF_URL, FASTPAPER_ADS_PDF_URL.

One override does not fit that pattern, because it is not a source: FASTPAPER_IDCONV_URL points at NCBI's ID converter (default https://pmc.ncbi.nlm.nih.gov), which figures uses to turn a DOI into a PMC ID. Only the DOI route touches it — an arXiv ID or a PMC ID never does.

Exit codes

Code Meaning
0 Success
1 General error (rejected arguments, parse failure, network failure)
2 Usage error from argument parsing: unknown flag, bad value, or a flag whose partner is missing (--context without --grep)
4 Nothing to return: no such paper, no --grep match, no such --section, no PDF for this paper at this source, no figure files for this paper

Those four are the whole set. Anything a caller can branch on lives here — a rate limit, a 403 and a refused --max-size all arrive as 1, distinguished by the message rather than the code.

Exit 4 is the one worth branching on: it means the request was well-formed and this source simply has nothing, so the next move is another source rather than another spelling.

Commands that take -o print a receipt to stderr once the file is written — Saved: results.json (12 results) — so a caller never has to read the file back to find out what landed. -q suppresses it. Zero results still print, since that is the case most worth seeing.

download prints Saved: papers/2301.08745.pdf (2.2 MiB) on stderr as well, and figures prints Saved: 3 figure files to papers/2511.11035 (382.2 KiB). -q suppresses neither: both write nothing to stdout, and neither path is derivable from the arguments, since a / in a DOI becomes _.

Both also exit 0 rather than failing when a target file is already there, printing File already exists: ... instead — rerunning is safe, and 1 stays reserved for something actually going wrong. Pass --overwrite to replace what is on disk.

Contributing

Contributions are welcome! See CONTRIBUTING.md and the developer docs under docs/.

Acknowledgements

This project was inspired by paper-search-mcp, an MCP server for searching and downloading academic papers from multiple sources. Many thanks to its authors for showing what a multi-source paper tool can look like.

License

GPL-3.0

About

No description, website, or topics provided.

Resources

Contributing

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages