@cyanheads/openalex-mcp-server 0.8.0 → 0.10.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 (67) hide show
  1. package/AGENTS.md +25 -7
  2. package/CLAUDE.md +25 -7
  3. package/Dockerfile +17 -8
  4. package/README.md +28 -19
  5. package/changelog/0.10.x/0.10.0.md +30 -0
  6. package/changelog/0.9.x/0.9.0.md +48 -0
  7. package/dist/config/server-config.d.ts +2 -0
  8. package/dist/config/server-config.d.ts.map +1 -1
  9. package/dist/config/server-config.js +10 -0
  10. package/dist/config/server-config.js.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp-server/prompts/definitions/literature-review.prompt.js +1 -1
  14. package/dist/mcp-server/prompts/definitions/research-landscape.prompt.js +1 -1
  15. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts +35 -4
  16. package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js +86 -41
  18. package/dist/mcp-server/tools/definitions/analyze-trends.tool.js.map +1 -1
  19. package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts +48 -5
  20. package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/citation-graph.tool.js +77 -24
  22. package/dist/mcp-server/tools/definitions/citation-graph.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/definitions/describe-fields.tool.d.ts.map +1 -1
  24. package/dist/mcp-server/tools/definitions/describe-fields.tool.js +34 -4
  25. package/dist/mcp-server/tools/definitions/describe-fields.tool.js.map +1 -1
  26. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts +43 -1
  27. package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts.map +1 -1
  28. package/dist/mcp-server/tools/definitions/resolve-name.tool.js +182 -40
  29. package/dist/mcp-server/tools/definitions/resolve-name.tool.js.map +1 -1
  30. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +64 -6
  31. package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/search-entities.tool.js +138 -35
  33. package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -1
  34. package/dist/mcp-server/tools/page-size.d.ts +18 -0
  35. package/dist/mcp-server/tools/page-size.d.ts.map +1 -0
  36. package/dist/mcp-server/tools/page-size.js +28 -0
  37. package/dist/mcp-server/tools/page-size.js.map +1 -0
  38. package/dist/mcp-server/tools/render-budget.d.ts +4 -2
  39. package/dist/mcp-server/tools/render-budget.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/render-budget.js +4 -2
  41. package/dist/mcp-server/tools/render-budget.js.map +1 -1
  42. package/dist/mcp-server/tools/response-budget.d.ts +3 -3
  43. package/dist/mcp-server/tools/response-budget.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/response-budget.js +64 -35
  45. package/dist/mcp-server/tools/response-budget.js.map +1 -1
  46. package/dist/services/openalex/budget.d.ts +3 -1
  47. package/dist/services/openalex/budget.d.ts.map +1 -1
  48. package/dist/services/openalex/budget.js +3 -1
  49. package/dist/services/openalex/budget.js.map +1 -1
  50. package/dist/services/openalex/field-catalog.json +50 -1
  51. package/dist/services/openalex/filter-values.d.ts +48 -0
  52. package/dist/services/openalex/filter-values.d.ts.map +1 -0
  53. package/dist/services/openalex/filter-values.js +350 -0
  54. package/dist/services/openalex/filter-values.js.map +1 -0
  55. package/dist/services/openalex/iso-codes.json +1565 -0
  56. package/dist/services/openalex/openalex-service.d.ts +176 -20
  57. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  58. package/dist/services/openalex/openalex-service.js +573 -127
  59. package/dist/services/openalex/openalex-service.js.map +1 -1
  60. package/dist/services/openalex/provider-text.d.ts +7 -6
  61. package/dist/services/openalex/provider-text.d.ts.map +1 -1
  62. package/dist/services/openalex/provider-text.js +485 -61
  63. package/dist/services/openalex/provider-text.js.map +1 -1
  64. package/dist/services/openalex/types.d.ts +5 -2
  65. package/dist/services/openalex/types.d.ts.map +1 -1
  66. package/package.json +14 -12
  67. package/server.json +35 -21
@@ -18,7 +18,7 @@ export declare const analyzeTrendsTool: import("@cyanheads/mcp-ts-core").ToolDef
18
18
  group_by: z.ZodString;
19
19
  filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
