@cyanheads/openalex-mcp-server 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/AGENTS.md +20 -7
  2. package/CLAUDE.md +20 -7
  3. package/Dockerfile +17 -8
  4. package/README.md +20 -17
  5. package/changelog/0.9.x/0.9.0.md +48 -0
  6. package/dist/mcp-server/prompts/definitions/literature-review.prompt.js +1 -1
  7. package/dist/mcp-server/prompts/definitions/research-landscape.prompt.js +1 -1
  8. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts +27 -2
  9. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts.map +1 -1
  10. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js +34 -4
  11. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts +41 -4
  13. package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts.map +1 -1
  14. package/dist/mcp-server/tools/definitions/citation-graph.tool.js +62 -14
  15. package/dist/mcp-server/tools/definitions/citation-graph.tool.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/describe-fields.tool.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/describe-fields.tool.js +34 -4
  18. package/dist/mcp-server/tools/definitions/describe-fields.tool.js.map +1 -1
  19. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts +37 -1
  20. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/resolve-name.tool.js +175 -40
  22. package/dist/mcp-server/tools/definitions/resolve-name.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +41 -4
  24. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -1
  25. package/dist/mcp-server/tools/definitions/search-entities.tool.js +71 -23
  26. package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -1
  27. package/dist/mcp-server/tools/render-budget.d.ts +4 -2
  28. package/dist/mcp-server/tools/render-budget.d.ts.map +1 -1
  29. package/dist/mcp-server/tools/render-budget.js +4 -2
  30. package/dist/mcp-server/tools/render-budget.js.map +1 -1
  31. package/dist/mcp-server/tools/response-budget.d.ts +2 -1
  32. package/dist/mcp-server/tools/response-budget.d.ts.map +1 -1
  33. package/dist/mcp-server/tools/response-budget.js +30 -21
  34. package/dist/mcp-server/tools/response-budget.js.map +1 -1
  35. package/dist/services/openalex/budget.d.ts +3 -1
  36. package/dist/services/openalex/budget.d.ts.map +1 -1
  37. package/dist/services/openalex/budget.js +3 -1
  38. package/dist/services/openalex/budget.js.map +1 -1
  39. package/dist/services/openalex/field-catalog.json +50 -1
  40. package/dist/services/openalex/filter-values.d.ts +46 -0
  41. package/dist/services/openalex/filter-values.d.ts.map +1 -0
  42. package/dist/services/openalex/filter-values.js +294 -0
  43. package/dist/services/openalex/filter-values.js.map +1 -0
  44. package/dist/services/openalex/iso-codes.json +1565 -0
  45. package/dist/services/openalex/openalex-service.d.ts +172 -20
  46. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  47. package/dist/services/openalex/openalex-service.js +560 -120
  48. package/dist/services/openalex/openalex-service.js.map +1 -1
  49. package/dist/services/openalex/provider-text.d.ts +7 -6
  50. package/dist/services/openalex/provider-text.d.ts.map +1 -1
  51. package/dist/services/openalex/provider-text.js +485 -61
  52. package/dist/services/openalex/provider-text.js.map +1 -1
  53. package/package.json +14 -12
  54. package/server.json +10 -24
@@ -9,11 +9,72 @@ import { escapeMarkdown } from '../escape-markdown.js';
9
9
  import { renderBudgetTrailer } from '../render-budget.js';
10
10
  import { getOpenAlexService, inferIdentifier, PMCID_NOT_INDEXED_HINT, } from '../../../services/openalex/openalex-service.js';
11
11
  import { ENTITY_TYPES } from '../../../services/openalex/types.js';
