@cyanheads/pubmed-mcp-server 2.10.13 → 2.10.15
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 +24 -6
- package/CLAUDE.md +24 -6
- package/README.md +7 -7
- package/changelog/2.0.x/2.0.0.md +32 -0
- package/changelog/2.0.x/2.0.1.md +32 -0
- package/changelog/2.1.x/2.1.0.md +17 -0
- package/changelog/2.1.x/2.1.1.md +29 -0
- package/changelog/2.1.x/2.1.2.md +18 -0
- package/changelog/2.1.x/2.1.3.md +10 -0
- package/changelog/2.1.x/2.1.4.md +12 -0
- package/changelog/2.1.x/2.1.5.md +18 -0
- package/changelog/2.1.x/2.1.6.md +15 -0
- package/changelog/2.10.x/2.10.0.md +17 -0
- package/changelog/2.10.x/2.10.1.md +14 -0
- package/changelog/2.10.x/2.10.10.md +22 -0
- package/changelog/2.10.x/2.10.11.md +21 -0
- package/changelog/2.10.x/2.10.12.md +14 -0
- package/changelog/2.10.x/2.10.13.md +28 -0
- package/changelog/2.10.x/2.10.14.md +29 -0
- package/changelog/2.10.x/2.10.15.md +28 -0
- package/changelog/2.10.x/2.10.2.md +13 -0
- package/changelog/2.10.x/2.10.3.md +26 -0
- package/changelog/2.10.x/2.10.4.md +11 -0
- package/changelog/2.10.x/2.10.5.md +31 -0
- package/changelog/2.10.x/2.10.6.md +30 -0
- package/changelog/2.10.x/2.10.7.md +17 -0
- package/changelog/2.10.x/2.10.8.md +22 -0
- package/changelog/2.10.x/2.10.9.md +15 -0
- package/changelog/2.2.x/2.2.0.md +67 -0
- package/changelog/2.2.x/2.2.1.md +10 -0
- package/changelog/2.2.x/2.2.2.md +20 -0
- package/changelog/2.2.x/2.2.3.md +17 -0
- package/changelog/2.2.x/2.2.4.md +34 -0
- package/changelog/2.2.x/2.2.5.md +10 -0
- package/changelog/2.2.x/2.2.6.md +17 -0
- package/changelog/2.3.x/2.3.0.md +27 -0
- package/changelog/2.3.x/2.3.1.md +15 -0
- package/changelog/2.3.x/2.3.10.md +20 -0
- package/changelog/2.3.x/2.3.11.md +21 -0
- package/changelog/2.3.x/2.3.2.md +27 -0
- package/changelog/2.3.x/2.3.3.md +38 -0
- package/changelog/2.3.x/2.3.4.md +21 -0
- package/changelog/2.3.x/2.3.5.md +24 -0
- package/changelog/2.3.x/2.3.6.md +26 -0
- package/changelog/2.3.x/2.3.7.md +31 -0
- package/changelog/2.3.x/2.3.8.md +19 -0
- package/changelog/2.3.x/2.3.9.md +22 -0
- package/changelog/2.4.x/2.4.0.md +34 -0
- package/changelog/2.4.x/2.4.1.md +32 -0
- package/changelog/2.5.x/2.5.0.md +35 -0
- package/changelog/2.5.x/2.5.1.md +32 -0
- package/changelog/2.5.x/2.5.2.md +23 -0
- package/changelog/2.5.x/2.5.3.md +22 -0
- package/changelog/2.5.x/2.5.5.md +52 -0
- package/changelog/2.5.x/2.5.6.md +33 -0
- package/changelog/2.6.x/2.6.0.md +32 -0
- package/changelog/2.6.x/2.6.1.md +26 -0
- package/changelog/2.6.x/2.6.10.md +16 -0
- package/changelog/2.6.x/2.6.11.md +24 -0
- package/changelog/2.6.x/2.6.12.md +29 -0
- package/changelog/2.6.x/2.6.2.md +23 -0
- package/changelog/2.6.x/2.6.3.md +17 -0
- package/changelog/2.6.x/2.6.4.md +21 -0
- package/changelog/2.6.x/2.6.5.md +30 -0
- package/changelog/2.6.x/2.6.6.md +25 -0
- package/changelog/2.6.x/2.6.7.md +37 -0
- package/changelog/2.6.x/2.6.8.md +15 -0
- package/changelog/2.6.x/2.6.9.md +36 -0
- package/changelog/2.7.x/2.7.0.md +41 -0
- package/changelog/2.7.x/2.7.1.md +21 -0
- package/changelog/2.7.x/2.7.10.md +13 -0
- package/changelog/2.7.x/2.7.11.md +15 -0
- package/changelog/2.7.x/2.7.2.md +22 -0
- package/changelog/2.7.x/2.7.3.md +18 -0
- package/changelog/2.7.x/2.7.4.md +15 -0
- package/changelog/2.7.x/2.7.5.md +34 -0
- package/changelog/2.7.x/2.7.6.md +14 -0
- package/changelog/2.7.x/2.7.7.md +14 -0
- package/changelog/2.7.x/2.7.8.md +18 -0
- package/changelog/2.7.x/2.7.9.md +16 -0
- package/changelog/2.8.x/2.8.0.md +23 -0
- package/changelog/2.9.x/2.9.0.md +21 -0
- package/changelog/2.9.x/2.9.1.md +12 -0
- package/changelog/2.9.x/2.9.10.md +15 -0
- package/changelog/2.9.x/2.9.2.md +21 -0
- package/changelog/2.9.x/2.9.3.md +11 -0
- package/changelog/2.9.x/2.9.4.md +24 -0
- package/changelog/2.9.x/2.9.5.md +20 -0
- package/changelog/2.9.x/2.9.6.md +22 -0
- package/changelog/2.9.x/2.9.7.md +26 -0
- package/changelog/2.9.x/2.9.8.md +15 -0
- package/changelog/2.9.x/2.9.9.md +35 -0
- package/changelog/template.md +151 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +14 -5
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +0 -60
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.js +4 -4
- package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +6 -3
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +6 -3
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +7 -2
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
- package/dist/services/error-contracts.d.ts +30 -13
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +30 -13
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/api-client.d.ts +14 -2
- package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
- package/dist/services/europe-pmc/api-client.js +30 -4
- package/dist/services/europe-pmc/api-client.js.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts +20 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.js +98 -50
- package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
- package/dist/services/ncbi/ncbi-service.d.ts +18 -26
- package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
- package/dist/services/ncbi/ncbi-service.js +111 -127
- package/dist/services/ncbi/ncbi-service.js.map +1 -1
- package/dist/services/ncbi/request-queue.d.ts +22 -30
- package/dist/services/ncbi/request-queue.d.ts.map +1 -1
- package/dist/services/ncbi/request-queue.js +29 -128
- package/dist/services/ncbi/request-queue.js.map +1 -1
- package/dist/services/ncbi/response-handler.d.ts +14 -1
- package/dist/services/ncbi/response-handler.d.ts.map +1 -1
- package/dist/services/ncbi/response-handler.js +65 -9
- package/dist/services/ncbi/response-handler.js.map +1 -1
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +3 -10
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/package.json +20 -11
- package/server.json +3 -3
- package/dist/services/retry-policy.d.ts +0 -18
- package/dist/services/retry-policy.d.ts.map +0 -1
- package/dist/services/retry-policy.js +0 -21
- package/dist/services/retry-policy.js.map +0 -1
|
@@ -12,45 +12,55 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Tool definitions import the contract arrays directly and spread them into
|
|
14
14
|
* their `errors: [...]` declarations to surface the failure modes to the LLM.
|
|
15
|
+
* Every service-array entry carries `thrownBy: 'service'` so the linter's
|
|
16
|
+
* `error-contract-unthrown` check skips it while still checking the handler's
|
|
17
|
+
* own reasons. Spread a service array only into a tool whose handler lets that
|
|
18
|
+
* service's errors propagate — a tool that catches them all never produces
|
|
19
|
+
* those reasons.
|
|
15
20
|
*
|
|
16
21
|
* @module src/services/error-contracts
|
|
17
22
|
*/
|
|
18
23
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
19
24
|
/**
|
|
20
|
-
* Failure modes the NCBI service layer can surface. Tools
|
|
21
|
-
* `getNcbiService()`
|
|
22
|
-
* declared contract matches what actually reaches the wire.
|
|
25
|
+
* Failure modes the NCBI service layer can surface. Tools whose handler lets
|
|
26
|
+
* `getNcbiService()` failures propagate spread these into their own `errors[]`
|
|
27
|
+
* so the declared contract matches what actually reaches the wire.
|
|
23
28
|
*/
|
|
24
29
|
export declare const NCBI_SERVICE_ERRORS: readonly [{
|
|
25
30
|
readonly reason: 'queue_full';
|
|
26
31
|
readonly code: JsonRpcErrorCode.RateLimited;
|
|
27
|
-
readonly when: '
|
|
28
|
-
readonly recovery: '
|
|
32
|
+
readonly when: 'The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429).';
|
|
33
|
+
readonly recovery: 'Wait the number of seconds in `retryAfter`, then retry; the NCBI request queue is saturated or cooling down after a rate limit.';
|
|
29
34
|
readonly retryable: true;
|
|
35
|
+
readonly thrownBy: 'service';
|
|
30
36
|
}, {
|
|
31
37
|
readonly reason: 'ncbi_unreachable';
|
|
32
38
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
33
39
|
readonly when: 'NCBI E-utilities is unreachable after all retry attempts.';
|
|
34
40
|
readonly recovery: 'Retry after a brief delay; NCBI was unreachable across all retry attempts.';
|
|
35
41
|
readonly retryable: true;
|
|
42
|
+
readonly thrownBy: 'service';
|
|
36
43
|
}, {
|
|
37
44
|
readonly reason: 'ncbi_deadline_exceeded';
|
|
38
45
|
readonly code: JsonRpcErrorCode.Timeout;
|
|
39
46
|
readonly when: 'Total request deadline expired before NCBI returned a response.';
|
|
40
47
|
readonly recovery: 'Reduce batch size or retry; NCBI may be under temporary load.';
|
|
41
48
|
readonly retryable: true;
|
|
49
|
+
readonly thrownBy: 'service';
|
|
42
50
|
}, {
|
|
43
51
|
readonly reason: 'ncbi_invalid_response';
|
|
44
52
|
readonly code: JsonRpcErrorCode.SerializationError;
|
|
45
53
|
readonly when: 'NCBI returned a body that could not be parsed (invalid XML/JSON).';
|
|
46
54
|
readonly recovery: 'Retry the request; NCBI returned a malformed response that could not be parsed.';
|
|
47
55
|
readonly retryable: true;
|
|
56
|
+
readonly thrownBy: 'service';
|
|
48
57
|
}, {
|
|
49
58
|
readonly reason: 'ncbi_resource_not_found';
|
|
50
59
|
readonly code: JsonRpcErrorCode.NotFound;
|
|
51
60
|
readonly when: 'NCBI returned a structured "not found" error for the requested ID(s).';
|
|
52
61
|
readonly recovery: 'Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help.';
|
|
53
62
|
readonly retryable: false;
|
|
63
|
+
readonly thrownBy: 'service';
|
|
54
64
|
}];
|
|
55
65
|
/**
|
|
56
66
|
* Input failure the NCBI query tools reject before any call is made. Declared
|
|
@@ -98,11 +108,12 @@ export declare const UNPAYWALL_SERVICE_ERRORS: readonly [{
|
|
|
98
108
|
readonly when: 'Unpaywall was unreachable when resolving a DOI or fetching content.';
|
|
99
109
|
readonly recovery: 'Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.';
|
|
100
110
|
readonly retryable: true;
|
|
111
|
+
readonly thrownBy: 'service';
|
|
101
112
|
}];
|
|
102
113
|
/**
|
|
103
|
-
* Failure modes the OpenAlex service layer can surface. Tools
|
|
104
|
-
* `getOpenAlexService()` / `getOpenAlexServiceOptional()`
|
|
105
|
-
* these into their `errors[]`.
|
|
114
|
+
* Failure modes the OpenAlex service layer can surface. Tools whose handler
|
|
115
|
+
* lets `getOpenAlexService()` / `getOpenAlexServiceOptional()` failures
|
|
116
|
+
* propagate spread these into their `errors[]`.
|
|
106
117
|
*/
|
|
107
118
|
export declare const OPENALEX_SERVICE_ERRORS: readonly [{
|
|
108
119
|
readonly reason: 'openalex_unreachable';
|
|
@@ -110,35 +121,41 @@ export declare const OPENALEX_SERVICE_ERRORS: readonly [{
|
|
|
110
121
|
readonly when: 'OpenAlex was unreachable after all retry attempts.';
|
|
111
122
|
readonly recovery: 'Retry after a brief delay; OpenAlex was unreachable. NCBI and Europe PMC remain available.';
|
|
112
123
|
readonly retryable: true;
|
|
124
|
+
readonly thrownBy: 'service';
|
|
113
125
|
}, {
|
|
114
126
|
readonly reason: 'openalex_invalid_response';
|
|
115
127
|
readonly code: JsonRpcErrorCode.SerializationError;
|
|
116
128
|
readonly when: 'OpenAlex returned a body that could not be parsed (invalid JSON).';
|
|
117
129
|
readonly recovery: 'Retry the request; OpenAlex returned a malformed response that could not be parsed.';
|
|
118
130
|
readonly retryable: true;
|
|
131
|
+
readonly thrownBy: 'service';
|
|
119
132
|
}];
|
|
120
133
|
/**
|
|
121
|
-
* Failure modes the Europe PMC service layer can surface. Tools
|
|
122
|
-
* `getEuropePmcService()`
|
|
134
|
+
* Failure modes the Europe PMC service layer can surface. Tools whose handler
|
|
135
|
+
* lets `getEuropePmcService()` failures propagate spread these into their
|
|
136
|
+
* `errors[]`.
|
|
123
137
|
*/
|
|
124
138
|
export declare const EUROPEPMC_SERVICE_ERRORS: readonly [{
|
|
125
139
|
readonly reason: 'europepmc_unreachable';
|
|
126
140
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
127
|
-
readonly when: 'Europe PMC
|
|
141
|
+
readonly when: 'Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results.';
|
|
128
142
|
readonly recovery: 'Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.';
|
|
129
143
|
readonly retryable: true;
|
|
144
|
+
readonly thrownBy: 'service';
|
|
130
145
|
}, {
|
|
131
146
|
readonly reason: 'europepmc_invalid_response';
|
|
132
147
|
readonly code: JsonRpcErrorCode.SerializationError;
|
|
133
148
|
readonly when: 'Europe PMC returned a body that could not be parsed (invalid JSON or XML).';
|
|
134
149
|
readonly recovery: 'Retry the request; Europe PMC returned a malformed response that could not be parsed.';
|
|
135
150
|
readonly retryable: true;
|
|
151
|
+
readonly thrownBy: 'service';
|
|
136
152
|
}, {
|
|
137
153
|
readonly reason: 'europepmc_invalid_input';
|
|
138
154
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
139
|
-
readonly when: 'Europe PMC rejected the request input
|
|
140
|
-
readonly recovery: 'Adjust the input —
|
|
155
|
+
readonly when: 'Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt.';
|
|
156
|
+
readonly recovery: 'Adjust the input — the query, the sort, or the cursorMark — before retrying; the same input will be rejected again.';
|
|
141
157
|
readonly retryable: false;
|
|
158
|
+
readonly thrownBy: 'service';
|
|
142
159
|
}];
|
|
143
160
|
/**
|
|
144
161
|
* Reason identifier union for type-safe authoring on the service layer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-contracts.d.ts","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-contracts.d.ts","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,22 +12,28 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Tool definitions import the contract arrays directly and spread them into
|
|
14
14
|
* their `errors: [...]` declarations to surface the failure modes to the LLM.
|
|
15
|
+
* Every service-array entry carries `thrownBy: 'service'` so the linter's
|
|
16
|
+
* `error-contract-unthrown` check skips it while still checking the handler's
|
|
17
|
+
* own reasons. Spread a service array only into a tool whose handler lets that
|
|
18
|
+
* service's errors propagate — a tool that catches them all never produces
|
|
19
|
+
* those reasons.
|
|
15
20
|
*
|
|
16
21
|
* @module src/services/error-contracts
|
|
17
22
|
*/
|
|
18
23
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
19
24
|
/**
|
|
20
|
-
* Failure modes the NCBI service layer can surface. Tools
|
|
21
|
-
* `getNcbiService()`
|
|
22
|
-
* declared contract matches what actually reaches the wire.
|
|
25
|
+
* Failure modes the NCBI service layer can surface. Tools whose handler lets
|
|
26
|
+
* `getNcbiService()` failures propagate spread these into their own `errors[]`
|
|
27
|
+
* so the declared contract matches what actually reaches the wire.
|
|
23
28
|
*/
|
|
24
29
|
export const NCBI_SERVICE_ERRORS = [
|
|
25
30
|
{
|
|
26
31
|
reason: 'queue_full',
|
|
27
32
|
code: JsonRpcErrorCode.RateLimited,
|
|
28
|
-
when: '
|
|
29
|
-
recovery: '
|
|
33
|
+
when: 'The local NCBI request queue shed the call — the queue is full, or the call cannot start before its total deadline (for example behind the cooldown that follows an NCBI 429).',
|
|
34
|
+
recovery: 'Wait the number of seconds in `retryAfter`, then retry; the NCBI request queue is saturated or cooling down after a rate limit.',
|
|
30
35
|
retryable: true,
|
|
36
|
+
thrownBy: 'service',
|
|
31
37
|
},
|
|
32
38
|
{
|
|
33
39
|
reason: 'ncbi_unreachable',
|
|
@@ -35,6 +41,7 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
35
41
|
when: 'NCBI E-utilities is unreachable after all retry attempts.',
|
|
36
42
|
recovery: 'Retry after a brief delay; NCBI was unreachable across all retry attempts.',
|
|
37
43
|
retryable: true,
|
|
44
|
+
thrownBy: 'service',
|
|
38
45
|
},
|
|
39
46
|
{
|
|
40
47
|
reason: 'ncbi_deadline_exceeded',
|
|
@@ -42,6 +49,7 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
42
49
|
when: 'Total request deadline expired before NCBI returned a response.',
|
|
43
50
|
recovery: 'Reduce batch size or retry; NCBI may be under temporary load.',
|
|
44
51
|
retryable: true,
|
|
52
|
+
thrownBy: 'service',
|
|
45
53
|
},
|
|
46
54
|
{
|
|
47
55
|
reason: 'ncbi_invalid_response',
|
|
@@ -49,6 +57,7 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
49
57
|
when: 'NCBI returned a body that could not be parsed (invalid XML/JSON).',
|
|
50
58
|
recovery: 'Retry the request; NCBI returned a malformed response that could not be parsed.',
|
|
51
59
|
retryable: true,
|
|
60
|
+
thrownBy: 'service',
|
|
52
61
|
},
|
|
53
62
|
{
|
|
54
63
|
reason: 'ncbi_resource_not_found',
|
|
@@ -56,6 +65,7 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
56
65
|
when: 'NCBI returned a structured "not found" error for the requested ID(s).',
|
|
57
66
|
recovery: 'Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help.',
|
|
58
67
|
retryable: false,
|
|
68
|
+
thrownBy: 'service',
|
|
59
69
|
},
|
|
60
70
|
];
|
|
61
71
|
/**
|
|
@@ -109,12 +119,13 @@ export const UNPAYWALL_SERVICE_ERRORS = [
|
|
|
109
119
|
when: 'Unpaywall was unreachable when resolving a DOI or fetching content.',
|
|
110
120
|
recovery: 'Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.',
|
|
111
121
|
retryable: true,
|
|
122
|
+
thrownBy: 'service',
|
|
112
123
|
},
|
|
113
124
|
];
|
|
114
125
|
/**
|
|
115
|
-
* Failure modes the OpenAlex service layer can surface. Tools
|
|
116
|
-
* `getOpenAlexService()` / `getOpenAlexServiceOptional()`
|
|
117
|
-
* these into their `errors[]`.
|
|
126
|
+
* Failure modes the OpenAlex service layer can surface. Tools whose handler
|
|
127
|
+
* lets `getOpenAlexService()` / `getOpenAlexServiceOptional()` failures
|
|
128
|
+
* propagate spread these into their `errors[]`.
|
|
118
129
|
*/
|
|
119
130
|
export const OPENALEX_SERVICE_ERRORS = [
|
|
120
131
|
{
|
|
@@ -123,6 +134,7 @@ export const OPENALEX_SERVICE_ERRORS = [
|
|
|
123
134
|
when: 'OpenAlex was unreachable after all retry attempts.',
|
|
124
135
|
recovery: 'Retry after a brief delay; OpenAlex was unreachable. NCBI and Europe PMC remain available.',
|
|
125
136
|
retryable: true,
|
|
137
|
+
thrownBy: 'service',
|
|
126
138
|
},
|
|
127
139
|
{
|
|
128
140
|
reason: 'openalex_invalid_response',
|
|
@@ -130,19 +142,22 @@ export const OPENALEX_SERVICE_ERRORS = [
|
|
|
130
142
|
when: 'OpenAlex returned a body that could not be parsed (invalid JSON).',
|
|
131
143
|
recovery: 'Retry the request; OpenAlex returned a malformed response that could not be parsed.',
|
|
132
144
|
retryable: true,
|
|
145
|
+
thrownBy: 'service',
|
|
133
146
|
},
|
|
134
147
|
];
|
|
135
148
|
/**
|
|
136
|
-
* Failure modes the Europe PMC service layer can surface. Tools
|
|
137
|
-
* `getEuropePmcService()`
|
|
149
|
+
* Failure modes the Europe PMC service layer can surface. Tools whose handler
|
|
150
|
+
* lets `getEuropePmcService()` failures propagate spread these into their
|
|
151
|
+
* `errors[]`.
|
|
138
152
|
*/
|
|
139
153
|
export const EUROPEPMC_SERVICE_ERRORS = [
|
|
140
154
|
{
|
|
141
155
|
reason: 'europepmc_unreachable',
|
|
142
156
|
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
143
|
-
when: 'Europe PMC
|
|
157
|
+
when: 'Europe PMC failed on every retry attempt — unreachable, an HTTP 404 or 5xx other than a 504 timeout from its search endpoint, or an empty response with no results.',
|
|
144
158
|
recovery: 'Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.',
|
|
145
159
|
retryable: true,
|
|
160
|
+
thrownBy: 'service',
|
|
146
161
|
},
|
|
147
162
|
{
|
|
148
163
|
reason: 'europepmc_invalid_response',
|
|
@@ -150,13 +165,15 @@ export const EUROPEPMC_SERVICE_ERRORS = [
|
|
|
150
165
|
when: 'Europe PMC returned a body that could not be parsed (invalid JSON or XML).',
|
|
151
166
|
recovery: 'Retry the request; Europe PMC returned a malformed response that could not be parsed.',
|
|
152
167
|
retryable: true,
|
|
168
|
+
thrownBy: 'service',
|
|
153
169
|
},
|
|
154
170
|
{
|
|
155
171
|
reason: 'europepmc_invalid_input',
|
|
156
172
|
code: JsonRpcErrorCode.ValidationError,
|
|
157
|
-
when: 'Europe PMC rejected the request input
|
|
158
|
-
recovery: 'Adjust the input —
|
|
173
|
+
when: 'Europe PMC rejected the request input — an error message such as an empty query, an empty response to a sort with an undocumented field or no asc/desc direction, or an empty response to a pagination cursor on every attempt.',
|
|
174
|
+
recovery: 'Adjust the input — the query, the sort, or the cursorMark — before retrying; the same input will be rejected again.',
|
|
159
175
|
retryable: false,
|
|
176
|
+
thrownBy: 'service',
|
|
160
177
|
},
|
|
161
178
|
];
|
|
162
179
|
const REASON_TO_RECOVERY = new Map([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-contracts.js","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-contracts.js","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Low-level HTTP client for Europe PMC's REST API. Builds URLs,
|
|
3
|
-
* injects the optional contact email, and exposes single-attempt search
|
|
4
|
-
* fullTextXML calls.
|
|
3
|
+
* injects the optional contact email, and exposes single-attempt search,
|
|
4
|
+
* fullTextXML, and citation-link calls. Classifies each endpoint's HTTP
|
|
5
|
+
* failures — a failed search as `europepmc_unreachable`, a missing fullTextXML
|
|
6
|
+
* as `not-available` — while retry logic lives in `EuropePmcService`.
|
|
5
7
|
* @module src/services/europe-pmc/api-client
|
|
6
8
|
*/
|
|
7
9
|
import { type EuropePmcSearchParams } from './types.js';
|
|
@@ -36,6 +38,16 @@ export declare class EuropePmcApiClient {
|
|
|
36
38
|
* Execute a search. Returns the raw JSON response body as a string so
|
|
37
39
|
* `EuropePmcService` can parse and surface SerializationError consistently
|
|
38
40
|
* when the body is malformed.
|
|
41
|
+
*
|
|
42
|
+
* A failed `/search` is an outage, never "no match" — a genuine zero-hit
|
|
43
|
+
* query is HTTP 200 with `hitCount: 0`. A 404 is reclassified from its
|
|
44
|
+
* status-mapped `NotFound` to `ServiceUnavailable` so the service retries it.
|
|
45
|
+
* A retryable 5xx keeps its code, so a 504 stays `Timeout`. Only a failure
|
|
46
|
+
* that ends up `ServiceUnavailable` carries `europepmc_unreachable` and its
|
|
47
|
+
* recovery hint, the one code that reason is declared for. An upstream
|
|
48
|
+
* `retryAfter` is kept. A 429 and every other 4xx pass through unchanged, as
|
|
49
|
+
* does a 501, whose `data.retryable: false` keeps it out of the retry loop.
|
|
50
|
+
* (#152)
|
|
39
51
|
*/
|
|
40
52
|
search(params: EuropePmcSearchParams): Promise<string>;
|
|
41
53
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAI5E,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GACpC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,wBAAwB,EAAI;IAEjE;;;;;;;;;;;;;;OAcG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4C3D;IAED;;;;;;;;;OASG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA4C7F;IAED;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7F;IAED;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAG9F;IAED,iFAAiF;YACnE,cAAc;IA8B5B,OAAO,CAAC,cAAc;IActB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAMzB"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Low-level HTTP client for Europe PMC's REST API. Builds URLs,
|
|
3
|
-
* injects the optional contact email, and exposes single-attempt search
|
|
4
|
-
* fullTextXML calls.
|
|
3
|
+
* injects the optional contact email, and exposes single-attempt search,
|
|
4
|
+
* fullTextXML, and citation-link calls. Classifies each endpoint's HTTP
|
|
5
|
+
* failures — a failed search as `europepmc_unreachable`, a missing fullTextXML
|
|
6
|
+
* as `not-available` — while retry logic lives in `EuropePmcService`.
|
|
5
7
|
* @module src/services/europe-pmc/api-client
|
|
6
8
|
*/
|
|
7
9
|
import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -27,6 +29,16 @@ export class EuropePmcApiClient {
|
|
|
27
29
|
* Execute a search. Returns the raw JSON response body as a string so
|
|
28
30
|
* `EuropePmcService` can parse and surface SerializationError consistently
|
|
29
31
|
* when the body is malformed.
|
|
32
|
+
*
|
|
33
|
+
* A failed `/search` is an outage, never "no match" — a genuine zero-hit
|
|
34
|
+
* query is HTTP 200 with `hitCount: 0`. A 404 is reclassified from its
|
|
35
|
+
* status-mapped `NotFound` to `ServiceUnavailable` so the service retries it.
|
|
36
|
+
* A retryable 5xx keeps its code, so a 504 stays `Timeout`. Only a failure
|
|
37
|
+
* that ends up `ServiceUnavailable` carries `europepmc_unreachable` and its
|
|
38
|
+
* recovery hint, the one code that reason is declared for. An upstream
|
|
39
|
+
* `retryAfter` is kept. A 429 and every other 4xx pass through unchanged, as
|
|
40
|
+
* does a 501, whose `data.retryable: false` keeps it out of the retry loop.
|
|
41
|
+
* (#152)
|
|
30
42
|
*/
|
|
31
43
|
async search(params) {
|
|
32
44
|
const url = this.buildSearchUrl(params);
|
|
@@ -42,8 +54,22 @@ export class EuropePmcApiClient {
|
|
|
42
54
|
});
|
|
43
55
|
}
|
|
44
56
|
catch (error) {
|
|
45
|
-
if (error instanceof McpError)
|
|
46
|
-
|
|
57
|
+
if (error instanceof McpError) {
|
|
58
|
+
const status = error.data?.status;
|
|
59
|
+
const isOutage = status === 404 ||
|
|
60
|
+
(typeof status === 'number' && status >= 500 && error.data?.retryable !== false);
|
|
61
|
+
if (!isOutage)
|
|
62
|
+
throw error;
|
|
63
|
+
const code = status === 404 ? JsonRpcErrorCode.ServiceUnavailable : error.code;
|
|
64
|
+
throw new McpError(code, `Europe PMC search request failed: ${error.message}`, {
|
|
65
|
+
...(code === JsonRpcErrorCode.ServiceUnavailable && {
|
|
66
|
+
reason: 'europepmc_unreachable',
|
|
67
|
+
...recoveryFor('europepmc_unreachable'),
|
|
68
|
+
}),
|
|
69
|
+
status,
|
|
70
|
+
...(error.data?.retryAfter !== undefined && { retryAfter: error.data.retryAfter }),
|
|
71
|
+
}, { cause: error });
|
|
72
|
+
}
|
|
47
73
|
const msg = error instanceof Error ? error.message : String(error);
|
|
48
74
|
throw serviceUnavailable(`Europe PMC search request failed: ${msg}`, { reason: 'europepmc_unreachable', ...recoveryFor('europepmc_unreachable') }, { cause: error });
|
|
49
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* from the same `ORDERED_XML_PARSER_OPTIONS` NCBI's ordered parser uses, so
|
|
8
8
|
* `parsePmcArticle` consumes the result without modification.
|
|
9
9
|
*
|
|
10
|
+
* A search's retry boundary covers fetch and response classification, so
|
|
11
|
+
* Europe PMC's intermittent empty `{ version }` envelope is retried like an
|
|
12
|
+
* HTTP outage; only a sort or cursor that explains it is reported as bad input.
|
|
13
|
+
*
|
|
10
14
|
* Optional service: only constructed when `EUROPEPMC_ENABLED=true` (the
|
|
11
15
|
* default). `getEuropePmcService()` returns `undefined` when disabled so
|
|
12
16
|
* callers can skip the chain step gracefully.
|
|
@@ -36,8 +40,18 @@ export declare class EuropePmcService {
|
|
|
36
40
|
/**
|
|
37
41
|
* Search Europe PMC. Cursor-based pagination — pass `cursorMark: '*'` (or
|
|
38
42
|
* omit) for the first page; pass the returned `nextCursorMark` for the next.
|
|
43
|
+
*
|
|
44
|
+
* The retry boundary covers the fetch and the response classification
|
|
45
|
+
* together, so an empty envelope retries like any other transient failure
|
|
46
|
+
* rather than surfacing after the loop has already returned. (#159)
|
|
39
47
|
*/
|
|
40
48
|
search(params: EuropePmcSearchParams): Promise<EuropePmcSearchResult>;
|
|
49
|
+
/**
|
|
50
|
+
* One search attempt: fetch, parse, and classify the body. `isLastAttempt`
|
|
51
|
+
* lets an empty envelope that has persisted through the whole retry budget
|
|
52
|
+
* be attributed to a caller-supplied cursor.
|
|
53
|
+
*/
|
|
54
|
+
private searchOnce;
|
|
41
55
|
/**
|
|
42
56
|
* Look up specific records by `source` + EPMC id. One search request covers
|
|
43
57
|
* the whole batch: each ref becomes a `recordLookupQuery` clause, OR-joined
|
|
@@ -103,7 +117,12 @@ export declare class EuropePmcService {
|
|
|
103
117
|
/**
|
|
104
118
|
* Retry wrapper for transient errors. Mirrors NCBI's `withRetry` minus the
|
|
105
119
|
* service-level deadline — EPMC requests are cheaper individually and the
|
|
106
|
-
* caller (typically `ctx.signal`) bounds the total chain.
|
|
120
|
+
* caller (typically `ctx.signal`) bounds the total chain. `execute` receives
|
|
121
|
+
* the zero-based attempt index. On exhaustion the last error keeps its code
|
|
122
|
+
* and an upstream `retryAfter`, so a 429 still tells the caller how long to
|
|
123
|
+
* wait. Only a `ServiceUnavailable` gains `europepmc_unreachable` and its hint;
|
|
124
|
+
* a `Timeout` or `RateLimited` keeps its code with no reason, as the NCBI
|
|
125
|
+
* service reports them.
|
|
107
126
|
*/
|
|
108
127
|
private withRetry;
|
|
109
128
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"europe-pmc-service.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/europe-pmc-service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"europe-pmc-service.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/europe-pmc-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAiBH,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,4CAA4C,CAAC;AAEzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EACV,uBAAuB,EAEvB,kBAAkB,EAElB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EAErB,qBAAqB,EACrB,eAAe,EAChB,MAAM,YAAY,CAAC;AAqEpB;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAL7B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAY;IAE7C,YACmB,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,qBAAqB,EAC5B,UAAU,EAAE,MAAM,EAQpC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWpE;IAED;;;;OAIG;YACW,UAAU;IAsHxB;;;;;;;;;;OAUG;IACG,YAAY,CAChB,IAAI,EAAE,SAAS,kBAAkB,EAAE,EACnC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQ/B;IAED;;;;;;;OAOG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAgBlC;IAED;;;;;;;;;;OAUG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,sBAAsB,CAAC,CAQjC;IAED;;;;;;;;;;OAUG;IACH,UAAU,CACR,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,sBAAsB,CAAC,CAQjC;IAED;;;;;OAKG;YACW,iBAAiB;IAsE/B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CA8BlD;IAED;;;;;;;;;OASG;YACW,SAAS;CAkDxB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAyB3C;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE"}
|