/** * @fileoverview OpenAlex API client service. Handles all communication with the OpenAlex REST API. * @module services/openalex/openalex-service */ import type { Context } from '@cyanheads/mcp-ts-core'; import { type AnalyzeParams, type AnalyzeResult, type AutocompleteParams, type AutocompleteRecord, type AutocompleteResult, type EntityRecord, type EntityType, type SearchParams, type SearchResult } from './types.js'; /** * Recovery for a PMCID that reached OpenAlex and came back with nothing. OpenAlex indexes no * PMCIDs — `has_pmcid:true` matches zero works and a work's `ids` object never carries the key — * so a PMCID cannot resolve however well formed it is, and the generic "verify the ID format" * advice sends the caller back to retry an identifier that can never work. Shared by the 404 * mapping here and the `openalex_resolve_name` miss notice so both say the same thing. */ export declare const PMCID_NOT_INDEXED_HINT = "OpenAlex indexes no PMCIDs, so a PMCID resolves nothing however it is written. Convert it to a PMID or DOI \u2014 the NCBI ID Converter (https://www.ncbi.nlm.nih.gov/pmc/tools/idconv/) does this \u2014 and retry with that identifier."; /** * Detect ID format and return the API path segment. * "10.1038/nature12373" → "doi:10.1038/nature12373" * "https://doi.org/10.1038/nature12373" → "doi:10.1038/nature12373" * "0000-0002-1825-0097" → "orcid:0000-0002-1825-0097" * "https://orcid.org/0000-0002-1825-0097" → "orcid:https://orcid.org/0000-0002-1825-0097" * "https://ror.org/00hx57361" → "ror:https://ror.org/00hx57361" * "013meh722" → "ror:013meh722" * "PMC1234567" → "pmcid:PMC1234567" * "https://pmc.ncbi.nlm.nih.gov/articles/PMC1234567/" → "pmcid:PMC1234567" * "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1234567/" → "pmcid:PMC1234567" * "https://pubmed.ncbi.nlm.nih.gov/21491125" → "pmid:21491125" * "PMID:21491125" → "pmid:21491125" * "W2741809807" → "W2741809807" */ export declare function normalizeId(id: string): string; /** An identifier the deterministic by-ID path can resolve, with the scheme it was read as. */ export interface ResolvedIdentifier { entityType: EntityType; /** The value in the form the OpenAlex path segment expects (`doi:10.1038/…`, `W2741809807`). */ id: string; /** Identifier scheme, for caller-facing messages: `doi`, `orcid`, `openalex`, … */ scheme: string; } /** * Classify a query as an identifier, or return `undefined` for a name. * * `normalizeId()` already knows every shape the by-ID lookup accepts, bare and URL-form alike, * and stamps external ones with their scheme prefix — so detection reduces to reading back what * it produced. Names never survive that: a name with a colon in it yields a prefix that is in no * scheme table, and anything else fails the native-ID pattern. */ export declare function inferIdentifier(query: string): ResolvedIdentifier | undefined; /** * Shape a singleton entity record like an autocomplete match, so both resolution paths of * `openalex_resolve_name` return one result type. `entity_type` is singularized to match what * the autocomplete endpoint emits (`work`, not `works`), and `works_count` stays null on works — * a work has no works of its own, which is the same thing autocomplete reports. */ export declare function toAutocompleteRecord(entityType: EntityType, record: EntityRecord): AutocompleteRecord; declare class OpenAlexService { private readonly baseUrl; private readonly apiKey; private readonly mailto; constructor(); /** Execute an HTTP request against the OpenAlex API with retry on transient failures. */ private request; private throwNormalizedRequestError; private parseResponse; /** Search/filter/sort entities, or retrieve a single entity by ID. */ search(params: SearchParams, ctx: Context): Promise; /** Group-by aggregation. */ analyze(params: AnalyzeParams, ctx: Context): Promise; /** * Resolve an identifier to the single entity it addresses, shaped as an autocomplete match. * * Autocomplete matches on `display_name` and the external-ID URL, so whether an identifier * resolves through it depends on scheme, bare-vs-URL form, and entity type — a bare ORCID or * a funder's own OpenAlex ID find nothing, while the identical query on works succeeds. This * path goes to `/{entity_type}/{id}` instead, where every accepted shape resolves or does not, * with no in-between. * * A miss returns no results rather than throwing. Autocomplete answers an unmatched query with * an empty list, and routing identifiers through here is meant to make them behave *more* like * the front door, not to hand one class of query a hard failure the rest never sees. */ resolveIdentifier(identifier: ResolvedIdentifier, ctx: Context): Promise; /** Autocomplete name resolution. */ autocomplete(params: AutocompleteParams, ctx: Context): Promise; } export declare function initOpenAlexService(): void; export declare function getOpenAlexService(): OpenAlexService; /** * Return the typed field catalog keyed by entity type → context → field list. * Only `filter` and `select` arrays are stored. `group_by` resolves to the `filter` key here; * the describe-fields handler then prunes the fields group_by cannot target (raw dates, * `*.search` operators, and `from_*`/`to_*` range modifiers). */ export declare function getFieldCatalog(): Record; export {}; //# sourceMappingURL=openalex-service.d.ts.map