@cyanheads/openalex-mcp-server 0.7.14 → 0.7.16
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.
- package/AGENTS.md +8 -4
- package/CLAUDE.md +8 -4
- package/README.md +12 -9
- package/changelog/0.7.x/0.7.15.md +33 -0
- package/changelog/0.7.x/0.7.16.md +32 -0
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.js +1 -1
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.js.map +1 -1
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts +22 -2
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.js +71 -17
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts +12 -1
- package/dist/mcp-server/tools/definitions/citation-graph.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/citation-graph.tool.js +56 -39
- package/dist/mcp-server/tools/definitions/citation-graph.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/describe-fields.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/describe-fields.tool.js +82 -15
- package/dist/mcp-server/tools/definitions/describe-fields.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts +18 -0
- package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/resolve-name.tool.js +29 -7
- package/dist/mcp-server/tools/definitions/resolve-name.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +50 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-entities.tool.js +176 -41
- package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -1
- package/dist/mcp-server/tools/escape-markdown.d.ts +32 -0
- package/dist/mcp-server/tools/escape-markdown.d.ts.map +1 -0
- package/dist/mcp-server/tools/escape-markdown.js +389 -0
- package/dist/mcp-server/tools/escape-markdown.js.map +1 -0
- package/dist/mcp-server/tools/render-entity-record.d.ts +2 -1
- package/dist/mcp-server/tools/render-entity-record.d.ts.map +1 -1
- package/dist/mcp-server/tools/render-entity-record.js +24 -8
- package/dist/mcp-server/tools/render-entity-record.js.map +1 -1
- package/dist/services/openalex/html-entities.json +2237 -0
- package/dist/services/openalex/openalex-service.d.ts +17 -1
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +211 -101
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/dist/services/openalex/provider-text.d.ts +20 -0
- package/dist/services/openalex/provider-text.d.ts.map +1 -0
- package/dist/services/openalex/provider-text.js +276 -0
- package/dist/services/openalex/provider-text.js.map +1 -0
- package/dist/services/openalex/types.d.ts +16 -0
- package/dist/services/openalex/types.d.ts.map +1 -1
- package/dist/services/openalex/types.js +7 -0
- package/dist/services/openalex/types.js.map +1 -1
- package/package.json +6 -3
- package/server.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-trends.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,yBAAyB,EAAE;IAC/D,WAAW,EACT,oXAAoX;IACtX,SAAS,EACP,oHAAoH;IACtH,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAC9E,MAAM,EAAE;QACN;YACE,MAAM,EAAE,cAAc;YACtB,IAAI,EAAE,gBAAgB,CAAC,WAAW;YAClC,IAAI,EAAE,iFAAiF;YACvF,SAAS,EAAE,IAAI;YACf,QAAQ,EACN,sFAAsF;
|
|
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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"}
|
|
@@ -32,63 +32,74 @@ export declare const getCitationGraphTool: import("@cyanheads/mcp-ts-core").Tool
|
|
|
32
32
|
readonly when: "OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429).";
|
|
33
33
|
readonly retryable: true;
|
|
34
34
|
readonly recovery: "Wait several seconds and retry; consider lowering request frequency for this caller.";
|
|
35
|
+
readonly thrownBy: "service";
|
|
35
36
|
}, {
|
|
36
37
|
readonly reason: "upstream_budget_exhausted";
|
|
37
38
|
readonly code: JsonRpcErrorCode.RateLimited;
|
|
38
39
|
readonly when: "The OpenAlex daily usage budget is spent (HTTP 429).";
|
|
39
40
|
readonly retryable: false;
|
|
40
41
|
readonly recovery: "The daily budget refills at midnight UTC — retrying sooner will not succeed. Set OPENALEX_API_KEY to a free key (https://openalex.org/settings/api) for a larger daily budget than anonymous access, or wait for the reset.";
|
|
42
|
+
readonly thrownBy: "service";
|
|
41
43
|
}, {
|
|
42
44
|
readonly reason: "upstream_timeout";
|
|
43
45
|
readonly code: JsonRpcErrorCode.Timeout;
|
|
44
46
|
readonly when: "OpenAlex did not respond within the request deadline.";
|
|
45
47
|
readonly retryable: true;
|
|
46
48
|
readonly recovery: "Retry after a short delay; if timeouts persist, narrow the request with tighter filters to reduce upstream load.";
|
|
49
|
+
readonly thrownBy: "service";
|
|
47
50
|
}, {
|
|
48
51
|
readonly reason: "upstream_unavailable";
|
|
49
52
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
50
53
|
readonly when: "OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON.";
|
|
51
54
|
readonly retryable: true;
|
|
52
55
|
readonly recovery: "Wait and retry; check https://openalex.org for service status if the outage persists.";
|
|
56
|
+
readonly thrownBy: "service";
|
|
53
57
|
}, {
|
|
54
58
|
readonly reason: "upstream_unauthorized";
|
|
55
59
|
readonly code: JsonRpcErrorCode.Unauthorized;
|
|
56
60
|
readonly when: "OpenAlex rejected the API key (HTTP 401).";
|
|
57
61
|
readonly recovery: "Check that OPENALEX_API_KEY is set to a valid OpenAlex account API key (free from https://openalex.org/settings/api).";
|
|
62
|
+
readonly thrownBy: "service";
|
|
58
63
|
}, {
|
|
59
64
|
readonly reason: "upstream_forbidden";
|
|
60
65
|
readonly code: JsonRpcErrorCode.Forbidden;
|
|
61
66
|
readonly when: "OpenAlex denied access to the requested resource (HTTP 403).";
|
|
62
67
|
readonly recovery: "Confirm the API key has access to this entity type or endpoint, then retry the request.";
|
|
68
|
+
readonly thrownBy: "service";
|
|
63
69
|
}, {
|
|
64
70
|
readonly reason: "comma_in_filter_value";
|
|
65
71
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
66
72
|
readonly when: "A filter value contains a comma, which collides with the OpenAlex filter separator.";
|
|
67
73
|
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.";
|
|
74
|
+
readonly thrownBy: "service";
|
|
68
75
|
}, {
|
|
69
76
|
readonly reason: "upstream_invalid_params";
|
|
70
77
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
71
78
|
readonly when: "OpenAlex rejected an invalid filter or sort field name (HTTP 400).";
|
|
72
79
|
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.";
|
|
80
|
+
readonly thrownBy: "service";
|
|
73
81
|
}, {
|
|
74
82
|
readonly reason: "upstream_invalid_id_value";
|
|
75
83
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
76
84
|
readonly when: "An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400).";
|
|
77
85
|
readonly recovery: "Call openalex_resolve_name to turn the name into an OpenAlex ID, then filter by that ID. Entity filters such as authorships.author.id and primary_topic.id accept IDs only.";
|
|
86
|
+
readonly thrownBy: "service";
|
|
78
87
|
}, {
|
|
79
88
|
readonly reason: "upstream_sort_requires_search";
|
|
80
89
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
81
90
|
readonly when: "sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400).";
|
|
82
91
|
readonly recovery: "Sorting by relevance_score requires an active search, which a citation-graph walk lacks — choose a concrete sort field such as -cited_by_count or -publication_date, or add a `*.search` filter.";
|
|
92
|
+
readonly thrownBy: "service";
|
|
83
93
|
}, {
|
|
84
94
|
readonly reason: "upstream_invalid_params_other";
|
|
85
95
|
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
86
96
|
readonly when: "OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name.";
|
|
87
97
|
readonly recovery: "Read the upstream message in the error above and adjust the request — check filter operators, value formats, and cursor/per_page bounds.";
|
|
98
|
+
readonly thrownBy: "service";
|
|
88
99
|
}, {
|
|
89
100
|
readonly reason: "reserved_filter_key";
|
|
90
101
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
91
|
-
readonly when: "filters contains cites/cited_by/related_to — the direction parameter reserves those keys.";
|
|
102
|
+
readonly when: "filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys.";
|
|
92
103
|
readonly recovery: "Remove the reserved key from filters, or restate the relationship through direction.";
|
|
93
104
|
}, {
|
|
94
105
|
readonly reason: "entity_not_found";
|
|
@@ -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;
|
|
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;AA2BjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqW/B,CAAC"}
|
|
@@ -6,39 +6,10 @@ import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
|
6
6
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
7
|
import { renderBudgetTrailer } from '../render-budget.js';
|
|
8
8
|
import { renderEntityRecord } from '../render-entity-record.js';
|
|
9
|
-
import { getOpenAlexService } from '../../../services/openalex/openalex-service.js';
|
|
9
|
+
import { getOpenAlexService, translateFilterKey } from '../../../services/openalex/openalex-service.js';
|
|
10
10
|
const OPENALEX_URL_PREFIX = 'https://openalex.org/';
|
|
11
|
-
/**
|
|
12
|
-
* Resolve any accepted seed identifier to a bare W-ID and confirm it exists. The graph
|
|
13
|
-
* filters (`cites`/`cited_by`/`related_to`) themselves don't validate the seed — passing
|
|
14
|
-
* a non-existent W-ID returns zero edges, indistinguishable from a valid seed with no
|
|
15
|
-
* citations yet. A singleton `/works/{id}` lookup gates the query so bad seeds surface
|
|
16
|
-
* as NotFound (bubbles via the service as `entity_not_found`) and good seeds with empty
|
|
17
|
-
* graphs return honest empty results.
|
|
18
|
-
*/
|
|
19
|
-
async function resolveSeedToWorkId(service, seedId, ctx) {
|
|
20
|
-
const lookup = await service.search({ entityType: 'works', id: seedId, select: ['id'] }, ctx);
|
|
21
|
-
const record = lookup.results[0];
|
|
22
|
-
if (!record?.id) {
|
|
23
|
-
throw ctx.fail('entity_not_found', `Could not resolve seed_id "${seedId}" to an OpenAlex work ID.`, { ...ctx.recoveryFor('entity_not_found'), seedId });
|
|
24
|
-
}
|
|
25
|
-
return record.id.replace(OPENALEX_URL_PREFIX, '');
|
|
26
|
-
}
|
|
27
11
|
const DIRECTIONS = ['cites', 'cited_by', 'related_to'];
|
|
28
12
|
const RESERVED_FILTER_KEYS = new Set(DIRECTIONS);
|
|
29
|
-
/** Reject a `filters` key that `direction` owns — merging it would silently overwrite one. */
|
|
30
|
-
function assertNoReservedFilterKey(input, ctx) {
|
|
31
|
-
if (!input.filters)
|
|
32
|
-
return;
|
|
33
|
-
const reserved = Object.keys(input.filters).find((key) => RESERVED_FILTER_KEYS.has(key));
|
|
34
|
-
if (reserved === undefined)
|
|
35
|
-
return;
|
|
36
|
-
throw ctx.fail('reserved_filter_key', `${reserved} cannot be passed in filters — direction reserves cites/cited_by/related_to.`, {
|
|
37
|
-
...ctx.recoveryFor('reserved_filter_key'),
|
|
38
|
-
reservedKey: reserved,
|
|
39
|
-
direction: input.direction,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
13
|
function buildCitationEcho(input) {
|
|
43
14
|
const parts = [`seed_id=${input.seed_id}`, `direction=${input.direction}`];
|
|
44
15
|
if (input.filters && Object.keys(input.filters).length > 0) {
|
|
@@ -59,6 +30,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
59
30
|
when: 'OpenAlex throttled the request for exceeding its per-second ceiling (HTTP 429).',
|
|
60
31
|
retryable: true,
|
|
61
32
|
recovery: 'Wait several seconds and retry; consider lowering request frequency for this caller.',
|
|
33
|
+
thrownBy: 'service',
|
|
62
34
|
},
|
|
63
35
|
{
|
|
64
36
|
reason: 'upstream_budget_exhausted',
|
|
@@ -66,6 +38,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
66
38
|
when: 'The OpenAlex daily usage budget is spent (HTTP 429).',
|
|
67
39
|
retryable: false,
|
|
68
40
|
recovery: 'The daily budget refills at midnight UTC — retrying sooner will not succeed. Set OPENALEX_API_KEY to a free key (https://openalex.org/settings/api) for a larger daily budget than anonymous access, or wait for the reset.',
|
|
41
|
+
thrownBy: 'service',
|
|
69
42
|
},
|
|
70
43
|
{
|
|
71
44
|
reason: 'upstream_timeout',
|
|
@@ -73,6 +46,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
73
46
|
when: 'OpenAlex did not respond within the request deadline.',
|
|
74
47
|
retryable: true,
|
|
75
48
|
recovery: 'Retry after a short delay; if timeouts persist, narrow the request with tighter filters to reduce upstream load.',
|
|
49
|
+
thrownBy: 'service',
|
|
76
50
|
},
|
|
77
51
|
{
|
|
78
52
|
reason: 'upstream_unavailable',
|
|
@@ -80,53 +54,61 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
80
54
|
when: 'OpenAlex was unreachable or unusable — HTTP 503, a connection failure, or a body that was empty, HTML, or unparseable JSON.',
|
|
81
55
|
retryable: true,
|
|
82
56
|
recovery: 'Wait and retry; check https://openalex.org for service status if the outage persists.',
|
|
57
|
+
thrownBy: 'service',
|
|
83
58
|
},
|
|
84
59
|
{
|
|
85
60
|
reason: 'upstream_unauthorized',
|
|
86
61
|
code: JsonRpcErrorCode.Unauthorized,
|
|
87
62
|
when: 'OpenAlex rejected the API key (HTTP 401).',
|
|
88
63
|
recovery: 'Check that OPENALEX_API_KEY is set to a valid OpenAlex account API key (free from https://openalex.org/settings/api).',
|
|
64
|
+
thrownBy: 'service',
|
|
89
65
|
},
|
|
90
66
|
{
|
|
91
67
|
reason: 'upstream_forbidden',
|
|
92
68
|
code: JsonRpcErrorCode.Forbidden,
|
|
93
69
|
when: 'OpenAlex denied access to the requested resource (HTTP 403).',
|
|
94
70
|
recovery: 'Confirm the API key has access to this entity type or endpoint, then retry the request.',
|
|
71
|
+
thrownBy: 'service',
|
|
95
72
|
},
|
|
96
73
|
{
|
|
97
74
|
reason: 'comma_in_filter_value',
|
|
98
75
|
code: JsonRpcErrorCode.InvalidParams,
|
|
99
76
|
when: 'A filter value contains a comma, which collides with the OpenAlex filter separator.',
|
|
100
77
|
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.',
|
|
78
|
+
thrownBy: 'service',
|
|
101
79
|
},
|
|
102
80
|
{
|
|
103
81
|
reason: 'upstream_invalid_params',
|
|
104
82
|
code: JsonRpcErrorCode.InvalidParams,
|
|
105
83
|
when: 'OpenAlex rejected an invalid filter or sort field name (HTTP 400).',
|
|
106
84
|
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.',
|
|
85
|
+
thrownBy: 'service',
|
|
107
86
|
},
|
|
108
87
|
{
|
|
109
88
|
reason: 'upstream_invalid_id_value',
|
|
110
89
|
code: JsonRpcErrorCode.InvalidParams,
|
|
111
90
|
when: 'An entity-ID filter received a value that is not an OpenAlex ID — usually a name (HTTP 400).',
|
|
112
91
|
recovery: 'Call openalex_resolve_name to turn the name into an OpenAlex ID, then filter by that ID. Entity filters such as authorships.author.id and primary_topic.id accept IDs only.',
|
|
92
|
+
thrownBy: 'service',
|
|
113
93
|
},
|
|
114
94
|
{
|
|
115
95
|
reason: 'upstream_sort_requires_search',
|
|
116
96
|
code: JsonRpcErrorCode.InvalidParams,
|
|
117
97
|
when: 'sort=-relevance_score was used but the citation-graph query has no active search (HTTP 400).',
|
|
118
98
|
recovery: 'Sorting by relevance_score requires an active search, which a citation-graph walk lacks — choose a concrete sort field such as -cited_by_count or -publication_date, or add a `*.search` filter.',
|
|
99
|
+
thrownBy: 'service',
|
|
119
100
|
},
|
|
120
101
|
{
|
|
121
102
|
reason: 'upstream_invalid_params_other',
|
|
122
103
|
code: JsonRpcErrorCode.InvalidParams,
|
|
123
104
|
when: 'OpenAlex rejected the request (HTTP 400) for a reason other than an invalid field name.',
|
|
124
105
|
recovery: 'Read the upstream message in the error above and adjust the request — check filter operators, value formats, and cursor/per_page bounds.',
|
|
106
|
+
thrownBy: 'service',
|
|
125
107
|
},
|
|
126
108
|
{
|
|
127
109
|
reason: 'reserved_filter_key',
|
|
128
110
|
code: JsonRpcErrorCode.ValidationError,
|
|
129
|
-
when: 'filters contains cites/cited_by/related_to — the direction parameter reserves those keys.',
|
|
111
|
+
when: 'filters contains cites/cited_by/related_to, or an alias of one such as cited_works — the direction parameter reserves those keys.',
|
|
130
112
|
recovery: 'Remove the reserved key from filters, or restate the relationship through direction.',
|
|
131
113
|
},
|
|
132
114
|
{
|
|
@@ -148,7 +130,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
148
130
|
filters: z
|
|
149
131
|
.record(z.string(), z.string())
|
|
150
132
|
.optional()
|
|
151
|
-
.describe('Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=">2020", is_oa="true". Do not include cites/cited_by/related_to — those are set by the `direction` parameter.'),
|
|
133
|
+
.describe('Additional filters to narrow the graph, same syntax as openalex_search_entities. Example: publication_year=">2020", is_oa="true". Do not include cites/cited_by/related_to, nor an alias of one such as cited_works — those keys are set by the `direction` parameter.'),
|
|
152
134
|
sort: z
|
|
153
135
|
.string()
|
|
154
136
|
.optional()
|
|
@@ -166,8 +148,9 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
166
148
|
.describe('Results per page (1-100). Default 25.'),
|
|
167
149
|
cursor: z
|
|
168
150
|
.string()
|
|
151
|
+
.min(1)
|
|
169
152
|
.optional()
|
|
170
|
-
.describe('Pagination cursor from a previous response. Pass to get the next page.'),
|
|
153
|
+
.describe('Pagination cursor from a previous response. Pass to get the next page. 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.'),
|
|
171
154
|
}),
|
|
172
155
|
output: z.object({
|
|
173
156
|
meta: z
|
|
@@ -175,7 +158,9 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
175
158
|
count: z
|
|
176
159
|
.number()
|
|
177
160
|
.describe('Total edges from seed_id in this direction (across all pages).'),
|
|
178
|
-
per_page: z
|
|
161
|
+
per_page: z
|
|
162
|
+
.number()
|
|
163
|
+
.describe('Page size OpenAlex echoed for this request — the requested per_page, not the number of records returned. A short or exhausted page carries fewer records than this.'),
|
|
179
164
|
next_cursor: z
|
|
180
165
|
.string()
|
|
181
166
|
.nullable()
|
|
@@ -193,7 +178,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
193
178
|
})
|
|
194
179
|
.passthrough()
|
|
195
180
|
.describe('A single OpenAlex work record on the citation graph. Additional fields vary by `select`.'))
|
|
196
|
-
.describe('Works on the citation graph in this direction.'),
|
|
181
|
+
.describe('Works on the citation graph in this direction. Text values are plain text — HTML entities decoded, HTML/JATS/MathML markup removed.'),
|
|
197
182
|
}),
|
|
198
183
|
// Agent-facing context for the success path — the query as parsed (seed + direction),
|
|
199
184
|
// the total edge count, and recovery guidance for empty traversals. Populated via
|
|
@@ -206,7 +191,7 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
206
191
|
notice: z
|
|
207
192
|
.string()
|
|
208
193
|
.optional()
|
|
209
|
-
.describe('
|
|
194
|
+
.describe('Guidance when no edges are returned. A first call suggests verifying the seed_id, broadening filters, or trying a different direction; a `cursor` continuation says the walk is already past its last edge instead. Absent when results are present.'),
|
|
210
195
|
budget: z
|
|
211
196
|
.object({
|
|
212
197
|
costUsd: z
|
|
@@ -230,9 +215,37 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
230
215
|
budget: { render: renderBudgetTrailer },
|
|
231
216
|
},
|
|
232
217
|
async handler(input, ctx) {
|
|
233
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Reject a `filters` key that `direction` owns — merging it would silently overwrite one.
|
|
220
|
+
* The check runs on the *translated* key, not the raw one: `cited_works` is an alias of
|
|
221
|
+
* `cites`, so reading the caller's spelling alone let it past the guard and the collision
|
|
222
|
+
* happened a layer down, where the direction won and the caller's filter disappeared.
|
|
223
|
+
*/
|
|
224
|
+
const reserved = Object.keys(input.filters ?? {}).find((key) => RESERVED_FILTER_KEYS.has(translateFilterKey('works', key)));
|
|
225
|
+
if (reserved !== undefined) {
|
|
226
|
+
const upstreamKey = translateFilterKey('works', reserved);
|
|
227
|
+
const resolution = upstreamKey === reserved ? '' : ` — it resolves to \`${upstreamKey}\``;
|
|
228
|
+
throw ctx.fail('reserved_filter_key', `${reserved} cannot be passed in filters${resolution}, and direction reserves cites/cited_by/related_to.`, {
|
|
229
|
+
...ctx.recoveryFor('reserved_filter_key'),
|
|
230
|
+
reservedKey: reserved,
|
|
231
|
+
direction: input.direction,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
234
|
const service = getOpenAlexService();
|
|
235
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Resolve any accepted seed identifier to a bare W-ID and confirm it exists. The graph
|
|
237
|
+
* filters (`cites`/`cited_by`/`related_to`) themselves don't validate the seed — passing a
|
|
238
|
+
* non-existent W-ID returns zero edges, indistinguishable from a valid seed with no citations
|
|
239
|
+
* yet. A singleton `/works/{id}` lookup gates the query so bad seeds surface as NotFound
|
|
240
|
+
* (a 404 bubbles from the service as `entity_not_found` too) and good seeds with empty graphs
|
|
241
|
+
* return honest empty results.
|
|
242
|
+
*/
|
|
243
|
+
const seedLookup = await service.search({ entityType: 'works', id: input.seed_id, select: ['id'] }, ctx);
|
|
244
|
+
const seedRecord = seedLookup.results[0];
|
|
245
|
+
if (!seedRecord?.id) {
|
|
246
|
+
throw ctx.fail('entity_not_found', `Could not resolve seed_id "${input.seed_id}" to an OpenAlex work ID.`, { ...ctx.recoveryFor('entity_not_found'), seedId: input.seed_id });
|
|
247
|
+
}
|
|
248
|
+
const workId = seedRecord.id.replace(OPENALEX_URL_PREFIX, '');
|
|
236
249
|
const mergedFilters = {
|
|
237
250
|
...(input.filters ?? {}),
|
|
238
251
|
[input.direction]: workId,
|
|
@@ -253,8 +266,12 @@ export const getCitationGraphTool = tool('openalex_get_citation_graph', {
|
|
|
253
266
|
});
|
|
254
267
|
const echo = buildCitationEcho(input);
|
|
255
268
|
ctx.enrich({ echo, totalCount: result.meta.count });
|
|
269
|
+
// An empty page on a `cursor` continuation means the walk already returned every edge it
|
|
270
|
+
// had, so the verify-the-seed advice would send the caller after a seed that is fine.
|
|
256
271
|
if (result.results.length === 0) {
|
|
257
|
-
ctx.enrich.notice(
|
|
272
|
+
ctx.enrich.notice(input.cursor === undefined
|
|
273
|
+
? `No edges for ${echo}. Verify the seed_id with openalex_resolve_name, broaden filters, or try a different direction.`
|
|
274
|
+
: `Pagination exhausted for ${echo} — the previous page held the last edge, so this one came back empty. Stop paging rather than broadening filters or changing direction.`);
|
|
258
275
|
}
|
|
259
276
|
return {
|
|
260
277
|
meta: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"citation-graph.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/citation-graph.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"citation-graph.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/citation-graph.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-fields.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/describe-fields.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"describe-fields.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/describe-fields.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AA4GjD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;wCAoE7B,CAAC"}
|
|
@@ -19,27 +19,92 @@ const NON_GROUPABLE_DATE_FIELDS = new Set([
|
|
|
19
19
|
'updated_date',
|
|
20
20
|
]);
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* Filter fields OpenAlex answers with an HTTP 400 as a group_by target, beyond the rule-based
|
|
23
|
+
* exclusions in `isGroupableField`. Taken from a live sweep that grouped every field this view
|
|
24
|
+
* listed on all eight entity types (2026-09-23): "Cannot group by date, number, or search
|
|
25
|
+
* fields." (decimal scores, several source year fields, `topics_count`), "Cannot group by
|
|
26
|
+
* <field>." (`display_name`, external-ID and page fields), "Group by referenced_works is not
|
|
27
|
+
* supported at this time.", and — for the three concept keys on authors — an invalid-ID 400.
|
|
28
|
+
*
|
|
29
|
+
* Keyed by entity type because the same name can group on one type and fail on another
|
|
30
|
+
* (`concepts.id` fails on authors and groups on works). The catalog stores names only, and name
|
|
31
|
+
* shape does not predict the outcome — `summary_stats.2yr_mean_citedness` and `publication_year`
|
|
32
|
+
* group while `fwci` and sources' `first_publication_year` do not — so the sweep is the rule.
|
|
33
|
+
* Re-run it after regenerating the field catalog.
|
|
27
34
|
*/
|
|
28
|
-
const
|
|
35
|
+
const GROUP_BY_REJECTED_FIELDS = {
|
|
36
|
+
works: new Set([
|
|
37
|
+
'biblio.first_page',
|
|
38
|
+
'biblio.last_page',
|
|
39
|
+
'citation_normalized_percentile.value',
|
|
40
|
+
'cited_by',
|
|
41
|
+
'display_name',
|
|
42
|
+
'doi',
|
|
43
|
+
'fwci',
|
|
44
|
+
'ids.mag',
|
|
45
|
+
'ids.pmcid',
|
|
46
|
+
'ids.pmid',
|
|
47
|
+
'mag',
|
|
48
|
+
'pmcid',
|
|
49
|
+
'pmid',
|
|
50
|
+
'referenced_works',
|
|
51
|
+
'related_to',
|
|
52
|
+
'sustainable_development_goals.score',
|
|
53
|
+
'topics_count',
|
|
54
|
+
]),
|
|
55
|
+
authors: new Set(['concept.id', 'concepts.id', 'display_name', 'x_concepts.id']),
|
|
56
|
+
sources: new Set([
|
|
57
|
+
'apc_prices.price',
|
|
58
|
+
'display_name',
|
|
59
|
+
'first_publication_year',
|
|
60
|
+
'ids.mag',
|
|
61
|
+
'is_high_oa_rate_since_year',
|
|
62
|
+
'is_in_doaj_since_year',
|
|
63
|
+
'is_in_jstage_since_year',
|
|
64
|
+
'last_publication_year',
|
|
65
|
+
'oa_flip_year',
|
|
66
|
+
]),
|
|
67
|
+
institutions: new Set(['display_name']),
|
|
68
|
+
topics: new Set(['display_name']),
|
|
69
|
+
keywords: new Set(['display_name']),
|
|
70
|
+
publishers: new Set(['display_name']),
|
|
71
|
+
funders: new Set(['display_name']),
|
|
72
|
+
};
|
|
29
73
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
74
|
+
* Fields OpenAlex lists as valid filters but answers with an HTTP 500 on every group_by attempt
|
|
75
|
+
* — an upstream defect rather than a documented restriction. A 500 classifies as
|
|
76
|
+
* ServiceUnavailable, whose recovery tells the caller to wait and retry, so advertising these as
|
|
77
|
+
* groupable sends an agent into a loop that never clears. Fields that 500 only intermittently
|
|
78
|
+
* and group on a retry (`openalex`, `authorships.author.id`) stay listed. Drop an entry once
|
|
79
|
+
* upstream starts answering it.
|
|
34
80
|
*/
|
|
35
|
-
|
|
81
|
+
const GROUP_BY_UPSTREAM_FAILURES = {
|
|
82
|
+
works: new Set([
|
|
83
|
+
'best_oa_location.raw_type',
|
|
84
|
+
'cites',
|
|
85
|
+
'has_embeddings',
|
|
86
|
+
'locations.raw_type',
|
|
87
|
+
'primary_location.raw_type',
|
|
88
|
+
]),
|
|
89
|
+
sources: new Set(['is_preprint_repository']),
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Whether a filter field of `entityType` is also a valid group_by target. group_by is a subset of
|
|
93
|
+
* filter: OpenAlex rejects (HTTP 400) the `*.search`/`*.search.exact` text operators, the
|
|
94
|
+
* `from_*`/`to_*` range-modifier directives, the raw date fields, and the per-type sweep rejects
|
|
95
|
+
* above — all valid as filters but not as aggregation keys. Most integer count fields ARE
|
|
96
|
+
* groupable (bucketed) and are intentionally kept.
|
|
97
|
+
*/
|
|
98
|
+
function isGroupableField(entityType, field) {
|
|
36
99
|
if (field.endsWith('.search') || field.endsWith('.search.exact'))
|
|
37
100
|
return false;
|
|
38
101
|
if (field.startsWith('from_') || field.startsWith('to_'))
|
|
39
102
|
return false;
|
|
40
|
-
if (
|
|
103
|
+
if (NON_GROUPABLE_DATE_FIELDS.has(field))
|
|
104
|
+
return false;
|
|
105
|
+
if (GROUP_BY_REJECTED_FIELDS[entityType].has(field))
|
|
41
106
|
return false;
|
|
42
|
-
return !
|
|
107
|
+
return !GROUP_BY_UPSTREAM_FAILURES[entityType]?.has(field);
|
|
43
108
|
}
|
|
44
109
|
export const describeFieldsTool = tool('openalex_describe_fields', {
|
|
45
110
|
description: 'List valid field names for an OpenAlex entity type and context (filter, group_by, or select). Use proactively before constructing a filter or group_by to avoid invalid-field 400 errors. Pass `query` to rank the list by name similarity — useful when you have a partial or guessed field name. Ranking never drops a field: the full list comes back either way.',
|
|
@@ -49,7 +114,7 @@ export const describeFieldsTool = tool('openalex_describe_fields', {
|
|
|
49
114
|
entity_type: z.enum(ENTITY_TYPES).describe('OpenAlex entity type to list fields for.'),
|
|
50
115
|
context: z
|
|
51
116
|
.enum(CONTEXTS)
|
|
52
|
-
.describe('Field usage context. "filter": fields accepted in the filter param. "group_by": fields accepted in group_by — a subset of the filter set (raw
|
|
117
|
+
.describe('Field usage context. "filter": fields accepted in the filter param. "group_by": fields accepted in group_by — a subset of the filter set that leaves out what OpenAlex refuses to aggregate (raw dates, *.search operators, decimal scores, display_name, and external-ID fields among them). "select": fields accepted in select.'),
|
|
53
118
|
query: z
|
|
54
119
|
.string()
|
|
55
120
|
.optional()
|
|
@@ -70,7 +135,9 @@ export const describeFieldsTool = tool('openalex_describe_fields', {
|
|
|
70
135
|
// group_by accepts only a subset of the filter fields — drop the ones OpenAlex rejects as
|
|
71
136
|
// aggregation keys so the tool never advertises a field analyze_trends will 400 on. filter
|
|
72
137
|
// and select keep the full set (dates / *.search / range-modifiers are valid there).
|
|
73
|
-
const pool = input.context === 'group_by'
|
|
138
|
+
const pool = input.context === 'group_by'
|
|
139
|
+
? catalogPool.filter((field) => isGroupableField(input.entity_type, field))
|
|
140
|
+
: catalogPool;
|
|
74
141
|
const fields = input.query ? rankAllFields(input.query, pool) : pool;
|
|
75
142
|
ctx.log.info('Field catalog lookup', {
|
|
76
143
|
entityType: input.entity_type,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-fields.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/describe-fields.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"describe-fields.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/describe-fields.tool.ts"],"names":[],"mappings":"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"}
|