pub async fn metadata_only(
ref_: &Ref,
profile: &CapabilityProfile,
ctx: &FetchContext,
) -> Result<MetadataOnlyOutcome, FetchError>Expand description
Resolve a Ref to metadata WITHOUT triggering a publisher PDF
fetch.
Binding spec: docs/MCP_TOOLS.md §11 (NORMATIVE — this function
MUST NOT call crate::http::HttpClient::fetch_pdf under any code
path). The posture-lint workflow greps for that pattern; the test
suite additionally exercises the DOI and arXiv branches end-to-end
against wiremock to assert the OA URL is reported, not followed.
§Dispatch
Ref::Doi(_)→ Crossref first (bibliographic metadata + OA URL viamessage.link[]). If Crossref returns a usable payload the call returns immediately; Unpaywall is consulted only as a fallback when Crossref fails. The Unpaywall fallback surfaces a license string and may overwriteoa_urlwith thebest_oa_locationchannel.Ref::Arxiv(_)→ArxivSource::fetch_metadata_only: ONLY the Atom feed (https://export.arxiv.org/api/query?id_list=<id>) is consulted; the PDF endpoint is NOT touched.licenseis set to the platform-wide"arxiv-default"token,oa_urlisNone(the arXiv abstract page is not a PDF URL).
§Side effects
Each consulted source appends ONE LogEvent::Fetch row to
ctx.log (arXiv emits its row under Capability::Metadata; the
DOI sources emit under Capability::Oa — they pre-date this
distinction and a follow-up slice may unify them). The orchestrator
itself does NOT bracket the call with SessionStart / SessionEnd
rows — that is the MCP server’s responsibility (it owns the
per-tool-call session boundary).
This function is the pure resolver: it consults the source(s)
and emits provenance rows, but it does NOT write to the store.
The docs/MCP_TOOLS.md §11 store-write SIDE EFFECT is provided by
metadata_only_to_store, which wraps this and persists the
metadata TOML to <root>/.metadata/<safekey>.toml. Keeping the
store-write in a separate entry point is exactly what lets
resolve_only safely delegate here — its contract forbids any
store write, and a pure metadata_only can never regress that
invariant (#139).
§Errors
Returns FetchError from the underlying Source dispatch. The
MCP boundary converts these to the closed crate::ErrorCode set
via the existing From<FetchError> for ErrorCode impl.