20
20
  include_unknown: z.ZodDefault<z.ZodBoolean>;
21
- per_page: z.ZodDefault<z.ZodNumber>;
21
+ per_page: z.ZodDefault<z.ZodLazy<z.ZodNumber>>;
22
22
  order: z.ZodOptional<z.ZodEnum<{
23
23
  count: "count";
24
24
  key: "key";
@@ -32,11 +32,17 @@ export declare const analyzeTrendsTool: import("@cyanheads/mcp-ts-core").ToolDef
32
32
  }, z.core.$strip>;
33
33
  groups: z.ZodArray<z.ZodObject<{
34
34
  key: z.ZodString;
35
- key_display_name: z.ZodString;
35
+ key_display_name: z.ZodNullable<z.ZodString>;
36
36
  count: z.ZodNumber;
37
37
  is_unknown: z.ZodOptional<z.ZodLiteral<true>>;
38
38
  }, z.core.$strip>>;
39
39
  }, z.core.$strip>, readonly [{
40
+ readonly reason: "unmatched_quote_in_filter_value";
41
+ readonly code: JsonRpcErrorCode.ValidationError;
42
+ readonly when: "A filter value sent unquoted has an unmatched double quote.";
43
+ readonly recovery: "Pair double quotes used for a search phrase, or remove the stray quote.";
44
+ readonly thrownBy: "service";
45
+ }, {
40
46
  readonly reason: "rate_limited";
41
47
  readonly code: JsonRpcErrorCode.RateLimited;
42
48
  readonly when: "OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429).";
@@ -50,6 +56,13 @@ export declare const analyzeTrendsTool: import("@cyanheads/mcp-ts-core").ToolDef
50
56
  readonly retryable: false;
51
57
  readonly recovery: "This server's daily OpenAlex budget is spent and refills at midnight UTC — retrying before then will not succeed.";
52
58
  readonly thrownBy: "service";
59
+ }, {
60
+ readonly reason: "upstream_plan_required";
61
+ readonly code: JsonRpcErrorCode.Forbidden;
62
+ 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\").";
63
+ readonly retryable: false;
64
+ 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.";
65
+ readonly thrownBy: "service";
53
66
  }, {
54
67
  readonly reason: "upstream_timeout";
55
68
  readonly code: JsonRpcErrorCode.Timeout;
@@ -79,8 +92,26 @@ export declare const analyzeTrendsTool: import("@cyanheads/mcp-ts-core").ToolDef
79
92
  }, {
80
93
  readonly reason: "comma_in_filter_value";
81
94
  readonly code: JsonRpcErrorCode.InvalidParams;
82
- readonly when: "A filter value contains a comma, which collides with the OpenAlex filter separator.";
83
- 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.";
95
+ 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.";
96
+ 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.";
97
+ readonly thrownBy: "service";
98
+ }, {
99
+ readonly reason: "invalid_country_code";
100
+ readonly code: JsonRpcErrorCode.ValidationError;
101
+ 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\").";
102
+ 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.";
103
+ readonly thrownBy: "service";
104
+ }, {
105
+ readonly reason: "mixed_and_or_in_filter_value";
106
+ readonly code: JsonRpcErrorCode.ValidationError;
107
+ 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.";
108
+ 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.";
109
+ readonly thrownBy: "service";
110
+ }, {
111
+ readonly reason: "quote_in_filter_value";
112
+ readonly code: JsonRpcErrorCode.ValidationError;
113
+ 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.";
114
+ readonly recovery: "Remove the double quote from the value; a phrase is matched without quoting its own text.";
84
115
  readonly thrownBy: "service";
85
116
  }, {
86
117
  readonly reason: "upstream_invalid_params";
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-trends.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAWjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0W5B,CAAC"}
1
+ {"version":3,"file":"analyze-trends.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAgBjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkZ5B,CAAC"}
@@ -5,17 +5,27 @@
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
6
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { escapeMarkdown } from '../escape-markdown.js';
8
+ import { clampPageSize, pageSizeSchema } from '../page-size.js';
8
9
  import { renderBudgetTrailer } from '../render-budget.js';
9
10
  import { joinNotices } from '../response-budget.js';
10
- import { getOpenAlexService } from '../../../services/openalex/openalex-service.js';
11
+ import { getOpenAlexService, isBooleanGroupBy } from '../../../services/openalex/openalex-service.js';
11
12
  import { ENTITY_TYPES } from '../../../services/openalex/types.js';
12
13
  /** Disclosure for a works group_by over `authorships.countries` (see the handler). */
13
14
  const COUNTRIES_CAP_NOTICE = 'OpenAlex builds authorships.countries groups from only the first 100 authorships of each work, so a country that appears only past position 100 on a large collaboration is not counted. Group by authorships.institutions.country_code to count every authorship.';
15
+ /** Upstream cursors can wrap even after a short page; callers own the stopping decision. */
16
+ const KEY_ORDER_GUIDANCE = "OpenAlex can wrap a key-ordered walk to earlier groups. Stop when a page's first key repeats an earlier key, or when a page is short or empty.";
14
17
  export const analyzeTrendsTool = tool('openalex_analyze_trends', {
15
- description: 'Aggregate OpenAlex entities into groups and count them. Use for trend analysis (group works by publication_year), distribution analysis (group by oa_status, type, country), and comparative analysis (group by institution or topic). Combine with filters to scope the analysis. Returns up to 200 groups per page — use cursor pagination for fields with many distinct values.',
18
+ description: `Aggregate OpenAlex entities into groups and count them. Use for trend analysis (group works by publication_year), distribution analysis (group by oa_status, type, country), and comparative analysis (group by institution or topic). Combine with filters to scope the analysis. Returns up to 200 groups per page — use order "key" and cursor pagination for fields with many distinct values. ${KEY_ORDER_GUIDANCE}`,
16
19
  sourceUrl: 'https://github.com/cyanheads/openalex-mcp-server/blob/main/src/mcp-server/tools/definitions/analyze-trends.tool.ts',
17
20
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
18
21
  errors: [
22
+ {
23
+ reason: 'unmatched_quote_in_filter_value',
24
+ code: JsonRpcErrorCode.ValidationError,
25
+ when: 'A filter value sent unquoted has an unmatched double quote.',
26
+ recovery: 'Pair double quotes used for a search phrase, or remove the stray quote.',
27
+ thrownBy: 'service',
28
+ },
19
29
  {
20
30
  reason: 'rate_limited',
21
31
  code: JsonRpcErrorCode.RateLimited,
@@ -32,6 +42,14 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
32
42
  recovery: "This server's daily OpenAlex budget is spent and refills at midnight UTC — retrying before then will not succeed.",
33
43
  thrownBy: 'service',
34
44
  },
45
+ {
46
+ reason: 'upstream_plan_required',
47
+ code: JsonRpcErrorCode.Forbidden,
48
+ 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").',
49
+ retryable: false,
50
+ 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.',
51
+ thrownBy: 'service',
52
+ },
35
53
  {
36
54
  reason: 'upstream_timeout',
37
55
  code: JsonRpcErrorCode.Timeout,
@@ -65,8 +83,29 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
65
83
  {
66
84
  reason: 'comma_in_filter_value',
67
85
  code: JsonRpcErrorCode.InvalidParams,
68
- when: 'A filter value contains a comma, which collides with the OpenAlex filter separator.',
69
- 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.',
86
+ 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.',
87
+ 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.',
88
+ thrownBy: 'service',
89
+ },
90
+ {
91
+ reason: 'invalid_country_code',
92
+ code: JsonRpcErrorCode.ValidationError,
93
+ 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").',
94
+ 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.',
95
+ thrownBy: 'service',
96
+ },
97
+ {
98
+ reason: 'mixed_and_or_in_filter_value',
99
+ code: JsonRpcErrorCode.ValidationError,
100
+ 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.',
101
+ 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.',
102
+ thrownBy: 'service',
103
+ },
104
+ {
105
+ reason: 'quote_in_filter_value',
106
+ code: JsonRpcErrorCode.ValidationError,
107
+ 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.',
108
+ recovery: 'Remove the double quote from the value; a phrase is matched without quoting its own text.',
70
109
  thrownBy: 'service',
71
110
  },
72
111
  {
@@ -119,31 +158,27 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
119
158
  group_by: z
120
159
  .string()
121
160
  .min(1)
122
- .describe('Field to group by. Works examples: "publication_year", "type", "oa_status", "primary_topic.field.id", "authorships.institutions.country_code", "is_retracted". OpenAlex builds authorships.countries groups from only the first 100 authorships of each work; authorships.institutions.country_code counts every authorship. Authors: "last_known_institutions.country_code", "has_orcid". Sources: "type", "is_oa", "country_code". Not all fields support group_by — call openalex_describe_fields(entity_type, "group_by") for the groupable set.'),
161
+ .describe('Field to group by. Works examples: "publication_year", "type", "oa_status", "primary_topic.field.id", "authorships.institutions.country_code", "is_retracted". The native ":include_unknown" suffix also enables include_unknown, even when the boolean is false; passing both adds the suffix once. OpenAlex builds authorships.countries groups from only the first 100 authorships of each work; authorships.institutions.country_code counts every authorship. Authors: "last_known_institutions.country_code", "has_orcid". Sources: "type", "is_oa", "country_code". Not all fields support group_by — call openalex_describe_fields(entity_type, "group_by") for the groupable set.'),
123
162
  filters: z
124
163
  .record(z.string(), z.string())
125
164
  .optional()
126
- .describe("Filter criteria (same syntax as openalex_search_entities filters). Narrows the population before aggregation. For full-text within filters, use abstract.search, title.search, or default.search — there is no bare 'search' filter key. Example: group works by year filtered to a specific topic."),
165
+ .describe('Filter record object, using the same value syntax as openalex_search_entities; native string input is unavailable here. Narrows the population before aggregation. Country fields take ISO 3166-1 alpha-2 codes ("DE") 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, awards.funder_display_name, geo.city, raw_affiliation_strings) matches as one phrase ("National Science Foundation"; 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 sent unquoted must have paired double quotes; balanced search phrases remain valid. 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. For full-text within filters, use abstract.search, title.search, or default.search — there is no bare \'search\' filter key. Example: group works by year filtered to a specific topic.'),
127
166
  include_unknown: z
128
167
  .boolean()
129
168
  .default(false)
130
- .describe('Add a group for entities with no value for the grouped field. Hidden by default. That group carries `is_unknown: true`; OpenAlex keys it -111 or -111.0 on numeric fields, "unknown" on text fields and under order "key", and an ID ending in /unknown on ID fields — a sentinel, not a measured value. The key is not a filter value: passing -111 as a filter matches a numeric range, not the entities with no value. Boolean fields have no separate group — a missing value counts as false.'),
131
- per_page: z
132
- .number()
133
- .int()
134
- .min(1)
135
- .max(200)
169
+ .describe('Add a group for entities with no value for the grouped field. Hidden by default unless group_by has the native ":include_unknown" suffix, which enables this setting even when the boolean is false; passing both adds the suffix once. That group carries `is_unknown: true`; OpenAlex keys it -111 or -111.0 on numeric fields, "unknown" on text fields and under order "key", and an ID ending in /unknown on ID fields — a sentinel, not a measured value. The key is not a filter value: passing -111 as a filter matches a numeric range, not the entities with no value. Boolean fields have no separate group — a missing value counts as false.'),
170
+ per_page: pageSizeSchema(200)
136
171
  .default(200)
137
- .describe('Maximum groups per page (1-200). Default 200 (the upstream cap). A real top-N knob when order is count (the default) — reduce to return only the highest-count groups.'),
172
+ .describe('Maximum groups per page. Default 200 (the upstream cap); positive integers above 200 clamp to 200 with a notice unless OPENALEX_CLAMP_PER_PAGE=false restores rejection. A real top-N knob in count order without a cursor (the default) — reduce to return only the highest-count groups.'),
138
173
  order: z
139
174
  .enum(['count', 'key'])
140
175
  .optional()
141
- .describe('Sort order for groups. Omit or pass "count" (default) to return the top-N groups by count descending — no further pages. Pass "key" to enumerate all distinct values in key-ascending order with cursor pagination. Use "key" only when you need a full traversal; most analysis calls want "count".'),
176
+ .describe(`Sort order for groups. Without a cursor, omit or pass "count" (default) to return the top-N groups by count descending — no further pages. Pass "key" to enumerate distinct values in key-ascending order with cursor pagination. Boolean fields keep count order even with "key" and do not support cursor pagination. A supplied cursor selects key order even if order is omitted or "count". ${KEY_ORDER_GUIDANCE}`),
142
177
  cursor: z
143
178
  .string()
144
179
  .min(1)
145
180
  .optional()
146
- .describe('Pagination cursor from a previous response. Only relevant when order is "key" — count-descending results have no next page. Pass the next_cursor from the previous response to advance. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page.'),
181
+ .describe(`Pagination cursor from a previous response. A supplied cursor selects key order even if order is omitted or "count". Pass the next_cursor from the previous response to advance, subject to the stop conditions below. Omit it on the first call — an empty string is rejected, since a supplied-but-blank cursor is a caller mistake rather than a request for the first page. ${KEY_ORDER_GUIDANCE}`),
147
182
  }),
148
183
  output: z.object({
149
184
  meta: z
@@ -153,7 +188,7 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
153
188
  next_cursor: z
154
189
  .string()
155
190
  .nullable()
156
- .describe('Cursor for next page of groups. null if no more groups.'),
191
+ .describe(`Upstream continuation cursor; null when OpenAlex supplies none. A non-null cursor does not guarantee unseen groups. ${KEY_ORDER_GUIDANCE}`),
157
192
  })
158
193
  .describe('Aggregation metadata.'),
159
194
  groups: z
@@ -164,7 +199,8 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
164
199
  .describe('Group key (OpenAlex ID or raw value), exactly as OpenAlex returns it.'),
165
200
  key_display_name: z
166
201
  .string()
167
- .describe('Human-readable group label as plain text, with HTML entities decoded and markup removed.'),
202
+ .nullable()
203
+ .describe('Human-readable group label as plain text, with HTML entities decoded and markup removed; null when OpenAlex supplies no label. Text output uses the group key when the label is null.'),
168
204
  count: z.number().describe('Number of entities in this group.'),
169
205
  is_unknown: z
170
206
  .literal(true)
@@ -180,14 +216,14 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
180
216
  enrichment: {
181
217
  echo: z
182
218
  .string()
183
- .describe('Compact echo of the input criteria (entity_type, group_by, filters) — surfaces what was actually requested when no groups are returned.'),
219
+ .describe('Compact echo of the criteria (entity_type, group_by, filters) and effective include_unknown and order settings — surfaces what was actually requested when no groups are returned.'),
184
220
  totalCount: z
185
221
  .number()
186
222
  .describe('Total entities matching the filters before grouping (across all pages).'),
187
223
  notice: z
188
224
  .string()
189
225
  .optional()
190
- .describe('Guidance notice. Set when a first call returns no groups (recovery suggestions), when a `cursor` continuation returns none because the traversal is already finished, when the page is full and more groups likely exist (truncation signal with narrowing advice), or when works are grouped by authorships.countries, which OpenAlex counts from only the first 100 authorships of each work. Absent otherwise.'),
226
+ .describe('Guidance notice. Set when per_page was clamped (requested and applied sizes), on every effective key-order call (upstream wrap warning and stopping conditions), when a first call returns no groups (recovery suggestions), when a cursor continuation returns none because the traversal is already finished, when a count-order page is full and more groups likely exist (truncation signal with narrowing advice), or when works are grouped by authorships.countries, which OpenAlex counts from only the first 100 authorships of each work. Absent otherwise.'),
191
227
  budget: z
192
228
  .object({
193
229
  costUsd: z
@@ -211,13 +247,14 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
211
247
  budget: { render: renderBudgetTrailer },
212
248
  },
213
249
  async handler(input, ctx) {
250
+ const pageSize = clampPageSize(input.per_page, 200, 'openalex_analyze_trends', ctx);
214
251
  const service = getOpenAlexService();
215
252
  const result = await service.analyze({
216
253
  entityType: input.entity_type,
217
254
  groupBy: input.group_by,
218
255
  filters: input.filters,
219
256
  includeUnknown: input.include_unknown,
220
- perPage: input.per_page,
257
+ perPage: pageSize.applied,
221
258
  order: input.order,
222
259
  cursor: input.cursor,
223
260
  }, ctx);
@@ -227,10 +264,13 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
227
264
  totalCount: result.meta.count,
228
265
  groupCount: result.groups.length,
229
266
  });
230
- const echo = buildAnalyzeEcho(input);
267
+ const keyOrder = input.cursor !== undefined || (input.order === 'key' && !isBooleanGroupBy(input.group_by));
268
+ const echo = buildAnalyzeEcho(input, keyOrder);
231
269
  ctx.enrich({ echo, totalCount: result.meta.count });
232
270
  // `notice` is last-wins, so every sentence is collected and set once.
233
271
  const notices = [];
272
+ if (pageSize.notice)
273
+ notices.push(pageSize.notice);
234
274
  // An empty page on a `cursor` continuation means the key-ascending traversal already
235
275
  // enumerated every group, so there is nothing left to remove or regroup. On a first call
236
276
  // the same shape can still be an honest zero — including the all-values-unknown case,
@@ -240,16 +280,13 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
240
280
  ? `No groups returned for ${echo}. Try removing filters or grouping by a different field.`
241
281
  : `Pagination exhausted for ${echo} — the previous page held the last group, so this one came back empty. Stop paging rather than removing filters or regrouping.`);
242
282
  }
243
- else if (input.order === 'key') {
244
- // Key-ascending traversal: omitted groups sit beyond the cursor, not in a count tail, so
245
- // the count-bound notice below would be false here (it would name the smallest keys as the
246
- // "top by count"). The honest signal is whether OpenAlex returned a next_cursor; page-fill
247
- // is irrelevant in this mode.
248
- if (result.meta.next_cursor) {
249
- notices.push(`Showing ${result.groups.length} groups in key-ascending order. Pass the returned \`next_cursor\` to continue the traversal.`);
250
- }
283
+ if (keyOrder) {
284
+ // A cursor selects key order regardless of `order`; upstream can wrap without suppressing it.
285
+ notices.push(result.groups.length === 0
286
+ ? KEY_ORDER_GUIDANCE
287
+ : `Showing ${result.groups.length} groups in key-ascending order. ${KEY_ORDER_GUIDANCE}${result.meta.next_cursor ? ' Otherwise, pass the returned `next_cursor` to continue the traversal.' : ''}`);
251
288
  }
252
- else if (result.groups.length === input.per_page) {
289
+ else if (result.groups.length > 0 && result.groups.length === pageSize.applied) {
253
290
  // Count-desc (the default): the page is filled to the limit, so more distinct groups likely
254
291
  // exist. The omitted groups all have counts ≤ the smallest group shown, so we can bound the
255
292
  // gap even though OpenAlex exposes no total group count.
@@ -279,11 +316,14 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
279
316
  },
280
317
  format: (result) => {
281
318
  const heading = `${result.meta.count} total entities across ${result.meta.groups_count ?? result.groups.length} groups on this page`;
319
+ const footer = result.meta.next_cursor
320
+ ? `\n\nUpstream next_cursor: \`${result.meta.next_cursor}\`. ${KEY_ORDER_GUIDANCE}`
321
+ : '';
282
322
  if (result.groups.length === 0) {
283
323
  return [
284
324
  {
285
325
  type: 'text',
286
- text: `No groups found. (count=${result.meta.count}, groups_count=${result.meta.groups_count ?? 0})`,
326
+ text: `No groups found. (count=${result.meta.count}, groups_count=${result.meta.groups_count ?? 0})${footer}`,
287
327
  },
288
328
  ];
289
329
  }
@@ -298,9 +338,6 @@ export const analyzeTrendsTool = tool('openalex_analyze_trends', {
298
338
  ]
299
339
  : result.groups;
300
340
  const lines = renderOrder.map((g) => `${renderGroupLabel(g)}: ${g.count}`);
301
- const footer = result.meta.next_cursor
302
- ? `\n\n*More groups available — next_cursor: \`${result.meta.next_cursor}\`*`
303
- : '';
304
341
  return [
305
342
  {
306
343
  type: 'text',
@@ -322,22 +359,30 @@ function isTimeSeriesGrouping(groups) {
322
359
  function renderGroupLabel(g) {
323
360
  const key = escapeMarkdown(g.key);
324
361
  if (g.is_unknown) {
325
- const redundant = g.key_display_name === g.key || g.key_display_name.toLowerCase() === 'unknown';
326
- const label = redundant ? '' : `, label ${escapeMarkdown(g.key_display_name)}`;
362
+ const label = g.key_display_name === null ||
363
+ g.key_display_name === g.key ||
364
+ g.key_display_name.toLowerCase() === 'unknown'
365
+ ? ''
366
+ : `, label ${escapeMarkdown(g.key_display_name)}`;
327
367
  return `unknown (no value; key ${key}${label})`;
328
368
  }
369
+ if (g.key_display_name === null)
370
+ return escapeMarkdown(g.key, 'line-start');
329
371
  const display = escapeMarkdown(g.key_display_name, 'line-start');
330
372
  return g.key === g.key_display_name ? display : `${display} (${key})`;
331
373
  }
332
- function buildAnalyzeEcho(input) {
333
- const parts = [`entity_type=${input.entity_type}`, `group_by=${input.group_by}`];
374
+ /** The request as one line: caller text is JSON-encoded, so a line break in it stays escaped. */
375
+ function buildAnalyzeEcho(input, keyOrder) {
376
+ const parts = [`entity_type=${input.entity_type}`, `group_by=${JSON.stringify(input.group_by)}`];
334
377
  if (input.filters && Object.keys(input.filters).length > 0) {
335
378
  parts.push(`filters=${JSON.stringify(input.filters)}`);
336
379
  }
337
- if (input.include_unknown)
380
+ if (input.include_unknown || input.group_by.endsWith(':include_unknown'))
338
381
  parts.push('include_unknown=true');
339
- if (input.order)
340
- parts.push(`order=${input.order}`);
382
+ if (keyOrder)
383
+ parts.push('order=key');
384
+ else if (input.order)
385
+ parts.push('order=count');
341
386
  return parts.join(' | ');
342
387
  }
343
388
  //# sourceMappingURL=analyze-trends.tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-trends.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze-trends.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,7 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
14
14
  filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
15
15
  sort: z.ZodOptional<z.ZodString>;
16
16
  select: z.ZodOptional<z.ZodArray<z.ZodString>>;
17
- per_page: z.ZodDefault<z.ZodNumber>;
17
+ per_page: z.ZodDefault<z.ZodLazy<z.ZodNumber>>;
18
18
  cursor: z.ZodOptional<z.ZodString>;
19
19
  }, z.core.$strip>, z.ZodObject<{
20
20
  meta: z.ZodObject<{
@@ -47,6 +47,12 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
47
47
  }, z.core.$strip>>>;
48
48
  over_budget: z.ZodOptional<z.ZodBoolean>;
49
49
  }, z.core.$strip>, readonly [{
50
+ readonly reason: "unmatched_quote_in_filter_value";
51
+ readonly code: JsonRpcErrorCode.ValidationError;
52
+ readonly when: "A filter value sent unquoted has an unmatched double quote.";
53
+ readonly recovery: "Pair double quotes used for a search phrase, or remove the stray quote.";
54
+ readonly thrownBy: "service";
55
+ }, {
50
56
  readonly reason: "rate_limited";
51
57
  readonly code: JsonRpcErrorCode.RateLimited;
52
58
  readonly when: "OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429).";
@@ -60,6 +66,13 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
60
66
  readonly retryable: false;
61
67
  readonly recovery: "This server's daily OpenAlex budget is spent and refills at midnight UTC — retrying before then will not succeed.";
62
68
  readonly thrownBy: "service";
69
+ }, {
70
+ readonly reason: "upstream_plan_required";
71
+ readonly code: JsonRpcErrorCode.Forbidden;
72
+ 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\").";
73
+ readonly retryable: false;
74
+ 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.";
75
+ readonly thrownBy: "service";
63
76
  }, {
64
77
  readonly reason: "upstream_timeout";
65
78
  readonly code: JsonRpcErrorCode.Timeout;
@@ -89,14 +102,32 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
89
102
  }, {
90
103
  readonly reason: "comma_in_filter_value";
91
104
  readonly code: JsonRpcErrorCode.InvalidParams;
92
- readonly when: "A filter value contains a comma, which collides with the OpenAlex filter separator.";
93
- 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.";
105
+ 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, authorships.institutions.geo.city, authorships.institutions.geo.region, raw_affiliation_strings, biblio fields) are exempt: a comma there is sent quoted, as part of the phrase.";
106
+ 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.";
107
+ readonly thrownBy: "service";
108
+ }, {
109
+ readonly reason: "invalid_country_code";
110
+ readonly code: JsonRpcErrorCode.ValidationError;
111
+ 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\").";
112
+ 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 works with no country.";
113
+ readonly thrownBy: "service";
114
+ }, {
115
+ readonly reason: "mixed_and_or_in_filter_value";
116
+ readonly code: JsonRpcErrorCode.ValidationError;
117
+ 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.";
118
+ 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 — {\"authorships.institutions.country_code\": \"DE|FR\", \"institutions.country_code\": \"US\"} matches works with (DE or FR) and US. The filters object holds each key once, so where no second key fits, run one call per alternative instead.";
119
+ readonly thrownBy: "service";
120
+ }, {
121
+ readonly reason: "quote_in_filter_value";
122
+ readonly code: JsonRpcErrorCode.ValidationError;
123
+ 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.";
124
+ readonly recovery: "Remove the double quote from the value; a phrase is matched without quoting its own text.";
94
125
  readonly thrownBy: "service";
95
126
  }, {
96
127
  readonly reason: "upstream_invalid_params";
97
128
  readonly code: JsonRpcErrorCode.InvalidParams;
98
- readonly when: "OpenAlex rejected an invalid filter or sort field name (HTTP 400).";
99
- readonly recovery: "The upstream message names the rejected token and suggests close matches. Pass a valid OpenAlex work ID (W…), DOI, or PMID for seed_id, or use openalex_describe_fields(entity_type, \"filter\") to browse valid filter fields.";
129
+ readonly when: "OpenAlex rejected an invalid filter, select, or sort field name (HTTP 400).";
130
+ readonly recovery: "The upstream message names the rejected token and suggests close matches. Pass a valid OpenAlex work ID (W…), DOI, or PMID for seed_id, and browse the valid work fields with openalex_describe_fields(\"works\", \"filter\") for filters or openalex_describe_fields(\"works\", \"select\") for select.";
100
131
  readonly thrownBy: "service";
101
132
  }, {
102
133
  readonly reason: "upstream_invalid_id_value";
@@ -121,6 +152,18 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
121
152
  readonly code: JsonRpcErrorCode.ValidationError;
122
153
  readonly when: "filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys.";
123
154
  readonly recovery: "Remove the reserved key from filters, or restate the relationship through direction.";
155
+ }, {
156
+ readonly reason: "id_type_mismatch";
157
+ readonly code: JsonRpcErrorCode.ValidationError;
158
+ readonly when: "seed_id is a native OpenAlex ID of another entity type — an author, source, institution, topic, publisher, funder, or concept ID rather than a work ID.";
159
+ readonly recovery: "seed_id must identify a work — pass the work's OpenAlex ID (W…), DOI, or PMID. To walk the works of an author or other entity, list them first with openalex_search_entities filters.";
160
+ readonly thrownBy: "service";
161
+ }, {
162
+ readonly reason: "id_dot_segment";
163
+ readonly code: JsonRpcErrorCode.ValidationError;
164
+ readonly when: "seed_id holds a `.` or `..` path segment, written plainly or percent-encoded.";
165
+ readonly recovery: "Pass the work's identifier itself — its OpenAlex ID (W…), DOI, or PMID — with no relative path segments.";
166
+ readonly thrownBy: "service";
124
167
  }, {
125
168
  readonly reason: "entity_not_found";
126
169
  readonly code: JsonRpcErrorCode.NotFound;
@@ -1 +1 @@
1
- {"version":3,"file":"citation-graph.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/citation-graph.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAoCjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqW/B,CAAC"}
1
+ {"version":3,"file":"citation-graph.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/citation-graph.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA2CjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6Z/B,CAAC"}