@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.
Files changed (154) hide show
  1. package/AGENTS.md +24 -6
  2. package/CLAUDE.md +24 -6
  3. package/README.md +7 -7
  4. package/changelog/2.0.x/2.0.0.md +32 -0
  5. package/changelog/2.0.x/2.0.1.md +32 -0
  6. package/changelog/2.1.x/2.1.0.md +17 -0
  7. package/changelog/2.1.x/2.1.1.md +29 -0
  8. package/changelog/2.1.x/2.1.2.md +18 -0
  9. package/changelog/2.1.x/2.1.3.md +10 -0
  10. package/changelog/2.1.x/2.1.4.md +12 -0
  11. package/changelog/2.1.x/2.1.5.md +18 -0
  12. package/changelog/2.1.x/2.1.6.md +15 -0
  13. package/changelog/2.10.x/2.10.0.md +17 -0
  14. package/changelog/2.10.x/2.10.1.md +14 -0
  15. package/changelog/2.10.x/2.10.10.md +22 -0
  16. package/changelog/2.10.x/2.10.11.md +21 -0
  17. package/changelog/2.10.x/2.10.12.md +14 -0
  18. package/changelog/2.10.x/2.10.13.md +28 -0
  19. package/changelog/2.10.x/2.10.14.md +29 -0
  20. package/changelog/2.10.x/2.10.15.md +28 -0
  21. package/changelog/2.10.x/2.10.2.md +13 -0
  22. package/changelog/2.10.x/2.10.3.md +26 -0
  23. package/changelog/2.10.x/2.10.4.md +11 -0
  24. package/changelog/2.10.x/2.10.5.md +31 -0
  25. package/changelog/2.10.x/2.10.6.md +30 -0
  26. package/changelog/2.10.x/2.10.7.md +17 -0
  27. package/changelog/2.10.x/2.10.8.md +22 -0
  28. package/changelog/2.10.x/2.10.9.md +15 -0
  29. package/changelog/2.2.x/2.2.0.md +67 -0
  30. package/changelog/2.2.x/2.2.1.md +10 -0
  31. package/changelog/2.2.x/2.2.2.md +20 -0
  32. package/changelog/2.2.x/2.2.3.md +17 -0
  33. package/changelog/2.2.x/2.2.4.md +34 -0
  34. package/changelog/2.2.x/2.2.5.md +10 -0
  35. package/changelog/2.2.x/2.2.6.md +17 -0
  36. package/changelog/2.3.x/2.3.0.md +27 -0
  37. package/changelog/2.3.x/2.3.1.md +15 -0
  38. package/changelog/2.3.x/2.3.10.md +20 -0
  39. package/changelog/2.3.x/2.3.11.md +21 -0
  40. package/changelog/2.3.x/2.3.2.md +27 -0
  41. package/changelog/2.3.x/2.3.3.md +38 -0
  42. package/changelog/2.3.x/2.3.4.md +21 -0
  43. package/changelog/2.3.x/2.3.5.md +24 -0
  44. package/changelog/2.3.x/2.3.6.md +26 -0
  45. package/changelog/2.3.x/2.3.7.md +31 -0
  46. package/changelog/2.3.x/2.3.8.md +19 -0
  47. package/changelog/2.3.x/2.3.9.md +22 -0
  48. package/changelog/2.4.x/2.4.0.md +34 -0
  49. package/changelog/2.4.x/2.4.1.md +32 -0
  50. package/changelog/2.5.x/2.5.0.md +35 -0
  51. package/changelog/2.5.x/2.5.1.md +32 -0
  52. package/changelog/2.5.x/2.5.2.md +23 -0
  53. package/changelog/2.5.x/2.5.3.md +22 -0
  54. package/changelog/2.5.x/2.5.5.md +52 -0
  55. package/changelog/2.5.x/2.5.6.md +33 -0
  56. package/changelog/2.6.x/2.6.0.md +32 -0
  57. package/changelog/2.6.x/2.6.1.md +26 -0
  58. package/changelog/2.6.x/2.6.10.md +16 -0
  59. package/changelog/2.6.x/2.6.11.md +24 -0
  60. package/changelog/2.6.x/2.6.12.md +29 -0
  61. package/changelog/2.6.x/2.6.2.md +23 -0
  62. package/changelog/2.6.x/2.6.3.md +17 -0
  63. package/changelog/2.6.x/2.6.4.md +21 -0
  64. package/changelog/2.6.x/2.6.5.md +30 -0
  65. package/changelog/2.6.x/2.6.6.md +25 -0
  66. package/changelog/2.6.x/2.6.7.md +37 -0
  67. package/changelog/2.6.x/2.6.8.md +15 -0
  68. package/changelog/2.6.x/2.6.9.md +36 -0
  69. package/changelog/2.7.x/2.7.0.md +41 -0
  70. package/changelog/2.7.x/2.7.1.md +21 -0
  71. package/changelog/2.7.x/2.7.10.md +13 -0
  72. package/changelog/2.7.x/2.7.11.md +15 -0
  73. package/changelog/2.7.x/2.7.2.md +22 -0
  74. package/changelog/2.7.x/2.7.3.md +18 -0
  75. package/changelog/2.7.x/2.7.4.md +15 -0
  76. package/changelog/2.7.x/2.7.5.md +34 -0
  77. package/changelog/2.7.x/2.7.6.md +14 -0
  78. package/changelog/2.7.x/2.7.7.md +14 -0
  79. package/changelog/2.7.x/2.7.8.md +18 -0
  80. package/changelog/2.7.x/2.7.9.md +16 -0
  81. package/changelog/2.8.x/2.8.0.md +23 -0
  82. package/changelog/2.9.x/2.9.0.md +21 -0
  83. package/changelog/2.9.x/2.9.1.md +12 -0
  84. package/changelog/2.9.x/2.9.10.md +15 -0
  85. package/changelog/2.9.x/2.9.2.md +21 -0
  86. package/changelog/2.9.x/2.9.3.md +11 -0
  87. package/changelog/2.9.x/2.9.4.md +24 -0
  88. package/changelog/2.9.x/2.9.5.md +20 -0
  89. package/changelog/2.9.x/2.9.6.md +22 -0
  90. package/changelog/2.9.x/2.9.7.md +26 -0
  91. package/changelog/2.9.x/2.9.8.md +15 -0
  92. package/changelog/2.9.x/2.9.9.md +35 -0
  93. package/changelog/template.md +151 -0
  94. package/dist/index.js +1 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts +7 -2
  97. package/dist/mcp-server/tools/definitions/convert-ids.tool.d.ts.map +1 -1
  98. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts +7 -2
  99. package/dist/mcp-server/tools/definitions/fetch-articles.tool.d.ts.map +1 -1
  100. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +14 -5
  101. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
  102. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +0 -60
  103. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
  104. package/dist/mcp-server/tools/definitions/find-related.tool.js +4 -4
  105. package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
  106. package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts +7 -2
  107. package/dist/mcp-server/tools/definitions/format-citations.tool.d.ts.map +1 -1
  108. package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts +7 -2
  109. package/dist/mcp-server/tools/definitions/lookup-citation.tool.d.ts.map +1 -1
  110. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +7 -2
  111. package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
  112. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts +6 -3
  113. package/dist/mcp-server/tools/definitions/pubmed-europepmc-fetch.tool.d.ts.map +1 -1
  114. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +6 -3
  115. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -1
  116. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +1 -1
  117. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -1
  118. package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +7 -2
  119. package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
  120. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +7 -2
  121. package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
  122. package/dist/services/error-contracts.d.ts +30 -13
  123. package/dist/services/error-contracts.d.ts.map +1 -1
  124. package/dist/services/error-contracts.js +30 -13
  125. package/dist/services/error-contracts.js.map +1 -1
  126. package/dist/services/europe-pmc/api-client.d.ts +14 -2
  127. package/dist/services/europe-pmc/api-client.d.ts.map +1 -1
  128. package/dist/services/europe-pmc/api-client.js +30 -4
  129. package/dist/services/europe-pmc/api-client.js.map +1 -1
  130. package/dist/services/europe-pmc/europe-pmc-service.d.ts +20 -1
  131. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
  132. package/dist/services/europe-pmc/europe-pmc-service.js +98 -50
  133. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
  134. package/dist/services/ncbi/ncbi-service.d.ts +18 -26
  135. package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
  136. package/dist/services/ncbi/ncbi-service.js +111 -127
  137. package/dist/services/ncbi/ncbi-service.js.map +1 -1
  138. package/dist/services/ncbi/request-queue.d.ts +22 -30
  139. package/dist/services/ncbi/request-queue.d.ts.map +1 -1
  140. package/dist/services/ncbi/request-queue.js +29 -128
  141. package/dist/services/ncbi/request-queue.js.map +1 -1
  142. package/dist/services/ncbi/response-handler.d.ts +14 -1
  143. package/dist/services/ncbi/response-handler.d.ts.map +1 -1
  144. package/dist/services/ncbi/response-handler.js +65 -9
  145. package/dist/services/ncbi/response-handler.js.map +1 -1
  146. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  147. package/dist/services/openalex/openalex-service.js +3 -10
  148. package/dist/services/openalex/openalex-service.js.map +1 -1
  149. package/package.json +20 -11
  150. package/server.json +3 -3
  151. package/dist/services/retry-policy.d.ts +0 -18
  152. package/dist/services/retry-policy.d.ts.map +0 -1
  153. package/dist/services/retry-policy.js +0 -21
  154. 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 that consume
