Observed since 20 Oct, 17:52 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Paper-request MCP: track, resolve, fetch (OA-only), and flag misconceptions for scientific papers
Paper-request MCP for scientific papers. The missing piece between
precis-mcp (navigates what's
already in your library) and
acatome-extract (ingests
PDFs that land in an inbox).
An LLM says "I want this paper" (DOI, arXiv id, title, or free-form citation). Quest:
acatome-extract takes over.Quest fetches from arXiv, Unpaywall, OpenAlex, Europe PMC, and Semantic
Scholar's open-access index only. It does not, will not, and cannot be
configured to use Sci-Hub, LibGen, institutional proxies without explicit opt-
in, or any other paywall-circumvention mechanism. Failed retrievals yield a
needs_user status with the publisher URL, for you to retrieve manually.
pip install acatome-quest-mcp
# or with uv
uv add acatome-quest-mcp
For dedup against a local acatome-store:
pip install 'acatome-quest-mcp[store]'
| Tool | What it does |
|---|---|
submit(ref, *, dry_run=False, source=None, priority=0, created_by=None) | Resolve + optionally queue. Idempotent. |
status(id=None, *, filter=None) | Read one or many requests. |
update(id, mode, **kwargs) | Mutate. Modes: confirm, repoint, flag, priority, cancel. |
submit_file(url?, content_base64?, filename?, request_id?, ref?, created_by?) | Attach a user-supplied PDF (e.g. Discord attachment) to an existing request or create a new one, flip to ingesting. |
submit(ref={"doi": "10.1021/jacs.2c01234"})
submit(ref={"title": "Anion exchange membranes for NOx reduction",
"authors": ["Feng, Z."], "year": 2024})
submit(ref={"raw": "Feng et al. 2024, Adv. Funct. Mater. 34, 2300512"})
submit(ref={"doi": "10.1234/x"}, dry_run=True) # resolve only, no queue
submit(ref={"doi": "10.1234/x"},
source={"document": "ch02.tex", "line": 147})
Response:
{
"id": "9f3b…",
"status": "found_in_store",
"resolved": {"doi": "10.1021/jacs.2c01234",
"title": "…", "authors": ["…"], "year": 2024,
"ref": "smith2022jacs"},
"candidates": [],
"misconceptions": []
}
status(id="9f3b…")
status(filter={"status": "needs_user"})
status(filter={"created_by": "asa", "has_misconception": True})
status(filter={"source_document": "ch02.tex"})
update(id, mode="confirm", choice=0) # pick candidates[0]
update(id, mode="repoint", doi="10.1023/A:…") # user-corrected DOI
update(id, mode="flag", code="retracted",
evidence="Retraction Watch 2024-08-12")
update(id, mode="priority", priority=5)
update(id, mode="cancel")
# User drops a PDF for an already-tracked request (reopens failed / needs_user):
submit_file(url="https://cdn.discordapp.com/…/paper.pdf",
request_id="7f3a…",
filename="feng2024.pdf")
# User supplies both a PDF and a DOI in one step (creates the request):
submit_file(url="https://cdn.discordapp.com/…/paper.pdf",
ref={"doi": "10.1021/jacs.2c01234"},
created_by="asa")
# Bytes already in memory (no URL to fetch):
submit_file(content_base64="JVBERi0xLjQKJf…",
request_id="7f3a…")
PDF magic bytes are validated; HTML error pages are rejected. The file is written to the extractor's inbox (~/.acatome/inbox/ by default) and the request flips to ingesting. If the paper's DOI is already in the store, the tool short-circuits to found_in_store without writing anything.
The acatome-quest binary exposes the same surface as the MCP plus a couple of
shell-friendly helpers:
acatome-quest submit 10.1021/jacs.2c01234
acatome-quest status <id>
acatome-quest status --filter status=needs_user
acatome-quest status --filter status=needs_user --count # just prints "3"
acatome-quest update <id> repoint --doi 10.1023/A:…
acatome-quest submit-file --path ./feng2024.pdf --request-id 7f3a…
acatome-quest submit-file --url https://.../paper.pdf --doi 10.1021/jacs.2c01234
acatome-quest report # markdown worklist
acatome-quest report --document ch04.tex --format markdown # scoped
acatome-quest runner [--once]
acatome-quest reconcile
report renders a paste-ready markdown document for every request in
needs_user, failed, or extract_failed — each entry with citation,
DOI/arXiv link, failure reason, misconception evidence, and a concrete
suggested action (repoint DOI, drop PDF into ~/.acatome/inbox/, request via
interlibrary loan, …). Hand it to a librarian or paste into an ILL form.
| Status | Meaning |
|---|---|
queued | Accepted, not yet fetched |
resolving | Metadata lookup in progress (transient) |
found_in_store | Dedup hit — slug in resolved.ref |
needs_user | Disambiguation or manual fetch required |
fetching | Runner has claimed and is downloading |
ingesting | PDF in inbox, waiting for acatome-extract watch |
ingested | Extraction done, slug in resolved.ref |
extract_failed | PDF delivered but extraction failed |
failed | All sources exhausted |
cancelled | update(mode=cancel) was called |
| Code | Severity | Trigger |
|---|---|---|
doi_invalid | major | Crossref 404 or syntactically malformed |
doi_truncated | major | 404, but doi + digit resolves |
doi_title_mismatch | critical | DOI resolves but title fuzz < 60 vs request |
title_not_found | critical | No S2/Crossref hit (fabrication suspect) |
duplicate_of | minor | Already in store under another slug |
retracted | critical | S2 / Retraction Watch flag |
preprint_of | info | arXiv preprint of a later journal paper |
pdf_mismatch | critical | User-dropped PDF resolved to a different paper than the request it was attached to |
agent ──submit()──► acatome-quest-mcp (FastMCP, stdio)
│
▼
cluster.papers.requests (Postgres)
│
▼
acatome-quest-runner (launchd, poll 30 s)
│
fetch: arxiv → unpaywall → …
│
▼
~/.acatome/inbox/<slug>__<hash>.pdf
│
▼
acatome-extract watch → acatome-store
▲
└── runner polls by DOI, flips to `ingested`
| Env var | Default | Description |
|---|---|---|
DATABASE_URL | postgresql://localhost/cluster | Postgres DSN |
QUEST_SCHEMA | papers | Schema name for the requests table |
ACATOME_INBOX | ~/.acatome/inbox | Drop directory watched by acatome-extract |
UNPAYWALL_EMAIL | (required at runner start) | Polite-pool contact |
ACATOME_CROSSREF_MAILTO | (recommended) | Crossref polite pool |
SEMANTIC_SCHOLAR_API_KEY | (optional) | Raises S2 rate limit |
QUEST_POLL_INTERVAL | 30 | Runner tick seconds |
QUEST_MAX_CONCURRENT | 4 | Max parallel fetches |
QUEST_INGEST_TIMEOUT | 900 | Seconds to wait for ingest after PDF drop |
QUEST_MAX_OPEN_PER_AGENT | 50 | Per-created_by cap |
uv sync
uv run pytest
uv run ruff check .
uv run mypy src tests
GPL-3.0-or-later. See LICENSE.
pypistats · 56 observations shown. Missing days are unknown. Source category: overall: without mirrors.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-20 – 2026-10-20 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-19 – 2026-10-19 | 4 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-18 – 2026-10-18 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-17 – 2026-10-17 | 6 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-16 – 2026-10-16 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-15 – 2026-10-15 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-13 – 2026-10-13 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-12 – 2026-10-12 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-11 – 2026-10-11 | 4 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-10 – 2026-10-10 | 4 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-09 – 2026-10-09 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-08 – 2026-10-08 | 9 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-07 – 2026-10-07 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-06 – 2026-10-06 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-04 – 2026-10-04 | 4 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-03 – 2026-10-03 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-10-01 – 2026-10-01 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-30 – 2026-09-30 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-28 – 2026-09-28 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-26 – 2026-09-26 | 7 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-25 – 2026-09-25 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-24 – 2026-09-24 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-23 – 2026-09-23 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-21 – 2026-09-21 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-18 – 2026-09-18 | 7 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-17 – 2026-09-17 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-12 – 2026-09-12 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-10 – 2026-09-10 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-09 – 2026-09-09 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-07 – 2026-09-07 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-06 – 2026-09-06 | 8 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-05 – 2026-09-05 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-09-03 – 2026-09-03 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-31 – 2026-08-31 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-30 – 2026-08-30 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-28 – 2026-08-28 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-24 – 2026-08-24 | 9 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-23 – 2026-08-23 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-21 – 2026-08-21 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-20 – 2026-08-20 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-19 – 2026-08-19 | 4 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-18 – 2026-08-18 | 36 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-17 – 2026-08-17 | 6 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-15 – 2026-08-15 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-14 – 2026-08-14 | 5 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-13 – 2026-08-13 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-11 – 2026-08-11 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-10 – 2026-08-10 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-09 – 2026-08-09 | 3 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-06 – 2026-08-06 | 2 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-04 – 2026-08-04 | 5 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-03 – 2026-08-03 | 10 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-02 – 2026-08-02 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-08-01 – 2026-08-01 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-07-27 – 2026-07-27 | 1 | 2026-10-21T15:54:41.5303589Z |
| 2026-07-24 – 2026-07-24 | 2 | 2026-10-21T15:54:41.5303589Z |
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.2.0 | 20 Apr 2026 | 6 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 17:52 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:2ac5850761f973e8f7a2a9c7918cbbd0b2389419e8249de0ca12ed98284c5346