Why
academic_search finds papers with DOIs, but a researcher often can't reach the full text without an institutional subscription. Unpaywall (by OurResearch, the team behind OpenAlex) maps DOIs to legal open-access copies for tens of millions of articles. Adding it completes the discover → metadata → access → extract chain: academic_search → OA resolution → scrape_page on the PDF. No other single MCP server offers the full chain.
academic_search now spans multiple providers — openalex, crossref, and exa (research-paper category) all return AcademicResults. OA enrichment sits below the provider layer, so it applies uniformly across all of them: any DOI-bearing result missing a PDF link can be enriched, regardless of which provider produced it.
What
OA resolution layered onto the existing academic pipeline (not a standalone tool):
- Enrichment: after
academic_search returns DOI-bearing results, optionally resolve each via Unpaywall and populate pdfUrl + openAccess when the provider didn't already supply them. Provider-agnostic — works for openalex, crossref, exa, and any future academic provider.
- Direct resolution: accept a
doi input on academic_search that triggers a direct DOI → metadata + best-OA-location lookup.
How
- Endpoint:
GET https://api.unpaywall.org/v2/{DOI}?email={email} → is_oa, oa_status (gold/green/hybrid/bronze), best_oa_location.url_for_pdf. Email-only auth, reusing the existing OpenAlexEmail / CrossRefEmail config pattern in AcademicProviderConfig (internal/search/domain.go). Generous 100k/day limit.
- Enrichment runs in
internal/tools/academic.go after results return; populate the existing AcademicResult.PDFUrl / OpenAccess fields (defined in internal/search/domain.go) only when empty (never overwrite a value the provider already supplied — e.g. an exa or openalex PDF link).
- SSRF-safe client (
scraper.NewSSRFSafeClient()), per-provider circuit breaker, 24h cache TTL keyed by DOI (OA status changes slowly).
- Best-effort: an Unpaywall failure must never fail the underlying
academic_search — results return unenriched.
Acceptance criteria
Dependencies
Why
academic_searchfinds papers with DOIs, but a researcher often can't reach the full text without an institutional subscription. Unpaywall (by OurResearch, the team behind OpenAlex) maps DOIs to legal open-access copies for tens of millions of articles. Adding it completes the discover → metadata → access → extract chain:academic_search→ OA resolution →scrape_pageon the PDF. No other single MCP server offers the full chain.academic_searchnow spans multiple providers —openalex,crossref, andexa(research-paper category) all returnAcademicResults. OA enrichment sits below the provider layer, so it applies uniformly across all of them: any DOI-bearing result missing a PDF link can be enriched, regardless of which provider produced it.What
OA resolution layered onto the existing academic pipeline (not a standalone tool):
academic_searchreturns DOI-bearing results, optionally resolve each via Unpaywall and populatepdfUrl+openAccesswhen the provider didn't already supply them. Provider-agnostic — works foropenalex,crossref,exa, and any future academic provider.doiinput onacademic_searchthat triggers a direct DOI → metadata + best-OA-location lookup.How
GET https://api.unpaywall.org/v2/{DOI}?email={email}→is_oa,oa_status(gold/green/hybrid/bronze),best_oa_location.url_for_pdf. Email-only auth, reusing the existingOpenAlexEmail/CrossRefEmailconfig pattern inAcademicProviderConfig(internal/search/domain.go). Generous 100k/day limit.internal/tools/academic.goafter results return; populate the existingAcademicResult.PDFUrl/OpenAccessfields (defined ininternal/search/domain.go) only when empty (never overwrite a value the provider already supplied — e.g. anexaoropenalexPDF link).scraper.NewSSRFSafeClient()), per-provider circuit breaker, 24h cache TTL keyed by DOI (OA status changes slowly).academic_search— results return unenriched.Acceptance criteria
academic_searchresults from any provider gainpdfUrl/openAccesswhen Unpaywall has an OA copy and the provider didn't supply onedoiinput performs a direct resolutionpdfUrlis never overwrittenhttptestmockdocs/TOOLS.mdnotes the enrichment behaviorDependencies
openalex,crossref,exa).