21
- * `getNcbiService()` should spread these into their own `errors[]` so the
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: 'Local NCBI request queue is at capacity.';
28
- readonly recovery: 'Retry after 1-2 seconds; the request queue hit the NCBI rate limit.';
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 that consume
104
- * `getOpenAlexService()` / `getOpenAlexServiceOptional()` should spread
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 that consume
122
- * `getEuropePmcService()` should spread these into their `errors[]`.
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 was unreachable after all retry attempts.';
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 (empty query, unknown sort field, malformed parameter).';
140
- readonly recovery: 'Adjust the input — usually the query or sort field — before retrying; the same input will be rejected again.';
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;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;aAE5B,MAAM,EAAE,YAAY;aACpB,IAAI;aACJ,IAAI,EAAE,0CAA0C;aAChD,QAAQ,EAAE,qEAAqE;aAC/E,SAAS;;aAGT,MAAM,EAAE,kBAAkB;aAC1B,IAAI;aACJ,IAAI,EAAE,2DAA2D;aACjE,QAAQ,EAAE,4EAA4E;aACtF,SAAS;;aAGT,MAAM,EAAE,wBAAwB;aAChC,IAAI;aACJ,IAAI,EAAE,iEAAiE;aACvE,QAAQ,EAAE,+DAA+D;aACzE,SAAS;;aAGT,MAAM,EAAE,uBAAuB;aAC/B,IAAI;aACJ,IAAI,EAAE,mEAAmE;aACzE,QAAQ,EAAE,iFAAiF;aAC3F,SAAS;;aAGT,MAAM,EAAE,yBAAyB;aACjC,IAAI;aACJ,IAAI,EAAE,uEAAuE;aAC7E,QAAQ,EACN,gGAAgG;aAClG,SAAS;EAEH,CAAC;AAEX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB;aAEhC,MAAM,EAAE,aAAa;aACrB,IAAI;aACJ,IAAI,EAAE,yIAAyI;aAC/I,QAAQ,EACN,2GAA2G;aAC7G,SAAS;EAEH,CAAC;AAEX;;;;;;;;;GASG;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,wBAAwB;aAEjC,MAAM,EAAE,uBAAuB;aAC/B,IAAI;aACJ,IAAI,EAAE,qEAAqE;aAC3E,QAAQ,EACN,gGAAgG;aAClG,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;;aAGT,MAAM,EAAE,2BAA2B;aACnC,IAAI;aACJ,IAAI,EAAE,mEAAmE;aACzE,QAAQ,EAAE,qFAAqF;aAC/F,SAAS;EAEH,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB;aAEjC,MAAM,EAAE,uBAAuB;aAC/B,IAAI;aACJ,IAAI,EAAE,sDAAsD;aAC5D,QAAQ,EACN,iGAAiG;aACnG,SAAS;;aAGT,MAAM,EAAE,4BAA4B;aACpC,IAAI;aACJ,IAAI,EAAE,4EAA4E;aAClF,QAAQ,EACN,uFAAuF;aACzF,SAAS;;aAGT,MAAM,EAAE,yBAAyB;aACjC,IAAI;aACJ,IAAI,EAAE,+FAA+F;aACrG,QAAQ,EACN,8GAA8G;aAChH,SAAS;EAEH,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,GAC9C,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,GACnD,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,GACnD,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC;AAWvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG;IAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAMtF"}
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 that consume
21
- * `getNcbiService()` should spread these into their own `errors[]` so the
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: 'Local NCBI request queue is at capacity.',
29
- recovery: 'Retry after 1-2 seconds; the request queue hit the NCBI rate limit.',
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 that consume
116
- * `getOpenAlexService()` / `getOpenAlexServiceOptional()` should spread
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 that consume
137
- * `getEuropePmcService()` should spread these into their `errors[]`.
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 was unreachable after all retry attempts.',
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 (empty query, unknown sort field, malformed parameter).',
158
- recovery: 'Adjust the input — usually the query or sort field — before retrying; the same input will be rejected again.',
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":"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"}
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 and
4
- * fullTextXML calls. Retry logic lives in `EuropePmcService`.
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;;;;;GAKG;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;;;;OAIG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB3D;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
+ {"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 and
4
- * fullTextXML calls. Retry logic lives in `EuropePmcService`.
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
- throw error;
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":"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"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;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,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAA8B,MAAM,YAAY,CAAC;AAE5E,MAAM,UAAU,GAAG,qEAAqE,CAAC;AAezF;;;;;;;;GAQG;AACH,MAAM,OAAO,kBAAkB;IACA,MAAM;IAAnC,YAA6B,MAAgC;sBAAhC,MAAM;IAA6B,CAAC;IAEjE;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,MAAM,CAAC,MAA6B;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,qBAAqB,CAAC,oBAAoB,CAAC;YACrD,SAAS,EAAE,iBAAiB;YAC5B,iBAAiB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE;SAC3C,CAAC,CAAC;QAEH,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE;gBACjE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE;gBACjE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;aAChD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC;gBAClC,MAAM,QAAQ,GACZ,MAAM,KAAK,GAAG;oBACd,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC;gBACnF,IAAI,CAAC,QAAQ;oBAAE,MAAM,KAAK,CAAC;gBAC3B,MAAM,IAAI,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC/E,MAAM,IAAI,QAAQ,CAChB,IAAI,EACJ,qCAAqC,KAAK,CAAC,OAAO,EAAE,EACpD;oBACE,GAAG,CAAC,IAAI,KAAK,gBAAgB,CAAC,kBAAkB,IAAI;wBAClD,MAAM,EAAE,uBAAuB;wBAC/B,GAAG,WAAW,CAAC,uBAAuB,CAAC;qBACxC,CAAC;oBACF,MAAM;oBACN,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;iBACnF,EACD,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,MAAM,kBAAkB,CACtB,qCAAqC,GAAG,EAAE,EAC1C,EAAE,MAAM,EAAE,uBAAuB,EAAE,GAAG,WAAW,CAAC,uBAAuB,CAAC,EAAE,EAC5E,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,MAAoB;QACpD,MAAM,GAAG,GAAG,GAAG,kBAAkB,IAAI,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC;QAC9E,MAAM,GAAG,GAAG,qBAAqB,CAAC,oBAAoB,CAAC;YACrD,SAAS,EAAE,sBAAsB;YACjC,iBAAiB,EAAE,EAAE,MAAM,EAAE;SAC9B,CAAC,CAAC;QAEH,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE;gBACjE,OAAO,EAAE;oBACP,MAAM,EAAE,sCAAsC;oBAC9C,YAAY,EAAE,UAAU;iBACzB;gBACD,gBAAgB,EAAE,CAAC,GAAG,CAAC;gBACvB,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;gBAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,CAAC,QAAQ,EAAE,CAAC;oBAC7C,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;gBACtF,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,MAAM,kBAAkB,CACtB,0CAA0C,GAAG,EAAE,EAC/C,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,uBAAuB,CAAC,EAAE,EACpF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,CACV,gDAAgD,EAChD,qBAAqB,CAAC,oBAAoB,CAAC;gBACzC,SAAS,EAAE,2BAA2B;gBACtC,iBAAiB,EAAE,EAAE,MAAM,EAAE;aAC9B,CAAC,CACH,CAAC;YACF,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;QACtF,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAChC,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,IAAY,EAAE,QAAgB,EAAE,IAAY,EAAE,MAAoB;QAC1E,MAAM,GAAG,GAAG,GAAG,kBAAkB,QAAQ,kBAAkB,CAAC,IAAI,CAAC,mBAAmB,IAAI,aAAa,QAAQ,cAAc,CAAC;QAC5H,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,IAAY,EAAE,QAAgB,EAAE,IAAY,EAAE,MAAoB;QAC3E,MAAM,GAAG,GAAG,GAAG,kBAAkB,QAAQ,kBAAkB,CAAC,IAAI,CAAC,oBAAoB,IAAI,aAAa,QAAQ,cAAc,CAAC;QAC7H,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,iFAAiF;IACzE,KAAK,CAAC,cAAc,CAC1B,GAAW,EACX,SAAiB,EACjB,IAAY,EACZ,MAAoB;QAEpB,MAAM,GAAG,GAAG,qBAAqB,CAAC,oBAAoB,CAAC;YACrD,SAAS;YACT,iBAAiB,EAAE,EAAE,IAAI,EAAE;SAC5B,CAAC,CAAC;QAEH,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE;gBACjE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,UAAU,EAAE;gBACjE,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,QAAQ;gBAAE,MAAM,KAAK,CAAC;YAC3C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,MAAM,kBAAkB,CACtB,oCAAoC,GAAG,EAAE,EACzC,EAAE,MAAM,EAAE,uBAAuB,EAAE,GAAG,WAAW,CAAC,uBAAuB,CAAC,EAAE,EAC5E,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAEO,cAAc,CAAC,MAA6B;QAClD,MAAM,WAAW,GAA2B;YAC1C,KAAK,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;YACpC,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,MAAM;YACvC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;YACvC,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,GAAG;SACrC,CAAC;QACF,IAAI,MAAM,CAAC,IAAI;YAAE,WAAW,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAE7D,OAAO,GAAG,kBAAkB,WAAW,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;IACvF,CAAC;IAED;;;;OAIG;IACK,gBAAgB,CAAC,MAA6B;QACpD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAChE,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1E,OAAO,IAAI,IAAI,UAAU,YAAY,GAAG,CAAC;IAC3C,CAAC;CACF"}
@@ -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;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,4CAA4C,CAAC;AAGzF,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;AAkDpB;;;;;;;;;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;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA6F1E;IAED;;;;;;;;;;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;;;;OAIG;YACW,SAAS;CAoDxB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAyB3C;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE"}
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"}