12
+ /** The singular noun for an entity type, as a record of it is named: `funders` → `funder`. */
13
+ function entityNoun(entityType) {
14
+ return entityType.replace(/s$/, '');
15
+ }
16
+ function withArticle(noun) {
17
+ return `${/^[aeiou]/.test(noun) ? 'an' : 'a'} ${noun}`;
18
+ }
19
+ /** `a`, `a or b`, `a, b, or c`. */
20
+ function orList(words) {
21
+ return words.length < 3
22
+ ? words.join(' or ')
23
+ : `${words.slice(0, -1).join(', ')}, or ${words.at(-1)}`;
24
+ }
25
+ function hasFilters(input) {
26
+ return input.filters !== undefined && Object.keys(input.filters).length > 0;
27
+ }
28
+ /**
29
+ * Where a name search looked: ` among institutions with filters={…}`, or ` across all entity
30
+ * types`. The filters are JSON-encoded, the form the search tools echo them in, so a quote or
31
+ * line break in a value cannot leave the notice's line.
32
+ */
33
+ function nameSearchScope(input) {
34
+ const types = input.entity_type ? ` among ${input.entity_type}` : ' across all entity types';
35
+ return hasFilters(input) ? `${types} with filters=${JSON.stringify(input.filters)}` : types;
36
+ }
37
+ /**
38
+ * What to try after a name search matched nothing. Filters come first when they applied, since
39
+ * one may exclude the match; omitting entity_type is never offered alongside them, as filters
40
+ * apply to a name search only with entity_type. With no entity_type, a typed search is the
41
+ * suggestion: cross-type autocomplete misses names a one-type search finds (`p53` among keywords).
42
+ */
43
+ function nameMissAdvice(input) {
44
+ if (hasFilters(input)) {
45
+ return 'Loosen or drop filters first — they may exclude the match — then try a shorter name or alternate spelling.';
46
+ }
47
+ return input.entity_type
48
+ ? 'Try a shorter name, alternate spelling, or omit entity_type to search across all types.'
49
+ : 'Try a shorter name or alternate spelling, or pass entity_type to search one type — a one-type search can find names the cross-type search misses.';
50
+ }
51
+ /**
52
+ * The notice for a bare OpenAlex ID that resolved nothing (`missed` names that), followed by
53
+ * what its name search did: the matches it found, nothing, a failure upstream — or, with
54
+ * `filters` and no entity_type, no name search at all.
55
+ */
56
+ function idMissNotice(missed, nameFallback, matched, input) {
57
+ if (!nameFallback) {
58
+ return `${missed}. It was not searched as a name: filters apply to a name search only with entity_type — pass entity_type to search one type by name with these filters, or drop filters to search every type.`;
59
+ }
60
+ if (nameFallback.failedReason !== undefined) {
61
+ return `${missed}, and the name search it fell back to failed upstream (reason ${nameFallback.failedReason}) — call again to complete it.`;
62
+ }
63
+ return matched > 0
64
+ ? `${missed}, so it was searched as a name${nameSearchScope(input)} instead.`
65
+ : `${missed}, and as a name it matched nothing${nameSearchScope(input)}. ${nameMissAdvice(input)}`;
66
+ }
12
67
  export const resolveNameTool = tool('openalex_resolve_name', {
13
- description: "Resolve a name or an identifier to an OpenAlex ID. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. A name returns up to 10 autocomplete matches with disambiguation hints. An identifier — OpenAlex ID, DOI, ORCID, ROR, PMID, or ISSN, bare or in URL form — resolves directly to the one record it addresses, and needs no entity_type. A PMCID is recognized as well, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead.",
68
+ description: "Resolve a name or an identifier to an OpenAlex ID. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. A name returns up to 10 autocomplete matches with disambiguation hints. An identifier — OpenAlex ID, DOI, ORCID, ROR, PMID, or ISSN, bare or in URL form — resolves directly to the record it addresses instead of running a name search. Short names share the bare OpenAlex ID shape (keyword p53 reads as publisher ID P53), so a bare OpenAlex ID that resolves nothing is searched as a name next. Two identifiers can address more than one entity type: a ROR names an organization's institution, funder, and publisher records alike, so it returns each one that exists (institution first) unless entity_type picks one of those three; and a Crossref Funder Registry DOI (10.13039/…) resolves to the funder unless entity_type is works. A PMCID is recognized as well, bare or as a PubMed Central URL, but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work's PMID or DOI instead.",
14
69
  sourceUrl: 'https://github.com/cyanheads/openalex-mcp-server/blob/main/src/mcp-server/tools/definitions/resolve-name.tool.ts',
15
70
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
16
71
  errors: [
72
+ {
73
+ reason: 'filters_without_entity_type',
74
+ code: JsonRpcErrorCode.ValidationError,
75
+ when: 'A name `query` passed `filters` without `entity_type`. Filter fields belong to one entity type, and a name search across every type takes no filters.',
76
+ recovery: 'Add entity_type naming the type the filter fields belong to, or drop `filters` to search every entity type by name.',
77
+ },
17
78
  {
18
79
  reason: 'rate_limited',
19
80
  code: JsonRpcErrorCode.RateLimited,
@@ -30,6 +91,14 @@ export const resolveNameTool = tool('openalex_resolve_name', {
30
91
  recovery: "This server's daily OpenAlex budget is spent and refills at midnight UTC — retrying before then will not succeed.",
31
92
  thrownBy: 'service',
32
93
  },
94
+ {
95
+ reason: 'upstream_plan_required',
96
+ code: JsonRpcErrorCode.Forbidden,
97
+ when: 'A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 "Plan upgrade required").',
98
+ retryable: false,
99
+ recovery: 'Drop the plan-gated filter and rerun; retrying it unchanged will not succeed. To bound works by date, filter on publication_date, from_publication_date, or to_publication_date, which need no paid plan.',
100
+ thrownBy: 'service',
101
+ },
33
102
  {
34
103
  reason: 'upstream_timeout',
35
104
  code: JsonRpcErrorCode.Timeout,
@@ -60,13 +129,41 @@ export const resolveNameTool = tool('openalex_resolve_name', {
60
129
  recovery: 'Confirm the API key has access to autocomplete, then retry the request.',
61
130
  thrownBy: 'service',
62
131
  },
132
+ {
133
+ reason: 'id_dot_segment',
134
+ code: JsonRpcErrorCode.ValidationError,
135
+ when: 'An identifier `query` holds a `.` or `..` path segment, written plainly or percent-encoded.',
136
+ recovery: 'Pass the identifier itself — an OpenAlex ID, DOI, ORCID, ROR, PMID, or ISSN — with no relative path segments, or pass a name to search for instead.',
137
+ thrownBy: 'service',
138
+ },
63
139
  {
64
140
  reason: 'comma_in_filter_value',
65
141
  code: JsonRpcErrorCode.InvalidParams,
66
- when: 'A `filters` value contains a comma, which collides with the OpenAlex filter separator.',
142
+ when: 'A `filters` value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as display_name.search) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, geo.city, geo.region, raw_affiliation_strings, block_key, parsed_longest_name, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase.',
67
143
  recovery: 'Use `|` for OR within a filter value (e.g. "2020|2021"), or move a free-text phrase containing commas into the `query` parameter.',
68
144
  thrownBy: 'service',
69
145
  },
146
+ {
147
+ reason: 'invalid_country_code',
148
+ code: JsonRpcErrorCode.ValidationError,
149
+ when: 'A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, "unknown", or two codes joined by a space ("DE US").',
150
+ recovery: 'Pass the country as an ISO 3166-1 alpha-2 code such as DE, US, or GB (an alpha-3 code, numeric code, or English name is converted), or null for records with no country.',
151
+ thrownBy: 'service',
152
+ },
153
+ {
154
+ reason: 'mixed_and_or_in_filter_value',
155
+ code: JsonRpcErrorCode.ValidationError,
156
+ when: 'A `filters` value puts "|" (OR) beside an AND operator — a "+", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on "|" first and drops every alternative that ANDs more than one value, so no single value can carry both.',
157
+ recovery: 'Keep "|" and the AND operator in separate filter keys: OR the alternatives in this key and put the AND in a second key that reaches the same values — on works, {"authorships.institutions.country_code": "DE|FR", "institutions.country_code": "US"} matches (DE or FR) and US. The filters object holds each key once, so where no second key fits, run one call per alternative instead.',
158
+ thrownBy: 'service',
159
+ },
160
+ {
161
+ reason: 'quote_in_filter_value',
162
+ code: JsonRpcErrorCode.ValidationError,
163
+ when: 'A `filters` value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search.',
164
+ recovery: 'Remove the double quote from the value; a phrase is matched without quoting its own text.',
165
+ thrownBy: 'service',
166
+ },
70
167
  {
71
168
  reason: 'upstream_invalid_params',
72
169
  code: JsonRpcErrorCode.InvalidParams,
@@ -109,15 +206,15 @@ export const resolveNameTool = tool('openalex_resolve_name', {
109
206
  entity_type: z
110
207
  .enum(ENTITY_TYPES)
111
208
  .optional()
112
- .describe('Entity type to search. Omit for cross-entity search (useful when entity type is unknown). Not applied when `query` is an identifier — an identifier determines its own entity type.'),
209
+ .describe('Entity type to search. Omit for cross-entity search (useful when entity type is unknown). With an identifier `query` it narrows only an identifier that can address more than one type: a ROR to institutions, funders, or publishers (omit it to get every one of the three that exists), and a Funder Registry DOI (10.13039/…) to works instead of the funder. Every other identifier resolves as the type it belongs to, whatever this names — except under keywords, where a bare OpenAlex ID shape ("P53") is searched as a keyword name, since keyword IDs are slugs.'),
113
210
  query: z
114
211
  .string()
115
212
  .min(1)
116
- .describe('Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID ("W2741809807", "F4320332161"), DOI ("10.1038/nature12373"), ORCID ("0000-0002-1825-0097"), ROR ("https://ror.org/00hx57361"), PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"), ISSN ("1234-5678") — which resolves straight to that one record instead of running a name search. A keyword URL ("https://openalex.org/keywords/groundwater") resolves the same way; a bare keyword slug reads as a name and runs a name search, which finds it too. A PMCID ("PMC1234567" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work\'s PMID or DOI instead.'),
213
+ .describe('Name or partial name to resolve. Also accepts an identifier, bare or in URL form — OpenAlex ID ("W2741809807", "F4320332161", or an OpenAlex record-page or API URL such as "https://openalex.org/works/W2741809807"), DOI ("10.1038/nature12373", or a funder\'s Funder Registry DOI such as "10.13039/100000001"), ORCID ("0000-0002-1825-0097"), ROR ("https://ror.org/00hx57361"), PMID ("12345678" or "https://pubmed.ncbi.nlm.nih.gov/12345678"), ISSN ("1234-5678") — which resolves straight to the record it addresses, or for a ROR up to three (see entity_type), instead of running a name search. A bare OpenAlex ID that resolves nothing is then searched as a name — under entity_type when given, and not at all when filters come without entity_type — since short names share its shape ("P53" is a keyword); under entity_type keywords it is searched as a name from the start. A keyword URL ("https://openalex.org/keywords/groundwater") resolves the same way; a bare keyword slug reads as a name and runs a name search, which finds it too. A PMCID ("PMC1234567" or a PubMed Central URL) is recognized but OpenAlex indexes no PMCIDs, so it resolves nothing — pass the work\'s PMID or DOI instead.'),
117
214
  filters: z
118
215
  .record(z.string(), z.string())
119
216
  .optional()
120
- .describe('Narrow autocomplete results with filters. Example: restrict to a specific country or publication year range. Applies to name queries only — an identifier already addresses a single record.'),
217
+ .describe('Narrow autocomplete results with filters, in the openalex_search_entities filter syntax. Example: restrict to a specific country or publication year range. Country fields take ISO 3166-1 alpha-2 codes ({"country_code": "DE"} on institutions) and `language` takes ISO 639-1 codes ("en"); alpha-3, numeric, ISO 639-2, and English-name forms are converted — a multi-word name is one value, so "South Korea|Japan" ORs two countries — and a value that cannot be a country code is rejected. A multi-word value on a field matched as whole text (display_name, geo.city, geo.region, awards.funder_display_name) matches as one phrase ({"geo.city": "New York"} on institutions; a comma, as in "University of California, Berkeley", is part of it, and it cannot contain a double quote of its own), "!" and "|" included, but cannot be ANDed with "+" in the same field. A value mixing "|" with an AND operator — a "+", or a space on a field where words mean AND — is rejected, since OpenAlex drops every "|" alternative that ANDs more than one value; put the AND in a second key instead. Requires entity_type — filter fields belong to one entity type, so a name search across every type takes none, and a name query passing filters without entity_type is rejected. Applies to name queries only — an identifier lookup returns the records it addresses regardless.'),
121
218
  }),
122
219
  output: z.object({
123
220
  results: z
@@ -148,7 +245,7 @@ export const resolveNameTool = tool('openalex_resolve_name', {
148
245
  .describe('Disambiguation context as plain text — last institution (authors), host organization (sources), place or country (institutions); author names (works) from a name search, publication year from an identifier lookup. null when the record carries none.'),
149
246
  })
150
247
  .describe('A single autocomplete match with its ID, name, entity type, activity stats, and a disambiguation hint.'))
151
- .describe('Autocomplete matches, up to 10.'),
248
+ .describe('Matches: up to 10 autocomplete matches for a name; for an identifier, the record it addresses — for a ROR, up to three, institution then funder then publisher — or none.'),
152
249
  }),
153
250
  // Agent-facing success-path context — a notice when the query matched nothing so the
154
251
  // caller sees explicit guidance rather than a silent empty array, plus the OpenAlex budget
@@ -158,7 +255,7 @@ export const resolveNameTool = tool('openalex_resolve_name', {
158
255
  notice: z
159
256
  .string()
160
257
  .optional()
161
- .describe('Guidance notice. Set when nothing matched (echoes the query and suggests corrections) or when an identifier query was passed name-search parameters that do not apply to it. Absent otherwise.'),
258
+ .describe('Guidance notice. Set when nothing matched (echoes the query, and the filters when a name search applied them, and suggests corrections), when a bare OpenAlex ID resolved nothing and the query was searched as a name instead (names both outcomes, or why the name search did not run or finish), when an identifier query was passed name-search parameters that do not apply to it, or when one of the lookups for a ROR failed upstream (names the entity type whose record may be missing). Absent otherwise.'),
162
259
  budget: z
163
260
  .object({
164
261
  costUsd: z
@@ -181,51 +278,88 @@ export const resolveNameTool = tool('openalex_resolve_name', {
181
278
  },
182
279
  async handler(input, ctx) {
183
280
  const service = getOpenAlexService();
184
- // An identifier addresses one record and carries its own entity type, so it goes to the
185
- // deterministic by-ID lookup. Autocomplete only ever matched identifiers by accident — it
186
- // indexes the external-ID URL, so the bare forms and several native OpenAlex IDs miss.
187
- const identifier = inferIdentifier(input.query);
188
- const result = identifier
189
- ? await service.resolveIdentifier(identifier, ctx)
190
- : await service.autocomplete({
191
- entityType: input.entity_type,
281
+ // An identifier addresses its own record, so it goes to the deterministic by-ID lookup.
282
+ // Autocomplete only ever matched identifiers by accident — it indexes the external-ID URL,
283
+ // so the bare forms and several native OpenAlex IDs miss.
284
+ const identifier = inferIdentifier(input.query, input.entity_type);
285
+ const nameSearch = {
286
+ entityType: input.entity_type,
287
+ query: input.query,
288
+ filters: input.filters,
289
+ };
290
+ // JSON-encoded so a line break in the query cannot start a heading or end a blockquote.
291
+ const query = JSON.stringify(input.query);
292
+ if (!identifier) {
293
+ // Cross-entity autocomplete rejects `filter` outright, so this pair can only fail upstream.
294
+ if (hasFilters(input) && !input.entity_type) {
295
+ throw ctx.fail('filters_without_entity_type', '`filters` applies to a name search only with entity_type — filter fields belong to one entity type, and a search across every type takes none.');
296
+ }
297
+ const result = await service.autocomplete(nameSearch, ctx);
298
+ ctx.log.info('Name resolved', {
192
299
  query: input.query,
193
- filters: input.filters,
194
- }, ctx);
300
+ entityType: input.entity_type ?? 'all',
301
+ resolvedVia: 'autocomplete',
302
+ matchCount: result.results.length,
303
+ });
304
+ if (result.results.length === 0) {
305
+ ctx.enrich.notice(`No matches for ${query}${nameSearchScope(input)}. ${nameMissAdvice(input)}`);
306
+ }
307
+ return result;
308
+ }
309
+ // A bare OpenAlex ID shape is also a short name's, so one that resolves nothing is searched
310
+ // as a name next — unless filters came without entity_type, which no name search takes.
311
+ const canSearchByName = identifier.mayBeName === true && (input.entity_type !== undefined || !hasFilters(input));
312
+ const { results, failedEntityTypes, nameFallback } = await service.resolveIdentifier(identifier, ctx, canSearchByName ? nameSearch : undefined, input.query);
195
313
  ctx.log.info('Name resolved', {
196
314
  query: input.query,
197
- entityType: identifier?.entityType ?? input.entity_type ?? 'all',
198
- resolvedVia: identifier ? identifier.scheme : 'autocomplete',
199
- matchCount: result.results.length,
315
+ entityType: identifier.entityTypes.join(','),
316
+ resolvedVia: nameFallback ? 'autocomplete' : identifier.scheme,
317
+ matchCount: results.length,
200
318
  });
319
+ const scheme = identifier.scheme === 'openalex' ? 'OpenAlex ID' : identifier.scheme.toUpperCase();
320
+ const nouns = identifier.entityTypes.map(entityNoun);
321
+ const missed = `No ${orList(nouns)} in OpenAlex for ${scheme} ${query}`;
322
+ // A bare ID that missed was read as a name next, so its notice covers both readings.
323
+ if (identifier.mayBeName && (nameFallback || results.length === 0)) {
324
+ ctx.enrich.notice(idMissNotice(missed, nameFallback, results.length, input));
325
+ return { results };
326
+ }
201
327
  const notices = [];
202
- if (identifier) {
203
- const scheme = identifier.scheme === 'openalex' ? 'OpenAlex ID' : identifier.scheme.toUpperCase();
204
- const entity = identifier.entityType.replace(/s$/, '');
205
- const ignored = [];
206
- if (input.entity_type && input.entity_type !== identifier.entityType) {
207
- ignored.push(`entity_type="${input.entity_type}"`);
208
- }
209
- if (input.filters && Object.keys(input.filters).length > 0)
210
- ignored.push('filters');
211
- if (ignored.length > 0) {
212
- notices.push(`Resolved "${input.query}" by ${scheme} — it identifies one ${entity} directly, so ${ignored.join(' and ')} did not apply.`);
213
- }
214
- if (result.results.length === 0) {
328
+ const ignored = [];
329
+ if (input.entity_type && !identifier.entityTypes.includes(input.entity_type)) {
330
+ ignored.push(`entity_type="${input.entity_type}"`);
331
+ }
332
+ if (hasFilters(input))
333
+ ignored.push('filters');
334
+ if (ignored.length > 0) {
335
+ const addressed = nouns.length === 1 ? `one ${nouns[0]}` : orList(nouns.map(withArticle));
336
+ notices.push(`Resolved ${query} by ${scheme} — it identifies ${addressed} directly, so ${ignored.join(' and ')} did not apply.`);
337
+ }
338
+ if (results.length === 0) {
339
+ if (identifier.scheme === 'pmcid') {
215
340
  // A PMCID is the one identifier whose miss says nothing about the identifier: it is
216
341
  // well formed and OpenAlex still holds no record for it, so "check it for a typo"
217
342
  // sends the caller to re-check something that was never wrong.
218
- notices.push(identifier.scheme === 'pmcid'
219
- ? `No ${entity} in OpenAlex for PMCID "${input.query}". ${PMCID_NOT_INDEXED_HINT}`
220
- : `No ${entity} in OpenAlex for ${scheme} "${input.query}". An identifier either resolves or does not — check it for a typo, or search for the entity by name instead.`);
343
+ notices.push(`${missed}. ${PMCID_NOT_INDEXED_HINT}`);
344
+ }
345
+ else if (identifier.scheme === 'ror' && input.entity_type && nouns.length === 1) {
346
+ notices.push(`${missed} under entity_type="${input.entity_type}". One ROR can name an institution, a funder, and a publisher — omit entity_type to look up all three.`);
347
+ }
348
+ else {
349
+ notices.push(`${missed}. An identifier either resolves or does not — check it for a typo, or search for the entity by name instead.`);
350
+ if (identifier.scheme === 'doi' && identifier.entityTypes[0] === 'funders') {
351
+ notices.push('Some works carry a Funder Registry DOI too — pass entity_type="works" to look it up as a work.');
352
+ }
221
353
  }
222
354
  }
223
- else if (result.results.length === 0) {
224
- notices.push(`No matches for "${input.query}"${input.entity_type ? ` among ${input.entity_type}` : ''}. Try a shorter name, alternate spelling, or omit entity_type to search across all types.`);
355
+ if (failedEntityTypes) {
356
+ const failed = failedEntityTypes.map(entityNoun);
357
+ const lookups = failed.length === 1 ? 'lookup' : 'lookups';
358
+ notices.push(`The ${failed.join(' and ')} ${lookups} failed upstream, so any ${orList(failed)} record for ${scheme} ${query} is missing from these results — call again to complete them.`);
225
359
  }
226
360
  if (notices.length > 0)
227
361
  ctx.enrich.notice(notices.join(' '));
228
- return result;
362
+ return { results };
229
363
  },
230
364
  format: (result) => {
231
365
  if (result.results.length === 0) {
@@ -240,7 +374,8 @@ export const resolveNameTool = tool('openalex_resolve_name', {
240
374
  if (r.external_id)
241
375
  details.push(escapeMarkdown(r.external_id));
242
376
  details.push(`${r.cited_by_count} citations`);
243
- details.push(r.works_count === null ? 'n/a works' : `${r.works_count} works`);
377
+ if (r.works_count !== null)
378
+ details.push(`${r.works_count} works`);
244
379
  if (r.hint)
245
380
  details.push(escapeMarkdown(r.hint));
246
381
  lines.push(details.join(' | '));
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-name.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/resolve-name.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve-name.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/resolve-name.tool.ts"],"names":[],"mappings":"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"}
@@ -118,7 +118,19 @@ export declare const searchEntitiesTool: import("@cyanheads/mcp-ts-core").ToolDe
118
118
  readonly reason: "slice_field_not_array";
119
119
  readonly code: JsonRpcErrorCode.ValidationError;
120
120
  readonly when: "The `slice` field is `*`, or is not an array on the record at `id` — absent, null, a scalar, or an object.";
121
- readonly recovery: "Name a top-level array field of that record, such as authorships, referenced_works, or locations; openalex_describe_fields(entity_type, \"select\") lists the field names.";
121
+ readonly recovery: "Name a top-level array field of that record, such as authorships, author_list, referenced_works, or locations; openalex_describe_fields(entity_type, \"select\") lists the field names.";
122
+ }, {
123
+ readonly reason: "id_type_mismatch";
124
+ readonly code: JsonRpcErrorCode.ValidationError;
125
+ readonly when: "`id` is a native OpenAlex ID whose letter names another entity type than `entity_type`, or a concept ID (C…), a type this server does not serve.";
126
+ readonly recovery: "Pass the entity_type the ID letter names — W works, A authors, S sources, I institutions, T topics, P publishers, F funders. A concept ID (C…) has no entity_type here; search topics for the concept by name with openalex_resolve_name instead.";
127
+ readonly thrownBy: "service";
128
+ }, {
129
+ readonly reason: "id_dot_segment";
130
+ readonly code: JsonRpcErrorCode.ValidationError;
131
+ readonly when: "`id` holds a `.` or `..` path segment, written plainly or percent-encoded.";
132
+ readonly recovery: "Pass the identifier itself — an OpenAlex ID, DOI, ORCID, ROR, PMID, or ISSN — with no relative path segments, or drop `id` and search with `query` or `filters`.";
133
+ readonly thrownBy: "service";
122
134
  }, {
123
135
  readonly reason: "entity_not_found";
124
136
  readonly code: JsonRpcErrorCode.NotFound;
@@ -139,6 +151,13 @@ export declare const searchEntitiesTool: import("@cyanheads/mcp-ts-core").ToolDe
139
151
  readonly retryable: false;
140
152
  readonly recovery: "This server's daily OpenAlex budget is spent and refills at midnight UTC — retrying before then will not succeed.";
141
153
  readonly thrownBy: "service";
154
+ }, {
155
+ readonly reason: "upstream_plan_required";
156
+ readonly code: JsonRpcErrorCode.Forbidden;
157
+ readonly when: "A filter is one OpenAlex serves only on a paid plan — created_date, updated_date, or a from_/to_ form of either (HTTP 429 \"Plan upgrade required\").";
158
+ readonly retryable: false;
159
+ readonly recovery: "Drop the plan-gated filter and rerun; retrying it unchanged will not succeed. To bound works by date, filter on publication_date, from_publication_date, or to_publication_date, which need no paid plan.";
160
+ readonly thrownBy: "service";
142
161
  }, {
143
162
  readonly reason: "upstream_timeout";
144
163
  readonly code: JsonRpcErrorCode.Timeout;
@@ -149,7 +168,7 @@ export declare const searchEntitiesTool: import("@cyanheads/mcp-ts-core").ToolDe
149
168
  }, {
150
169
  readonly reason: "upstream_unavailable";
151
170
  readonly code: JsonRpcErrorCode.ServiceUnavailable;
152
- readonly when: "OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON.";
171
+ readonly when: "OpenAlex was unreachable or unusable — HTTP 503, a connection failure, a body that was empty, HTML, or unparseable JSON, or a semantic search whose query embedding failed upstream (HTTP 400 `Failed to embed query:`, or semantic search not configured).";
153
172
  readonly retryable: true;
154
173
  readonly recovery: "Wait and retry; check https://openalex.org for service status if the outage persists.";
155
174
  readonly thrownBy: "service";
@@ -168,8 +187,26 @@ export declare const searchEntitiesTool: import("@cyanheads/mcp-ts-core").ToolDe
168
187
  }, {
169
188
  readonly reason: "comma_in_filter_value";
170
189
  readonly code: JsonRpcErrorCode.InvalidParams;
171
- readonly when: "A filter value contains a comma, which collides with the OpenAlex filter separator.";
172
- readonly recovery: "Use `|` for OR within a filter value (e.g. \"2020|2021\"), or use a `.search` filter or the `query` parameter for free-text phrases that contain commas.";
190
+ readonly when: "A filter value contains a comma, which collides with the OpenAlex filter separator, on a field that takes no phrase. Free-text search filters (any `.search` key, such as title.search or abstract.search.exact) and fields matched as whole text (display_name, awards.funder_display_name, awards.funder_award_id, geo.city, geo.region, raw_affiliation_strings, block_key, parsed_longest_name, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase.";
191
+ readonly recovery: "Use `|` for OR within a filter value (e.g. \"2020|2021\"), or move a phrase that contains commas into a free-text search filter such as title.search or raw_affiliation_strings.search, or into the `query` parameter.";
192
+ readonly thrownBy: "service";
193
+ }, {
194
+ readonly reason: "invalid_country_code";
195
+ readonly code: JsonRpcErrorCode.ValidationError;
196
+ readonly when: "A country filter (a key ending in country_code, country_codes, or countries) holds a segment that cannot be a country code, such as a misspelled name, an unassigned number, \"unknown\", or two codes joined by a space (\"DE US\").";
197
+ readonly recovery: "Pass the country as an ISO 3166-1 alpha-2 code such as DE, US, or GB (an alpha-3 code, numeric code, or English name is converted), or null for records with no country.";
198
+ readonly thrownBy: "service";
199
+ }, {
200
+ readonly reason: "mixed_and_or_in_filter_value";
201
+ readonly code: JsonRpcErrorCode.ValidationError;
202
+ readonly when: "A filter value puts \"|\" (OR) beside an AND operator — a \"+\", or a space on a field where words mean AND (on a country field a space is always part of a country name, and inside a language name it is part of the name). OpenAlex splits the value on \"|\" first and drops every alternative that ANDs more than one value, so no single value can carry both.";
203
+ readonly recovery: "Keep \"|\" and the AND operator in separate filter keys: OR the alternatives in this key and put the AND in a second key that reaches the same values — on works, {\"authorships.institutions.country_code\": \"DE|FR\", \"institutions.country_code\": \"US\"} matches (DE or FR) and US. The filters object holds each key once, so where no second key fits, run one call per alternative instead.";
204
+ readonly thrownBy: "service";
205
+ }, {
206
+ readonly reason: "quote_in_filter_value";
207
+ readonly code: JsonRpcErrorCode.ValidationError;
208
+ readonly when: "A filter value sent to OpenAlex double-quoted holds a double quote inside its outer pair — a value the server quotes (a multi-word value on a field matched as whole text, or a value holding a comma on such a field or on a free-text search filter), or one the caller wrapped in double quotes on any filter but a free-text search.";
209
+ readonly recovery: "Remove the double quote from the value; a phrase is matched without quoting its own text.";
173
210
  readonly thrownBy: "service";
174
211
  }, {
175
212
  readonly reason: "upstream_invalid_params";
@@ -1 +1 @@
1
- {"version":3,"file":"search-entities.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-entities.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAuFjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAknB7B,CAAC"}
1
+ {"version":3,"file":"search-entities.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-entities.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA2FjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqqB7B,CAAC"}