@cyanheads/pubmed-mcp-server 2.10.18 → 2.10.19
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 +11 -13
- package/CLAUDE.md +11 -13
- package/Dockerfile +77 -36
- package/README.md +86 -109
- package/changelog/2.10.x/2.10.19.md +42 -0
- package/dist/mcp-server/resources/definitions/database-info.resource.d.ts +43 -1
- package/dist/mcp-server/resources/definitions/database-info.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/database-info.resource.js +2 -0
- package/dist/mcp-server/resources/definitions/database-info.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/_visible-text.d.ts +19 -0
- package/dist/mcp-server/tools/definitions/_visible-text.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_visible-text.js +22 -0
- package/dist/mcp-server/tools/definitions/_visible-text.js.map +1 -0
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts +11 -4
- package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/convert-ids.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/convert-ids.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +20 -7
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.js +40 -5
- package/dist/mcp-server/tools/definitions/fetch-articles.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +11 -4
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +18 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.js +19 -5
- package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +11 -4
- 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 +16 -15
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.js +40 -22
- package/dist/mcp-server/tools/definitions/lookup-citation.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +13 -6
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js +8 -8
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +19 -5
- 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 +37 -7
- 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 +40 -18
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.js +193 -43
- package/dist/mcp-server/tools/definitions/search-articles.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +13 -6
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.js +8 -8
- package/dist/mcp-server/tools/definitions/spell-check.tool.js.map +1 -1
- package/dist/services/error-contracts.d.ts +42 -71
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +43 -75
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/api-client.d.ts +8 -2
- package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
- package/dist/services/europe-pmc/api-client.js +13 -8
- package/dist/services/europe-pmc/api-client.js.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts +25 -2
- package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.js +90 -25
- package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
- package/dist/services/europe-pmc/types.d.ts +6 -1
- package/dist/services/europe-pmc/types.d.ts.map +1 -1
- package/dist/services/ncbi/api-client.d.ts.map +1 -1
- package/dist/services/ncbi/api-client.js +2 -3
- package/dist/services/ncbi/api-client.js.map +1 -1
- package/dist/services/ncbi/ncbi-service.d.ts +2 -1
- package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
- package/dist/services/ncbi/ncbi-service.js +67 -20
- package/dist/services/ncbi/ncbi-service.js.map +1 -1
- package/dist/services/ncbi/parsing/article-parser.d.ts +15 -1
- package/dist/services/ncbi/parsing/article-parser.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/article-parser.js +29 -0
- package/dist/services/ncbi/parsing/article-parser.js.map +1 -1
- package/dist/services/ncbi/response-handler.d.ts.map +1 -1
- package/dist/services/ncbi/response-handler.js +22 -19
- package/dist/services/ncbi/response-handler.js.map +1 -1
- package/dist/services/ncbi/types.d.ts +33 -0
- package/dist/services/ncbi/types.d.ts.map +1 -1
- package/dist/services/openalex/api-client.d.ts.map +1 -1
- package/dist/services/openalex/api-client.js +6 -7
- package/dist/services/openalex/api-client.js.map +1 -1
- package/dist/services/openalex/openalex-service.d.ts +3 -3
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +4 -6
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
- package/dist/services/unpaywall/unpaywall-service.js +1 -3
- package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
- package/package.json +6 -5
- package/server.json +3 -3
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Canonical error contracts
|
|
3
|
-
*
|
|
4
|
-
* input rejections
|
|
5
|
-
*
|
|
2
|
+
* @fileoverview Canonical error contracts: the failure modes the NCBI, Europe
|
|
3
|
+
* PMC, and OpenAlex services throw and tools declare in `errors[]`, plus the
|
|
4
|
+
* input rejections some tools raise before any upstream call. Unpaywall has no
|
|
5
|
+
* array here: its one caller, `pubmed_fetch_fulltext`, catches every Unpaywall
|
|
6
|
+
* failure and reports it as a tier outcome, so no Unpaywall reason reaches a
|
|
7
|
+
* caller as an error.
|
|
6
8
|
*
|
|
7
|
-
* Service-layer code
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* handler-level `ctx.fail
|
|
9
|
+
* Service-layer code stamps `data: { reason }` on its throws, adding its own
|
|
10
|
+
* `data.recovery` only where the hint depends on runtime detail. When a failure
|
|
11
|
+
* with no hint of its own reaches a tool or resource that declares the reason,
|
|
12
|
+
* the framework fills that entry's `recovery` in as `data.recovery.hint` — the
|
|
13
|
+
* same hint a handler-level `ctx.fail` gets. A tool's error also renders the
|
|
14
|
+
* hint as a `Recovery:` line in its `content[]` text (dropped when the message
|
|
15
|
+
* already contains it); a resource's carries it only in the JSON-RPC error's
|
|
16
|
+
* `data`.
|
|
12
17
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
18
|
+
* Definitions import the contract arrays directly and spread them into their
|
|
19
|
+
* `errors: [...]` declarations to surface the failure modes to the LLM.
|
|
15
20
|
* Every service-array entry carries `thrownBy: 'service'` so the linter's
|
|
16
21
|
* `error-contract-unthrown` check skips it while still checking the handler's
|
|
17
22
|
* own reasons. Spread a service array only into a tool whose handler lets that
|
|
@@ -36,15 +41,22 @@ export declare const NCBI_SERVICE_ERRORS: readonly [{
|
|
|
36
41
|
}, {
|
|
37
42
|
readonly reason: 'ncbi_unreachable';
|
|
38
43
|
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
39
|
-
readonly when: 'NCBI E-utilities
|
|
40
|
-
readonly recovery: 'Retry after a brief delay; NCBI
|
|
44
|
+
readonly when: 'NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline.';
|
|
45
|
+
readonly recovery: 'Retry after a brief delay; NCBI failed on every attempt this call made.';
|
|
46
|
+
readonly retryable: true;
|
|
47
|
+
readonly thrownBy: 'service';
|
|
48
|
+
}, {
|
|
49
|
+
readonly reason: 'ncbi_rate_limited';
|
|
50
|
+
readonly code: JsonRpcErrorCode.RateLimited;
|
|
51
|
+
readonly when: 'NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap.';
|
|
52
|
+
readonly recovery: "Wait as long as `retryAfter` says when present (a number of seconds or an HTTP date), otherwise a few seconds, then retry; NCBI is throttling this server's requests. If the server runs without NCBI_API_KEY, setting it raises the E-utilities ceiling from about 3 to about 10 requests per second and shortens the default gap between requests — an operator setting in the server's environment, not a tool input.";
|
|
41
53
|
readonly retryable: true;
|
|
42
54
|
readonly thrownBy: 'service';
|
|
43
55
|
}, {
|
|
44
56
|
readonly reason: 'ncbi_deadline_exceeded';
|
|
45
57
|
readonly code: JsonRpcErrorCode.Timeout;
|
|
46
|
-
readonly when: '
|
|
47
|
-
readonly recovery: '
|
|
58
|
+
readonly when: 'The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff.';
|
|
59
|
+
readonly recovery: 'Retry after a brief delay; NCBI may be under temporary load. For a large batch, split it into smaller calls.';
|
|
48
60
|
readonly retryable: true;
|
|
49
61
|
readonly thrownBy: 'service';
|
|
50
62
|
}, {
|
|
@@ -63,32 +75,30 @@ export declare const NCBI_SERVICE_ERRORS: readonly [{
|
|
|
63
75
|
readonly thrownBy: 'service';
|
|
64
76
|
}];
|
|
65
77
|
/**
|
|
66
|
-
* Input failure the
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* cannot happen.
|
|
78
|
+
* Input failure the free-text query tools reject before any call is made —
|
|
79
|
+
* the NCBI query tools and `pubmed_europepmc_search` alike. Declared here
|
|
80
|
+
* rather than inside a service array so only the tools that actually throw it
|
|
81
|
+
* advertise it — a tool that takes no free-text query can never produce it,
|
|
82
|
+
* and declaring it there would advertise a failure mode that cannot happen.
|
|
71
83
|
*
|
|
72
84
|
* Unlike the service arrays in this module, this one is thrown from a tool
|
|
73
|
-
* handler via `ctx.fail('blank_query', …)`, so its
|
|
74
|
-
* through `ctx.recoveryFor` and it stays out of {@link ServiceErrorReason}.
|
|
85
|
+
* handler via `ctx.fail('blank_query', …)`, so its entry carries no `thrownBy`.
|
|
75
86
|
*/
|
|
76
|
-
export declare const
|
|
87
|
+
export declare const QUERY_INPUT_ERRORS: readonly [{
|
|
77
88
|
readonly reason: 'blank_query';
|
|
78
89
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
79
|
-
readonly when: 'The query
|
|
80
|
-
readonly recovery: 'Supply a nonblank search term
|
|
90
|
+
readonly when: 'The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and ` ` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`.';
|
|
91
|
+
readonly recovery: 'Supply a nonblank search term; retrying the same blank input will not help.';
|
|
81
92
|
readonly retryable: false;
|
|
82
93
|
}];
|
|
83
94
|
/**
|
|
84
95
|
* Input failure the identifier-conversion path rejects before any call is made.
|
|
85
|
-
* Separate from {@link
|
|
96
|
+
* Separate from {@link QUERY_INPUT_ERRORS} for the same reason that array
|
|
86
97
|
* is separate from the service contracts: only a tool that takes caller-supplied
|
|
87
98
|
* identifiers can produce it.
|
|
88
99
|
*
|
|
89
|
-
* Thrown from a tool handler via `ctx.fail('malformed_id', …)`, so its
|
|
90
|
-
*
|
|
91
|
-
* {@link ServiceErrorReason}.
|
|
100
|
+
* Thrown from a tool handler via `ctx.fail('malformed_id', …)`, so its entry
|
|
101
|
+
* carries no `thrownBy`.
|
|
92
102
|
*/
|
|
93
103
|
export declare const NCBI_ID_INPUT_ERRORS: readonly [{
|
|
94
104
|
readonly reason: 'malformed_id';
|
|
@@ -97,21 +107,6 @@ export declare const NCBI_ID_INPUT_ERRORS: readonly [{
|
|
|
97
107
|
readonly recovery: 'Submit one identifier per `ids` element, in the declared idType format; the same packed value will be rejected again.';
|
|
98
108
|
readonly retryable: false;
|
|
99
109
|
}];
|
|
100
|
-
/**
|
|
101
|
-
* Failure modes the Unpaywall service layer can surface. A tool whose handler
|
|
102
|
-
* lets `getUnpaywallService()` failures propagate spreads these into its
|
|
103
|
-
* `errors[]`. None does today: `pubmed_fetch_fulltext`, the only consumer,
|
|
104
|
-
* folds every Unpaywall failure into its tier chain, so the entries serve the
|
|
105
|
-
* service's own `recoveryFor` hints.
|
|
106
|
-
*/
|
|
107
|
-
export declare const UNPAYWALL_SERVICE_ERRORS: readonly [{
|
|
108
|
-
readonly reason: 'unpaywall_unreachable';
|
|
109
|
-
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
110
|
-
readonly when: 'Unpaywall was unreachable when resolving a DOI or fetching content.';
|
|
111
|
-
readonly recovery: 'Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.';
|
|
112
|
-
readonly retryable: true;
|
|
113
|
-
readonly thrownBy: 'service';
|
|
114
|
-
}];
|
|
115
110
|
/**
|
|
116
111
|
* Failure modes the OpenAlex service layer can surface. Tools whose handler
|
|
117
112
|
* lets `getOpenAlexService()` / `getOpenAlexServiceOptional()` failures
|
|
@@ -126,7 +121,7 @@ export declare const OPENALEX_SERVICE_ERRORS: readonly [{
|
|
|
126
121
|
readonly thrownBy: 'service';
|
|
127
122
|
}, {
|
|
128
123
|
readonly reason: 'openalex_invalid_response';
|
|
129
|
-
readonly code: JsonRpcErrorCode.
|
|
124
|
+
readonly code: JsonRpcErrorCode.ServiceUnavailable;
|
|
130
125
|
readonly when: 'OpenAlex returned a body that could not be parsed (invalid JSON).';
|
|
131
126
|
readonly recovery: 'Retry the request; OpenAlex returned a malformed response that could not be parsed.';
|
|
132
127
|
readonly retryable: true;
|
|
@@ -154,33 +149,9 @@ export declare const EUROPEPMC_SERVICE_ERRORS: readonly [{
|
|
|
154
149
|
}, {
|
|
155
150
|
readonly reason: 'europepmc_invalid_input';
|
|
156
151
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
157
|
-
readonly when: 'Europe PMC rejected the request input — an error message
|
|
158
|
-
readonly recovery:
|
|
152
|
+
readonly when: 'Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served).';
|
|
153
|
+
readonly recovery: "Fix the rejected input before retrying — the query, the sort, or the cursorMark (pass the previous response's `nextCursorMark` verbatim, or `*` to restart); the same input will most likely be rejected again.";
|
|
159
154
|
readonly retryable: false;
|
|
160
155
|
readonly thrownBy: 'service';
|
|
161
156
|
}];
|
|
162
|
-
/**
|
|
163
|
-
* Reason identifier union for type-safe authoring on the service layer.
|
|
164
|
-
* Tool handlers get a tighter typed union from `ctx.fail` / `ctx.recoveryFor`
|
|
165
|
-
* via the framework — this is the service-side fallback.
|
|
166
|
-
*/
|
|
167
|
-
export type ServiceErrorReason = (typeof NCBI_SERVICE_ERRORS)[number]['reason'] | (typeof UNPAYWALL_SERVICE_ERRORS)[number]['reason'] | (typeof EUROPEPMC_SERVICE_ERRORS)[number]['reason'] | (typeof OPENALEX_SERVICE_ERRORS)[number]['reason'];
|
|
168
|
-
/**
|
|
169
|
-
* Service-layer counterpart to `ctx.recoveryFor`. Returns `{ recovery: { hint } }`
|
|
170
|
-
* for the contract reason. Use at every service throw that stamps a `reason` so
|
|
171
|
-
* the wire payload carries the same actionable hint the LLM gets from
|
|
172
|
-
* handler-level `ctx.fail`.
|
|
173
|
-
*
|
|
174
|
-
* The parameter is constrained to `ServiceErrorReason`, so typos fail at compile
|
|
175
|
-
* time. The runtime guard catches the impossible case where the reason union
|
|
176
|
-
* and the recovery map drift apart in future edits.
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* throw serviceUnavailable(msg, { reason: 'ncbi_unreachable', ...recoveryFor('ncbi_unreachable') });
|
|
180
|
-
*/
|
|
181
|
-
export declare function recoveryFor(reason: ServiceErrorReason): {
|
|
182
|
-
recovery: {
|
|
183
|
-
hint: string;
|
|
184
|
-
};
|
|
185
|
-
};
|
|
186
157
|
//# sourceMappingURL=error-contracts.d.ts.map
|
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;aAE5B,MAAM,EAAE,YAAY;aACpB,IAAI;aACJ,IAAI,EAAE,gLAAgL;aACtL,QAAQ,EACN,iIAAiI;aACnI,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,kBAAkB;aAC1B,IAAI;aACJ,IAAI,EAAE,4IAA4I;aAClJ,QAAQ,EAAE,yEAAyE;aACnF,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,mBAAmB;aAC3B,IAAI;aACJ,IAAI,EAAE,gOAAgO;aACtO,QAAQ,EACN,0ZAA0Z;aAC5Z,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,wBAAwB;aAChC,IAAI;aACJ,IAAI,EAAE,mIAAmI;aACzI,QAAQ,EACN,8GAA8G;aAChH,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,uBAAuB;aAC/B,IAAI;aACJ,IAAI,EAAE,mEAAmE;aACzE,QAAQ,EAAE,iFAAiF;aAC3F,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,yBAAyB;aACjC,IAAI;aACJ,IAAI,EAAE,uEAAuE;aAC7E,QAAQ,EACN,gGAAgG;aAClG,SAAS;aACT,QAAQ,EAAE,SAAS;EAEb,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB;aAE3B,MAAM,EAAE,aAAa;aACrB,IAAI;aACJ,IAAI,EAAE,qYAAqY;aAC3Y,QAAQ,EAAE,6EAA6E;aACvF,SAAS;EAEH,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;aAE7B,MAAM,EAAE,cAAc;aACtB,IAAI;aACJ,IAAI,EAAE,0LAA0L;aAChM,QAAQ,EACN,uHAAuH;aACzH,SAAS;EAEH,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;aAEhC,MAAM,EAAE,sBAAsB;aAC9B,IAAI;aACJ,IAAI,EAAE,oDAAoD;aAC1D,QAAQ,EACN,4FAA4F;aAC9F,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,2BAA2B;aACnC,IAAI;aACJ,IAAI,EAAE,mEAAmE;aACzE,QAAQ,EAAE,qFAAqF;aAC/F,SAAS;aACT,QAAQ,EAAE,SAAS;EAEb,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;aAEjC,MAAM,EAAE,uBAAuB;aAC/B,IAAI;aACJ,IAAI,EAAE,qKAAqK;aAC3K,QAAQ,EACN,iGAAiG;aACnG,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,4BAA4B;aACpC,IAAI;aACJ,IAAI,EAAE,4EAA4E;aAClF,QAAQ,EACN,uFAAuF;aACzF,SAAS;aACT,QAAQ,EAAE,SAAS;;aAGnB,MAAM,EAAE,yBAAyB;aACjC,IAAI;aACJ,IAAI,EAAE,6UAA6U;aACnV,QAAQ,EACN,iNAAiN;aACnN,SAAS;aACT,QAAQ,EAAE,SAAS;EAEb,CAAC"}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Canonical error contracts
|
|
3
|
-
*
|
|
4
|
-
* input rejections
|
|
5
|
-
*
|
|
2
|
+
* @fileoverview Canonical error contracts: the failure modes the NCBI, Europe
|
|
3
|
+
* PMC, and OpenAlex services throw and tools declare in `errors[]`, plus the
|
|
4
|
+
* input rejections some tools raise before any upstream call. Unpaywall has no
|
|
5
|
+
* array here: its one caller, `pubmed_fetch_fulltext`, catches every Unpaywall
|
|
6
|
+
* failure and reports it as a tier outcome, so no Unpaywall reason reaches a
|
|
7
|
+
* caller as an error.
|
|
6
8
|
*
|
|
7
|
-
* Service-layer code
|
|
8
|
-
* `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* handler-level `ctx.fail
|
|
9
|
+
* Service-layer code stamps `data: { reason }` on its throws, adding its own
|
|
10
|
+
* `data.recovery` only where the hint depends on runtime detail. When a failure
|
|
11
|
+
* with no hint of its own reaches a tool or resource that declares the reason,
|
|
12
|
+
* the framework fills that entry's `recovery` in as `data.recovery.hint` — the
|
|
13
|
+
* same hint a handler-level `ctx.fail` gets. A tool's error also renders the
|
|
14
|
+
* hint as a `Recovery:` line in its `content[]` text (dropped when the message
|
|
15
|
+
* already contains it); a resource's carries it only in the JSON-RPC error's
|
|
16
|
+
* `data`.
|
|
12
17
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
18
|
+
* Definitions import the contract arrays directly and spread them into their
|
|
19
|
+
* `errors: [...]` declarations to surface the failure modes to the LLM.
|
|
15
20
|
* Every service-array entry carries `thrownBy: 'service'` so the linter's
|
|
16
21
|
* `error-contract-unthrown` check skips it while still checking the handler's
|
|
17
22
|
* own reasons. Spread a service array only into a tool whose handler lets that
|
|
@@ -38,16 +43,24 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
38
43
|
{
|
|
39
44
|
reason: 'ncbi_unreachable',
|
|
40
45
|
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
41
|
-
when: 'NCBI E-utilities
|
|
42
|
-
recovery: 'Retry after a brief delay; NCBI
|
|
46
|
+
when: 'NCBI E-utilities failed on every attempt the retry budget allowed — retries ran out, or the next backoff would overrun the total deadline.',
|
|
47
|
+
recovery: 'Retry after a brief delay; NCBI failed on every attempt this call made.',
|
|
48
|
+
retryable: true,
|
|
49
|
+
thrownBy: 'service',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
reason: 'ncbi_rate_limited',
|
|
53
|
+
code: JsonRpcErrorCode.RateLimited,
|
|
54
|
+
when: 'NCBI answered HTTP 429 (too many requests) and the call stopped on it — retries ran out, the next backoff would overrun the total deadline, or the Retry-After NCBI named outlasts the time left or the 30-second backoff cap.',
|
|
55
|
+
recovery: "Wait as long as `retryAfter` says when present (a number of seconds or an HTTP date), otherwise a few seconds, then retry; NCBI is throttling this server's requests. If the server runs without NCBI_API_KEY, setting it raises the E-utilities ceiling from about 3 to about 10 requests per second and shortens the default gap between requests — an operator setting in the server's environment, not a tool input.",
|
|
43
56
|
retryable: true,
|
|
44
57
|
thrownBy: 'service',
|
|
45
58
|
},
|
|
46
59
|
{
|
|
47
60
|
reason: 'ncbi_deadline_exceeded',
|
|
48
61
|
code: JsonRpcErrorCode.Timeout,
|
|
49
|
-
when: '
|
|
50
|
-
recovery: '
|
|
62
|
+
when: 'The total NCBI request deadline expired before NCBI answered successfully — mid-request, while queued, or during a retry backoff.',
|
|
63
|
+
recovery: 'Retry after a brief delay; NCBI may be under temporary load. For a large batch, split it into smaller calls.',
|
|
51
64
|
retryable: true,
|
|
52
65
|
thrownBy: 'service',
|
|
53
66
|
},
|
|
@@ -69,34 +82,32 @@ export const NCBI_SERVICE_ERRORS = [
|
|
|
69
82
|
},
|
|
70
83
|
];
|
|
71
84
|
/**
|
|
72
|
-
* Input failure the
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* cannot happen.
|
|
85
|
+
* Input failure the free-text query tools reject before any call is made —
|
|
86
|
+
* the NCBI query tools and `pubmed_europepmc_search` alike. Declared here
|
|
87
|
+
* rather than inside a service array so only the tools that actually throw it
|
|
88
|
+
* advertise it — a tool that takes no free-text query can never produce it,
|
|
89
|
+
* and declaring it there would advertise a failure mode that cannot happen.
|
|
77
90
|
*
|
|
78
91
|
* Unlike the service arrays in this module, this one is thrown from a tool
|
|
79
|
-
* handler via `ctx.fail('blank_query', …)`, so its
|
|
80
|
-
* through `ctx.recoveryFor` and it stays out of {@link ServiceErrorReason}.
|
|
92
|
+
* handler via `ctx.fail('blank_query', …)`, so its entry carries no `thrownBy`.
|
|
81
93
|
*/
|
|
82
|
-
export const
|
|
94
|
+
export const QUERY_INPUT_ERRORS = [
|
|
83
95
|
{
|
|
84
96
|
reason: 'blank_query',
|
|
85
97
|
code: JsonRpcErrorCode.ValidationError,
|
|
86
|
-
when: 'The query
|
|
87
|
-
recovery: 'Supply a nonblank search term
|
|
98
|
+
when: 'The query contains no search term: nothing is left once whitespace and invisible characters such as a zero-width space are disregarded. pubmed_search_articles and pubmed_europepmc_search first decode HTML entities and also disregard markup and parentheses, so `()`, `<b></b>`, and ` ` hold no term there; pubmed_search_articles also disregards bracketed field tags such as `[pdat]`.',
|
|
99
|
+
recovery: 'Supply a nonblank search term; retrying the same blank input will not help.',
|
|
88
100
|
retryable: false,
|
|
89
101
|
},
|
|
90
102
|
];
|
|
91
103
|
/**
|
|
92
104
|
* Input failure the identifier-conversion path rejects before any call is made.
|
|
93
|
-
* Separate from {@link
|
|
105
|
+
* Separate from {@link QUERY_INPUT_ERRORS} for the same reason that array
|
|
94
106
|
* is separate from the service contracts: only a tool that takes caller-supplied
|
|
95
107
|
* identifiers can produce it.
|
|
96
108
|
*
|
|
97
|
-
* Thrown from a tool handler via `ctx.fail('malformed_id', …)`, so its
|
|
98
|
-
*
|
|
99
|
-
* {@link ServiceErrorReason}.
|
|
109
|
+
* Thrown from a tool handler via `ctx.fail('malformed_id', …)`, so its entry
|
|
110
|
+
* carries no `thrownBy`.
|
|
100
111
|
*/
|
|
101
112
|
export const NCBI_ID_INPUT_ERRORS = [
|
|
102
113
|
{
|
|
@@ -107,23 +118,6 @@ export const NCBI_ID_INPUT_ERRORS = [
|
|
|
107
118
|
retryable: false,
|
|
108
119
|
},
|
|
109
120
|
];
|
|
110
|
-
/**
|
|
111
|
-
* Failure modes the Unpaywall service layer can surface. A tool whose handler
|
|
112
|
-
* lets `getUnpaywallService()` failures propagate spreads these into its
|
|
113
|
-
* `errors[]`. None does today: `pubmed_fetch_fulltext`, the only consumer,
|
|
114
|
-
* folds every Unpaywall failure into its tier chain, so the entries serve the
|
|
115
|
-
* service's own `recoveryFor` hints.
|
|
116
|
-
*/
|
|
117
|
-
export const UNPAYWALL_SERVICE_ERRORS = [
|
|
118
|
-
{
|
|
119
|
-
reason: 'unpaywall_unreachable',
|
|
120
|
-
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
121
|
-
when: 'Unpaywall was unreachable when resolving a DOI or fetching content.',
|
|
122
|
-
recovery: 'Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.',
|
|
123
|
-
retryable: true,
|
|
124
|
-
thrownBy: 'service',
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
121
|
/**
|
|
128
122
|
* Failure modes the OpenAlex service layer can surface. Tools whose handler
|
|
129
123
|
* lets `getOpenAlexService()` / `getOpenAlexServiceOptional()` failures
|
|
@@ -140,7 +134,7 @@ export const OPENALEX_SERVICE_ERRORS = [
|
|
|
140
134
|
},
|
|
141
135
|
{
|
|
142
136
|
reason: 'openalex_invalid_response',
|
|
143
|
-
code: JsonRpcErrorCode.
|
|
137
|
+
code: JsonRpcErrorCode.ServiceUnavailable,
|
|
144
138
|
when: 'OpenAlex returned a body that could not be parsed (invalid JSON).',
|
|
145
139
|
recovery: 'Retry the request; OpenAlex returned a malformed response that could not be parsed.',
|
|
146
140
|
retryable: true,
|
|
@@ -172,36 +166,10 @@ export const EUROPEPMC_SERVICE_ERRORS = [
|
|
|
172
166
|
{
|
|
173
167
|
reason: 'europepmc_invalid_input',
|
|
174
168
|
code: JsonRpcErrorCode.ValidationError,
|
|
175
|
-
when: 'Europe PMC rejected the request input — an error message
|
|
176
|
-
recovery:
|
|
169
|
+
when: 'Europe PMC rejected the request input — an error message in place of results, an empty response to a sort with an undocumented field or no asc/desc direction, or a pagination cursor it cannot read (an empty response on the last attempt the retry budget allows, or a second HTTP 503 when the first page of the same query is served).',
|
|
170
|
+
recovery: "Fix the rejected input before retrying — the query, the sort, or the cursorMark (pass the previous response's `nextCursorMark` verbatim, or `*` to restart); the same input will most likely be rejected again.",
|
|
177
171
|
retryable: false,
|
|
178
172
|
thrownBy: 'service',
|
|
179
173
|
},
|
|
180
174
|
];
|
|
181
|
-
const REASON_TO_RECOVERY = new Map([
|
|
182
|
-
...NCBI_SERVICE_ERRORS,
|
|
183
|
-
...UNPAYWALL_SERVICE_ERRORS,
|
|
184
|
-
...EUROPEPMC_SERVICE_ERRORS,
|
|
185
|
-
...OPENALEX_SERVICE_ERRORS,
|
|
186
|
-
].map((entry) => [entry.reason, entry.recovery]));
|
|
187
|
-
/**
|
|
188
|
-
* Service-layer counterpart to `ctx.recoveryFor`. Returns `{ recovery: { hint } }`
|
|
189
|
-
* for the contract reason. Use at every service throw that stamps a `reason` so
|
|
190
|
-
* the wire payload carries the same actionable hint the LLM gets from
|
|
191
|
-
* handler-level `ctx.fail`.
|
|
192
|
-
*
|
|
193
|
-
* The parameter is constrained to `ServiceErrorReason`, so typos fail at compile
|
|
194
|
-
* time. The runtime guard catches the impossible case where the reason union
|
|
195
|
-
* and the recovery map drift apart in future edits.
|
|
196
|
-
*
|
|
197
|
-
* @example
|
|
198
|
-
* throw serviceUnavailable(msg, { reason: 'ncbi_unreachable', ...recoveryFor('ncbi_unreachable') });
|
|
199
|
-
*/
|
|
200
|
-
export function recoveryFor(reason) {
|
|
201
|
-
const hint = REASON_TO_RECOVERY.get(reason);
|
|
202
|
-
if (hint === undefined) {
|
|
203
|
-
throw new Error(`recoveryFor: no recovery hint registered for reason "${reason}"`);
|
|
204
|
-
}
|
|
205
|
-
return { recovery: { hint } };
|
|
206
|
-
}
|
|
207
175
|
//# sourceMappingURL=error-contracts.js.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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"}
|
|
@@ -45,8 +45,8 @@ export declare class EuropePmcApiClient {
|
|
|
45
45
|
* query is HTTP 200 with `hitCount: 0`. A 404 is reclassified from its
|
|
46
46
|
* status-mapped `NotFound` to `ServiceUnavailable` so the service retries it.
|
|
47
47
|
* A retryable 5xx keeps its code, so a 504 stays `Timeout`. Only a failure
|
|
48
|
-
* that ends up `ServiceUnavailable` carries `europepmc_unreachable
|
|
49
|
-
*
|
|
48
|
+
* that ends up `ServiceUnavailable` carries `europepmc_unreachable`, the one
|
|
49
|
+
* code that reason is declared for. An upstream
|
|
50
50
|
* `retryAfter` is kept. A 429 and every other 4xx pass through unchanged, as
|
|
51
51
|
* does a 501, whose `data.retryable: false` keeps it out of the retry loop.
|
|
52
52
|
* (#152)
|
|
@@ -84,6 +84,12 @@ export declare class EuropePmcApiClient {
|
|
|
84
84
|
* optional source filter. EPMC's query syntax supports `SRC:"X"` field tokens —
|
|
85
85
|
* the requested sources are OR-joined into a parenthesized clause and ANDed
|
|
86
86
|
* with the caller's query.
|
|
87
|
+
*
|
|
88
|
+
* A single source is repeated (`SRC:"MED" OR SRC:"MED"`). Europe PMC matches
|
|
89
|
+
* nothing when a query is exactly one `EXT_ID` clause ANDed with one `SRC`
|
|
90
|
+
* clause and either value is quoted; the repeated clause matches quoted and
|
|
91
|
+
* unquoted `EXT_ID` alike and returns the same total as a lone clause for any
|
|
92
|
+
* other query. Multi-source clauses are sent as they are. (#175)
|
|
87
93
|
*/
|
|
88
94
|
export declare function buildSearchQuery(params: Pick<EuropePmcSearchParams, 'query' | 'sources'>): string;
|
|
89
95
|
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,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,CA2C3D;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;CAavB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG,MAAM,CAOjG"}
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
12
12
|
import { fetchWithTimeout, logger, requestContextService } from '@cyanheads/mcp-ts-core/utils';
|
|
13
|
-
import { recoveryFor } from '../error-contracts.js';
|
|
14
13
|
import { EUROPEPMC_API_BASE } from './types.js';
|
|
15
14
|
const USER_AGENT = 'pubmed-mcp-server (+https://github.com/cyanheads/pubmed-mcp-server)';
|
|
16
15
|
/**
|
|
@@ -36,8 +35,8 @@ export class EuropePmcApiClient {
|
|
|
36
35
|
* query is HTTP 200 with `hitCount: 0`. A 404 is reclassified from its
|
|
37
36
|
* status-mapped `NotFound` to `ServiceUnavailable` so the service retries it.
|
|
38
37
|
* A retryable 5xx keeps its code, so a 504 stays `Timeout`. Only a failure
|
|
39
|
-
* that ends up `ServiceUnavailable` carries `europepmc_unreachable
|
|
40
|
-
*
|
|
38
|
+
* that ends up `ServiceUnavailable` carries `europepmc_unreachable`, the one
|
|
39
|
+
* code that reason is declared for. An upstream
|
|
41
40
|
* `retryAfter` is kept. A 429 and every other 4xx pass through unchanged, as
|
|
42
41
|
* does a 501, whose `data.retryable: false` keeps it out of the retry loop.
|
|
43
42
|
* (#152)
|
|
@@ -66,14 +65,13 @@ export class EuropePmcApiClient {
|
|
|
66
65
|
throw new McpError(code, `Europe PMC search request failed: ${error.message}`, {
|
|
67
66
|
...(code === JsonRpcErrorCode.ServiceUnavailable && {
|
|
68
67
|
reason: 'europepmc_unreachable',
|
|
69
|
-
...recoveryFor('europepmc_unreachable'),
|
|
70
68
|
}),
|
|
71
69
|
status,
|
|
72
70
|
...(error.data?.retryAfter !== undefined && { retryAfter: error.data.retryAfter }),
|
|
73
71
|
}, { cause: error });
|
|
74
72
|
}
|
|
75
73
|
const msg = error instanceof Error ? error.message : String(error);
|
|
76
|
-
throw serviceUnavailable(`Europe PMC search request failed: ${msg}`, { reason: 'europepmc_unreachable'
|
|
74
|
+
throw serviceUnavailable(`Europe PMC search request failed: ${msg}`, { reason: 'europepmc_unreachable' }, { cause: error });
|
|
77
75
|
}
|
|
78
76
|
return response.text();
|
|
79
77
|
}
|
|
@@ -112,7 +110,7 @@ export class EuropePmcApiClient {
|
|
|
112
110
|
throw error;
|
|
113
111
|
}
|
|
114
112
|
const msg = error instanceof Error ? error.message : String(error);
|
|
115
|
-
throw serviceUnavailable(`Europe PMC fullTextXML request failed: ${msg}`, { reason: 'europepmc_unreachable', epmcId
|
|
113
|
+
throw serviceUnavailable(`Europe PMC fullTextXML request failed: ${msg}`, { reason: 'europepmc_unreachable', epmcId }, { cause: error });
|
|
116
114
|
}
|
|
117
115
|
const xml = await response.text();
|
|
118
116
|
if (!xml.trim()) {
|
|
@@ -159,7 +157,7 @@ export class EuropePmcApiClient {
|
|
|
159
157
|
if (error instanceof McpError)
|
|
160
158
|
throw error;
|
|
161
159
|
const msg = error instanceof Error ? error.message : String(error);
|
|
162
|
-
throw serviceUnavailable(`Europe PMC links request failed: ${msg}`, { reason: 'europepmc_unreachable'
|
|
160
|
+
throw serviceUnavailable(`Europe PMC links request failed: ${msg}`, { reason: 'europepmc_unreachable' }, { cause: error });
|
|
163
161
|
}
|
|
164
162
|
return response.text();
|
|
165
163
|
}
|
|
@@ -183,12 +181,19 @@ export class EuropePmcApiClient {
|
|
|
183
181
|
* optional source filter. EPMC's query syntax supports `SRC:"X"` field tokens —
|
|
184
182
|
* the requested sources are OR-joined into a parenthesized clause and ANDed
|
|
185
183
|
* with the caller's query.
|
|
184
|
+
*
|
|
185
|
+
* A single source is repeated (`SRC:"MED" OR SRC:"MED"`). Europe PMC matches
|
|
186
|
+
* nothing when a query is exactly one `EXT_ID` clause ANDed with one `SRC`
|
|
187
|
+
* clause and either value is quoted; the repeated clause matches quoted and
|
|
188
|
+
* unquoted `EXT_ID` alike and returns the same total as a lone clause for any
|
|
189
|
+
* other query. Multi-source clauses are sent as they are. (#175)
|
|
186
190
|
*/
|
|
187
191
|
export function buildSearchQuery(params) {
|
|
188
192
|
const base = params.query.trim();
|
|
189
193
|
if (!params.sources || params.sources.length === 0)
|
|
190
194
|
return base;
|
|
191
|
-
const
|
|
195
|
+
const sources = params.sources.length === 1 ? [...params.sources, ...params.sources] : params.sources;
|
|
196
|
+
const sourceClause = sources.map((s) => `SRC:"${s}"`).join(' OR ');
|
|
192
197
|
return `(${base}) AND (${sourceClause})`;
|
|
193
198
|
}
|
|
194
199
|
//# sourceMappingURL=api-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE/F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* A search's retry boundary covers fetch and response classification, so
|
|
15
15
|
* Europe PMC's intermittent empty `{ version }` envelope is retried like an
|
|
16
16
|
* HTTP outage; only a sort or cursor that explains it is reported as bad input.
|
|
17
|
+
* Europe PMC answers an unreadable cursor with the HTTP 503 it uses for an
|
|
18
|
+
* outage, so a 503 on a caller-supplied cursor is checked against the first
|
|
19
|
+
* page of the same query, and the cursor is blamed only when a retry draws
|
|
20
|
+
* the 503 again while that first page was served.
|
|
17
21
|
* A search reports the effective query Europe PMC echoes back, or — with no
|
|
18
22
|
* echo — the source-filtered query it sent.
|
|
19
23
|
*
|
|
@@ -51,12 +55,26 @@ export declare class EuropePmcService {
|
|
|
51
55
|
* The retry boundary covers the fetch and the response classification
|
|
52
56
|
* together, so an empty envelope retries like any other transient failure
|
|
53
57
|
* rather than surfacing after the loop has already returned. (#159)
|
|
58
|
+
*
|
|
59
|
+
* The first HTTP 503 on a caller-supplied cursor draws one probe of the same
|
|
60
|
+
* query's first page, and the cursor is retried either way: one 503 is not
|
|
61
|
+
* enough to tell a bad cursor from a transient outage. A later 503 fails the
|
|
62
|
+
* search at once as non-retryable input only when that probe was served;
|
|
63
|
+
* otherwise it goes on through the ordinary retry path. A 503 with no retry
|
|
64
|
+
* left is never probed, since nothing could use the answer. (#180)
|
|
54
65
|
*/
|
|
55
66
|
search(params: EuropePmcSearchParams): Promise<EuropePmcSearchResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Whether Europe PMC serves the first page of the search in `params`: one
|
|
69
|
+
* request (`cursorMark: '*'`, `pageSize: 1`), paced like any other, with no
|
|
70
|
+
* retries of its own. Any failure reads as not served — except the caller's
|
|
71
|
+
* cancellation, which propagates.
|
|
72
|
+
*/
|
|
73
|
+
private firstPageServed;
|
|
56
74
|
/**
|
|
57
75
|
* One search attempt: fetch, parse, and classify the body. `isLastAttempt`
|
|
58
|
-
* lets an empty envelope
|
|
59
|
-
*
|
|
76
|
+
* lets an empty envelope on the last attempt the retry budget allows be
|
|
77
|
+
* attributed to a caller-supplied cursor.
|
|
60
78
|
*/
|
|
61
79
|
private searchOnce;
|
|
62
80
|
/**
|
|
@@ -129,6 +147,11 @@ export declare class EuropePmcService {
|
|
|
129
147
|
* signal reaches the queue wait and the backoff sleep. `execute` receives the
|
|
130
148
|
* one-based attempt number.
|
|
131
149
|
*
|
|
150
|
+
* `reclassify`, when given, sees each failed attempt after its queue slot is
|
|
151
|
+
* released and before the retry decision, with the attempt's signal and its
|
|
152
|
+
* one-based number. The error it returns is the one the loop judges, so it
|
|
153
|
+
* can end the retries by returning a non-transient error.
|
|
154
|
+
*
|
|
132
155
|
* A caller abort rethrows the caller's own reason; an exhausted failure is
|
|
133
156
|
* reshaped by {@link toServiceError}, after the loop.
|
|
134
157
|
*/
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AASH,OAAO,EAGL,KAAK,KAAK,EAGX,MAAM,8BAA8B,CAAC;AAMtC,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,4CAA4C,CAAC;AAEzF,OAAO,EAAoB,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,EACV,uBAAuB,EAEvB,kBAAkB,EAElB,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EAErB,qBAAqB,EACrB,eAAe,EAChB,MAAM,YAAY,CAAC;AAsGpB;;;;;;;;;;GAUG;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,KAAK,EACZ,UAAU,EAAE,MAAM,EAQpC;IAED;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA+BpE;IAED;;;;;OAKG;YACW,eAAe;IAgB7B;;;;OAIG;YACW,UAAU;IAyHxB;;;;;;;;;;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,CASlC;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;IAkE/B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CA4BlD;IAED;;;;;;;;;;;;;;;OAeG;YACW,QAAQ;CA8CvB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAyB3C;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE"}
|