{"id":49767053,"url":"https://github.com/retospect/acatome-quest-mcp","last_synced_at":"2026-05-11T10:57:35.102Z","repository":{"id":352635728,"uuid":"1215230621","full_name":"retospect/acatome-quest-mcp","owner":"retospect","description":"Paper-request MCP: track, resolve, fetch (OA-only), and flag misconceptions for scientific papers","archived":false,"fork":false,"pushed_at":"2026-04-30T09:16:20.000Z","size":84,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T10:56:56.278Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/retospect.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-19T16:46:34.000Z","updated_at":"2026-04-30T09:16:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/retospect/acatome-quest-mcp","commit_stats":null,"previous_names":["retospect/acatome-quest-mcp"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/retospect/acatome-quest-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retospect%2Facatome-quest-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retospect%2Facatome-quest-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retospect%2Facatome-quest-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retospect%2Facatome-quest-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/retospect","download_url":"https://codeload.github.com/retospect/acatome-quest-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/retospect%2Facatome-quest-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32891966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-11T10:57:34.104Z","updated_at":"2026-05-11T10:57:35.097Z","avatar_url":"https://github.com/retospect.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# acatome-quest-mcp\n\n**Paper-request MCP for scientific papers.**  The missing piece between\n[`precis-mcp`](https://github.com/retospect/precis-mcp) (navigates what's\nalready in your library) and\n[`acatome-extract`](https://github.com/retospect/acatome-extract) (ingests\nPDFs that land in an inbox).\n\nAn LLM says *\"I want this paper\"* (DOI, arXiv id, title, or free-form citation).\nQuest:\n\n1. **Checks the store first** — no duplicate work if we already have it.\n2. **Resolves the metadata** via Crossref + Semantic Scholar + arXiv.\n3. **Flags misconceptions** — broken DOI, DOI↔title mismatch, duplicate of an\n   existing slug, fabrication suspect.\n4. **Fetches the PDF** from legitimate open-access sources only and drops it\n   into the existing watch inbox, where `acatome-extract` takes over.\n5. Returns a **request id** in milliseconds.  Slow extraction happens out of\n   band; the MCP call never blocks.\n\n## Open access only — by policy\n\nQuest fetches from **arXiv, Unpaywall, OpenAlex, Europe PMC, and Semantic\nScholar's open-access index only**.  It does not, will not, and cannot be\nconfigured to use Sci-Hub, LibGen, institutional proxies without explicit opt-\nin, or any other paywall-circumvention mechanism.  Failed retrievals yield a\n`needs_user` status with the publisher URL, for you to retrieve manually.\n\n## Install\n\n```bash\npip install acatome-quest-mcp\n# or with uv\nuv add acatome-quest-mcp\n```\n\nFor dedup against a local `acatome-store`:\n\n```bash\npip install 'acatome-quest-mcp[store]'\n```\n\n## Four tools\n\n| Tool | What it does |\n|------|--------------|\n| `submit(ref, *, dry_run=False, source=None, priority=0, created_by=None)` | Resolve + optionally queue.  Idempotent. |\n| `status(id=None, *, filter=None)` | Read one or many requests. |\n| `update(id, mode, **kwargs)` | Mutate.  Modes: `confirm`, `repoint`, `flag`, `priority`, `cancel`. |\n| `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`. |\n\n### submit\n\n```python\nsubmit(ref={\"doi\": \"10.1021/jacs.2c01234\"})\nsubmit(ref={\"title\": \"Anion exchange membranes for NOx reduction\",\n            \"authors\": [\"Feng, Z.\"], \"year\": 2024})\nsubmit(ref={\"raw\": \"Feng et al. 2024, Adv. Funct. Mater. 34, 2300512\"})\nsubmit(ref={\"doi\": \"10.1234/x\"}, dry_run=True)      # resolve only, no queue\nsubmit(ref={\"doi\": \"10.1234/x\"},\n       source={\"document\": \"ch02.tex\", \"line\": 147})\n```\n\nResponse:\n\n```json\n{\n  \"id\": \"9f3b…\",\n  \"status\": \"found_in_store\",\n  \"resolved\": {\"doi\": \"10.1021/jacs.2c01234\",\n               \"title\": \"…\", \"authors\": [\"…\"], \"year\": 2024,\n               \"ref\": \"smith2022jacs\"},\n  \"candidates\": [],\n  \"misconceptions\": []\n}\n```\n\n### status\n\n```python\nstatus(id=\"9f3b…\")\nstatus(filter={\"status\": \"needs_user\"})\nstatus(filter={\"created_by\": \"asa\", \"has_misconception\": True})\nstatus(filter={\"source_document\": \"ch02.tex\"})\n```\n\n### update\n\n```python\nupdate(id, mode=\"confirm\", choice=0)            # pick candidates[0]\nupdate(id, mode=\"repoint\", doi=\"10.1023/A:…\")   # user-corrected DOI\nupdate(id, mode=\"flag\", code=\"retracted\",\n       evidence=\"Retraction Watch 2024-08-12\")\nupdate(id, mode=\"priority\", priority=5)\nupdate(id, mode=\"cancel\")\n```\n\n### submit_file\n\n```python\n# User drops a PDF for an already-tracked request (reopens failed / needs_user):\nsubmit_file(url=\"https://cdn.discordapp.com/…/paper.pdf\",\n            request_id=\"7f3a…\",\n            filename=\"feng2024.pdf\")\n\n# User supplies both a PDF and a DOI in one step (creates the request):\nsubmit_file(url=\"https://cdn.discordapp.com/…/paper.pdf\",\n            ref={\"doi\": \"10.1021/jacs.2c01234\"},\n            created_by=\"asa\")\n\n# Bytes already in memory (no URL to fetch):\nsubmit_file(content_base64=\"JVBERi0xLjQKJf…\",\n            request_id=\"7f3a…\")\n```\n\nPDF 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.\n\n## CLI\n\nThe `acatome-quest` binary exposes the same surface as the MCP plus a couple of\nshell-friendly helpers:\n\n```bash\nacatome-quest submit 10.1021/jacs.2c01234\nacatome-quest status \u003cid\u003e\nacatome-quest status --filter status=needs_user\nacatome-quest status --filter status=needs_user --count     # just prints \"3\"\nacatome-quest update \u003cid\u003e repoint --doi 10.1023/A:…\nacatome-quest submit-file --path ./feng2024.pdf --request-id 7f3a…\nacatome-quest submit-file --url https://.../paper.pdf --doi 10.1021/jacs.2c01234\nacatome-quest report                                         # markdown worklist\nacatome-quest report --document ch04.tex --format markdown   # scoped\nacatome-quest runner [--once]\nacatome-quest reconcile\n```\n\n`report` renders a paste-ready markdown document for every request in\n`needs_user`, `failed`, or `extract_failed` — each entry with citation,\nDOI/arXiv link, failure reason, misconception evidence, and a concrete\nsuggested action (repoint DOI, drop PDF into `~/.acatome/inbox/`, request via\ninterlibrary loan, …). Hand it to a librarian or paste into an ILL form.\n\n## Statuses\n\n| Status | Meaning |\n|--------|---------|\n| `queued` | Accepted, not yet fetched |\n| `resolving` | Metadata lookup in progress (transient) |\n| `found_in_store` | Dedup hit — slug in `resolved.ref` |\n| `needs_user` | Disambiguation or manual fetch required |\n| `fetching` | Runner has claimed and is downloading |\n| `ingesting` | PDF in inbox, waiting for `acatome-extract watch` |\n| `ingested` | Extraction done, slug in `resolved.ref` |\n| `extract_failed` | PDF delivered but extraction failed |\n| `failed` | All sources exhausted |\n| `cancelled` | `update(mode=cancel)` was called |\n\n## Misconception codes\n\n| Code | Severity | Trigger |\n|------|----------|---------|\n| `doi_invalid` | major | Crossref 404 or syntactically malformed |\n| `doi_truncated` | major | 404, but `doi + digit` resolves |\n| `doi_title_mismatch` | critical | DOI resolves but title fuzz \u003c 60 vs request |\n| `title_not_found` | critical | No S2/Crossref hit (fabrication suspect) |\n| `duplicate_of` | minor | Already in store under another slug |\n| `retracted` | critical | S2 / Retraction Watch flag |\n| `preprint_of` | info | arXiv preprint of a later journal paper |\n| `pdf_mismatch` | critical | User-dropped PDF resolved to a different paper than the request it was attached to |\n\n## Architecture\n\n```text\n agent ──submit()──► acatome-quest-mcp (FastMCP, stdio)\n                            │\n                            ▼\n              cluster.papers.requests (Postgres)\n                            │\n                            ▼\n           acatome-quest-runner (launchd, poll 30 s)\n                  │\n          fetch: arxiv → unpaywall → …\n                  │\n                  ▼\n          ~/.acatome/inbox/\u003cslug\u003e__\u003chash\u003e.pdf\n                  │\n                  ▼\n          acatome-extract watch  →  acatome-store\n                  ▲\n                  └── runner polls by DOI, flips to `ingested`\n```\n\n## Configuration\n\n| Env var | Default | Description |\n|---------|---------|-------------|\n| `DATABASE_URL` | `postgresql://localhost/cluster` | Postgres DSN |\n| `QUEST_SCHEMA` | `papers` | Schema name for the `requests` table |\n| `ACATOME_INBOX` | `~/.acatome/inbox` | Drop directory watched by `acatome-extract` |\n| `UNPAYWALL_EMAIL` | *(required at runner start)* | Polite-pool contact |\n| `ACATOME_CROSSREF_MAILTO` | *(recommended)* | Crossref polite pool |\n| `SEMANTIC_SCHOLAR_API_KEY` | *(optional)* | Raises S2 rate limit |\n| `QUEST_POLL_INTERVAL` | `30` | Runner tick seconds |\n| `QUEST_MAX_CONCURRENT` | `4` | Max parallel fetches |\n| `QUEST_INGEST_TIMEOUT` | `900` | Seconds to wait for ingest after PDF drop |\n| `QUEST_MAX_OPEN_PER_AGENT` | `50` | Per-`created_by` cap |\n\n## Development\n\n```bash\nuv sync\nuv run pytest\nuv run ruff check .\nuv run mypy src tests\n```\n\n## License\n\nGPL-3.0-or-later.  See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fretospect%2Facatome-quest-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fretospect%2Facatome-quest-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fretospect%2Facatome-quest-mcp/lists"}