@cyanheads/pubmed-mcp-server 2.6.11 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.md +36 -14
  3. package/dist/config/server-config.d.ts +6 -0
  4. package/dist/config/server-config.d.ts.map +1 -1
  5. package/dist/config/server-config.js +52 -0
  6. package/dist/config/server-config.js.map +1 -1
  7. package/dist/index.js +19 -12
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +71 -23
  10. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +671 -141
  12. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +0 -1
  14. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/find-related.tool.js +21 -41
  16. package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +87 -0
  18. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -0
  19. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +217 -0
  20. package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -0
  21. package/dist/services/error-contracts.d.ts +18 -1
  22. package/dist/services/error-contracts.d.ts.map +1 -1
  23. package/dist/services/error-contracts.js +21 -4
  24. package/dist/services/error-contracts.js.map +1 -1
  25. package/dist/services/europe-pmc/api-client.d.ts +47 -0
  26. package/dist/services/europe-pmc/api-client.d.ts.map +1 -0
  27. package/dist/services/europe-pmc/api-client.js +123 -0
  28. package/dist/services/europe-pmc/api-client.js.map +1 -0
  29. package/dist/services/europe-pmc/europe-pmc-service.d.ts +71 -0
  30. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -0
  31. package/dist/services/europe-pmc/europe-pmc-service.js +243 -0
  32. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -0
  33. package/dist/services/europe-pmc/request-queue.d.ts +33 -0
  34. package/dist/services/europe-pmc/request-queue.d.ts.map +1 -0
  35. package/dist/services/europe-pmc/request-queue.js +107 -0
  36. package/dist/services/europe-pmc/request-queue.js.map +1 -0
  37. package/dist/services/europe-pmc/types.d.ts +123 -0
  38. package/dist/services/europe-pmc/types.d.ts.map +1 -0
  39. package/dist/services/europe-pmc/types.js +16 -0
  40. package/dist/services/europe-pmc/types.js.map +1 -0
  41. package/dist/services/ncbi/ncbi-service.d.ts +6 -7
  42. package/dist/services/ncbi/ncbi-service.d.ts.map +1 -1
  43. package/dist/services/ncbi/ncbi-service.js +11 -11
  44. package/dist/services/ncbi/ncbi-service.js.map +1 -1
  45. package/dist/services/ncbi/request-queue.d.ts +26 -11
  46. package/dist/services/ncbi/request-queue.d.ts.map +1 -1
  47. package/dist/services/ncbi/request-queue.js +94 -52
  48. package/dist/services/ncbi/request-queue.js.map +1 -1
  49. package/dist/services/ncbi/response-handler.js +1 -1
  50. package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
  51. package/dist/services/unpaywall/unpaywall-service.js +14 -13
  52. package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
  53. package/package.json +7 -6
  54. package/server.json +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubmed-europepmc-search.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/pubmed-europepmc-search.tool.ts"],"names":[],"mappings":"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"}
@@ -62,12 +62,29 @@ export declare const UNPAYWALL_SERVICE_ERRORS: readonly [{
62
62
  readonly recovery: "Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.";
63
63
  readonly retryable: true;
64
64
  }];
65
+ /**
66
+ * Failure modes the Europe PMC service layer can surface. Tools that consume
67
+ * `getEuropePmcService()` should spread these into their `errors[]`.
68
+ */
69
+ export declare const EUROPEPMC_SERVICE_ERRORS: readonly [{
70
+ readonly reason: "europepmc_unreachable";
71
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
72
+ readonly when: "Europe PMC was unreachable after all retry attempts.";
73
+ readonly recovery: "Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.";
74
+ readonly retryable: true;
75
+ }, {
76
+ readonly reason: "europepmc_invalid_response";
77
+ readonly code: JsonRpcErrorCode.SerializationError;
78
+ readonly when: "Europe PMC returned a body that could not be parsed (invalid JSON or XML).";
79
+ readonly recovery: "Retry the request; Europe PMC returned a malformed response that could not be parsed.";
80
+ readonly retryable: true;
81
+ }];
65
82
  /**
66
83
  * Reason identifier union for type-safe authoring on the service layer.
67
84
  * Tool handlers get a tighter typed union from `ctx.fail` / `ctx.recoveryFor`
68
85
  * via the framework — this is the service-side fallback.
69
86
  */
70
- export type ServiceErrorReason = (typeof NCBI_SERVICE_ERRORS)[number]['reason'] | (typeof UNPAYWALL_SERVICE_ERRORS)[number]['reason'];
87
+ export type ServiceErrorReason = (typeof NCBI_SERVICE_ERRORS)[number]['reason'] | (typeof UNPAYWALL_SERVICE_ERRORS)[number]['reason'] | (typeof EUROPEPMC_SERVICE_ERRORS)[number]['reason'];
71
88
  /**
72
89
  * Service-layer counterpart to `ctx.recoveryFor`. Returns `{ recovery: { hint } }`
73
90
  * for the contract reason. Use at every service throw that stamps a `reason` so
@@ -1 +1 @@
1
- {"version":3,"file":"error-contracts.d.ts","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCtB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;EAS3B,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,CAAC;AASxD;;;;;;;;;;;;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":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCtB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;EAS3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;EAiB3B,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,CAAC;AAQxD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG;IAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAMtF"}
@@ -70,10 +70,27 @@ export const UNPAYWALL_SERVICE_ERRORS = [
70
70
  retryable: true,
71
71
  },
72
72
  ];
73
- const REASON_TO_RECOVERY = new Map([...NCBI_SERVICE_ERRORS, ...UNPAYWALL_SERVICE_ERRORS].map((entry) => [
74
- entry.reason,
75
- entry.recovery,
76
- ]));
73
+ /**
74
+ * Failure modes the Europe PMC service layer can surface. Tools that consume
75
+ * `getEuropePmcService()` should spread these into their `errors[]`.
76
+ */
77
+ export const EUROPEPMC_SERVICE_ERRORS = [
78
+ {
79
+ reason: 'europepmc_unreachable',
80
+ code: JsonRpcErrorCode.ServiceUnavailable,
81
+ when: 'Europe PMC was unreachable after all retry attempts.',
82
+ recovery: 'Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.',
83
+ retryable: true,
84
+ },
85
+ {
86
+ reason: 'europepmc_invalid_response',
87
+ code: JsonRpcErrorCode.SerializationError,
88
+ when: 'Europe PMC returned a body that could not be parsed (invalid JSON or XML).',
89
+ recovery: 'Retry the request; Europe PMC returned a malformed response that could not be parsed.',
90
+ retryable: true,
91
+ },
92
+ ];
93
+ const REASON_TO_RECOVERY = new Map([...NCBI_SERVICE_ERRORS, ...UNPAYWALL_SERVICE_ERRORS, ...EUROPEPMC_SERVICE_ERRORS].map((entry) => [entry.reason, entry.recovery]));
77
94
  /**
78
95
  * Service-layer counterpart to `ctx.recoveryFor`. Returns `{ recovery: { hint } }`
79
96
  * for the contract reason. Use at every service throw that stamps a `reason` so
@@ -1 +1 @@
1
- {"version":3,"file":"error-contracts.js","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC;QACE,MAAM,EAAE,YAAY;QACpB,IAAI,EAAE,gBAAgB,CAAC,WAAW;QAClC,IAAI,EAAE,0CAA0C;QAChD,QAAQ,EAAE,qEAAqE;QAC/E,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,kBAAkB;QAC1B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,2DAA2D;QACjE,QAAQ,EAAE,4EAA4E;QACtF,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,wBAAwB;QAChC,IAAI,EAAE,gBAAgB,CAAC,OAAO;QAC9B,IAAI,EAAE,iEAAiE;QACvE,QAAQ,EAAE,+DAA+D;QACzE,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,mEAAmE;QACzE,QAAQ,EAAE,iFAAiF;QAC3F,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,yBAAyB;QACjC,IAAI,EAAE,gBAAgB,CAAC,QAAQ;QAC/B,IAAI,EAAE,uEAAuE;QAC7E,QAAQ,EACN,gGAAgG;QAClG,SAAS,EAAE,KAAK;KACjB;CACO,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC;QACE,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,qEAAqE;QAC3E,QAAQ,EACN,gGAAgG;QAClG,SAAS,EAAE,IAAI;KAChB;CACO,CAAC;AAWX,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAChC,CAAC,GAAG,mBAAmB,EAAE,GAAG,wBAAwB,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IACnE,KAAK,CAAC,MAAM;IACZ,KAAK,CAAC,QAAQ;CACf,CAAC,CACH,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,MAA0B;IACpD,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,wDAAwD,MAAM,GAAG,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;AAChC,CAAC"}
1
+ {"version":3,"file":"error-contracts.js","sourceRoot":"","sources":["../../src/services/error-contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC;QACE,MAAM,EAAE,YAAY;QACpB,IAAI,EAAE,gBAAgB,CAAC,WAAW;QAClC,IAAI,EAAE,0CAA0C;QAChD,QAAQ,EAAE,qEAAqE;QAC/E,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,kBAAkB;QAC1B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,2DAA2D;QACjE,QAAQ,EAAE,4EAA4E;QACtF,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,wBAAwB;QAChC,IAAI,EAAE,gBAAgB,CAAC,OAAO;QAC9B,IAAI,EAAE,iEAAiE;QACvE,QAAQ,EAAE,+DAA+D;QACzE,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,mEAAmE;QACzE,QAAQ,EAAE,iFAAiF;QAC3F,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,yBAAyB;QACjC,IAAI,EAAE,gBAAgB,CAAC,QAAQ;QAC/B,IAAI,EAAE,uEAAuE;QAC7E,QAAQ,EACN,gGAAgG;QAClG,SAAS,EAAE,KAAK;KACjB;CACO,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC;QACE,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,qEAAqE;QAC3E,QAAQ,EACN,gGAAgG;QAClG,SAAS,EAAE,IAAI;KAChB;CACO,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC;QACE,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,sDAAsD;QAC5D,QAAQ,EACN,iGAAiG;QACnG,SAAS,EAAE,IAAI;KAChB;IACD;QACE,MAAM,EAAE,4BAA4B;QACpC,IAAI,EAAE,gBAAgB,CAAC,kBAAkB;QACzC,IAAI,EAAE,4EAA4E;QAClF,QAAQ,EACN,uFAAuF;QACzF,SAAS,EAAE,IAAI;KAChB;CACO,CAAC;AAYX,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAChC,CAAC,GAAG,mBAAmB,EAAE,GAAG,wBAAwB,EAAE,GAAG,wBAAwB,CAAC,CAAC,GAAG,CACpF,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAC1C,CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,MAA0B;IACpD,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,wDAAwD,MAAM,GAAG,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
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`.
5
+ * @module src/services/europe-pmc/api-client
6
+ */
7
+ import { type EuropePmcSearchParams } from './types.js';
8
+ export interface EuropePmcApiClientConfig {
9
+ email?: string;
10
+ timeoutMs: number;
11
+ }
12
+ /**
13
+ * Outcome of a fullTextXML fetch attempt. Wraps the typed contract so service
14
+ * callers don't need to inspect raw `Response` objects.
15
+ */
16
+ export type EuropePmcFullTextFetchResult = {
17
+ kind: 'found';
18
+ xml: string;
19
+ } | {
20
+ kind: 'not-available';
21
+ reason: string;
22
+ };
23
+ /** Low-level HTTP client for Europe PMC. Single-attempt — retries upstream. */
24
+ export declare class EuropePmcApiClient {
25
+ private readonly config;
26
+ constructor(config: EuropePmcApiClientConfig);
27
+ /**
28
+ * Execute a search. Returns the raw JSON response body as a string so
29
+ * `EuropePmcService` can parse and surface SerializationError consistently
30
+ * when the body is malformed.
31
+ */
32
+ search(params: EuropePmcSearchParams): Promise<string>;
33
+ /**
34
+ * Fetch the JATS full-text XML for an EPMC record by its internal id.
35
+ * Returns `{ kind: 'not-available' }` for 404 — EPMC has the record but
36
+ * doesn't publish a full-text XML for it (very common for preprints).
37
+ */
38
+ fullTextXml(epmcId: string, signal?: AbortSignal): Promise<EuropePmcFullTextFetchResult>;
39
+ private buildSearchUrl;
40
+ /**
41
+ * Combine the caller's query with an optional source filter. EPMC's query
42
+ * syntax supports `SRC:"X"` field tokens — we OR-join the requested sources
43
+ * into a parenthesized clause and AND it with the user's query.
44
+ */
45
+ private buildQueryString;
46
+ }
47
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,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,+EAA+E;AAC/E,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,wBAAwB;IAE7D;;;;OAIG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAiC5D;;;;OAIG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAmD9F,OAAO,CAAC,cAAc;IActB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAMzB"}
@@ -0,0 +1,123 @@
1
+ /**
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`.
5
+ * @module src/services/europe-pmc/api-client
6
+ */
7
+ import { McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
8
+ import { fetchWithTimeout, httpErrorFromResponse, logger, requestContextService, } from '@cyanheads/mcp-ts-core/utils';
9
+ import { recoveryFor } from '../../services/error-contracts.js';
10
+ import { EUROPEPMC_API_BASE } from './types.js';
11
+ const USER_AGENT = 'pubmed-mcp-server (+https://github.com/cyanheads/pubmed-mcp-server)';
12
+ /** Low-level HTTP client for Europe PMC. Single-attempt — retries upstream. */
13
+ export class EuropePmcApiClient {
14
+ config;
15
+ constructor(config) {
16
+ this.config = config;
17
+ }
18
+ /**
19
+ * Execute a search. Returns the raw JSON response body as a string so
20
+ * `EuropePmcService` can parse and surface SerializationError consistently
21
+ * when the body is malformed.
22
+ */
23
+ async search(params) {
24
+ const url = this.buildSearchUrl(params);
25
+ const ctx = requestContextService.createRequestContext({
26
+ operation: 'EuropePmcSearch',
27
+ query: params.query,
28
+ });
29
+ let response;
30
+ try {
31
+ response = await fetchWithTimeout(url, this.config.timeoutMs, ctx, {
32
+ headers: { Accept: 'application/json', 'User-Agent': USER_AGENT },
33
+ ...(params.signal && { signal: params.signal }),
34
+ });
35
+ }
36
+ catch (error) {
37
+ if (error instanceof McpError)
38
+ throw error;
39
+ const msg = error instanceof Error ? error.message : String(error);
40
+ throw serviceUnavailable(`Europe PMC search request failed: ${msg}`, { reason: 'europepmc_unreachable', ...recoveryFor('europepmc_unreachable') }, { cause: error });
41
+ }
42
+ if (!response.ok) {
43
+ throw await httpErrorFromResponse(response, {
44
+ service: 'Europe PMC',
45
+ data: { url, reason: 'europepmc_unreachable', ...recoveryFor('europepmc_unreachable') },
46
+ });
47
+ }
48
+ return response.text();
49
+ }
50
+ /**
51
+ * Fetch the JATS full-text XML for an EPMC record by its internal id.
52
+ * Returns `{ kind: 'not-available' }` for 404 — EPMC has the record but
53
+ * doesn't publish a full-text XML for it (very common for preprints).
54
+ */
55
+ async fullTextXml(epmcId, signal) {
56
+ const url = `${EUROPEPMC_API_BASE}/${encodeURIComponent(epmcId)}/fullTextXML`;
57
+ const ctx = requestContextService.createRequestContext({
58
+ operation: 'EuropePmcFullTextXml',
59
+ epmcId,
60
+ });
61
+ let response;
62
+ try {
63
+ response = await fetchWithTimeout(url, this.config.timeoutMs, ctx, {
64
+ headers: {
65
+ Accept: 'application/xml, text/xml, */*;q=0.5',
66
+ 'User-Agent': USER_AGENT,
67
+ },
68
+ ...(signal && { signal }),
69
+ });
70
+ }
71
+ catch (error) {
72
+ if (error instanceof McpError)
73
+ throw error;
74
+ const msg = error instanceof Error ? error.message : String(error);
75
+ throw serviceUnavailable(`Europe PMC fullTextXML request failed: ${msg}`, { reason: 'europepmc_unreachable', epmcId, ...recoveryFor('europepmc_unreachable') }, { cause: error });
76
+ }
77
+ if (response.status === 404) {
78
+ return { kind: 'not-available', reason: 'EPMC has no fullTextXML for this record' };
79
+ }
80
+ if (!response.ok) {
81
+ throw await httpErrorFromResponse(response, {
82
+ service: 'Europe PMC fullTextXML',
83
+ data: { epmcId, reason: 'europepmc_unreachable', ...recoveryFor('europepmc_unreachable') },
84
+ });
85
+ }
86
+ const xml = await response.text();
87
+ if (!xml.trim()) {
88
+ logger.debug('Europe PMC returned an empty fullTextXML body.', requestContextService.createRequestContext({
89
+ operation: 'EuropePmcFullTextXmlEmpty',
90
+ epmcId,
91
+ }));
92
+ return { kind: 'not-available', reason: 'EPMC returned an empty fullTextXML body' };
93
+ }
94
+ return { kind: 'found', xml };
95
+ }
96
+ buildSearchUrl(params) {
97
+ const finalParams = {
98
+ query: this.buildQueryString(params),
99
+ format: 'json',
100
+ resultType: params.resultType ?? 'core',
101
+ pageSize: String(params.pageSize ?? 25),
102
+ cursorMark: params.cursorMark ?? '*',
103
+ };
104
+ if (params.sort)
105
+ finalParams.sort = params.sort;
106
+ if (this.config.email)
107
+ finalParams.email = this.config.email;
108
+ return `${EUROPEPMC_API_BASE}/search?${new URLSearchParams(finalParams).toString()}`;
109
+ }
110
+ /**
111
+ * Combine the caller's query with an optional source filter. EPMC's query
112
+ * syntax supports `SRC:"X"` field tokens — we OR-join the requested sources
113
+ * into a parenthesized clause and AND it with the user's query.
114
+ */
115
+ buildQueryString(params) {
116
+ const base = params.query.trim();
117
+ if (!params.sources || params.sources.length === 0)
118
+ return base;
119
+ const sourceClause = params.sources.map((s) => `SRC:"${s}"`).join(' OR ');
120
+ return `(${base}) AND (${sourceClause})`;
121
+ }
122
+ }
123
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @fileoverview Europe PMC service. Wraps the EPMC REST API with rate-limiting,
3
+ * retries, and JATS XML parsing. Two methods: `search()` for keyword discovery
4
+ * across the EPMC corpus (MED/PMC/PPR/PAT/AGR) and `fullTextXml()` for fetching
5
+ * a record's full-text JATS. The XML parser matches NCBI's ordered config so
6
+ * `parsePmcArticle` consumes the result without modification.
7
+ *
8
+ * Optional service: only constructed when `EUROPEPMC_ENABLED=true` (the
9
+ * default). `getEuropePmcService()` returns `undefined` when disabled so
10
+ * callers can skip the chain step gracefully.
11
+ *
12
+ * @module src/services/europe-pmc/europe-pmc-service
13
+ */
14
+ import type { JatsNode } from '../../services/ncbi/parsing/pmc-xml-helpers.js';
15
+ import { EuropePmcApiClient } from './api-client.js';
16
+ import { EuropePmcRequestQueue } from './request-queue.js';
17
+ import type { EuropePmcFullTextResult, EuropePmcSearchParams, EuropePmcSearchResult, EuropePmcSource } from './types.js';
18
+ /**
19
+ * Facade over the Europe PMC REST API. Two methods:
20
+ * - `search()` — keyword search across MED/PMC/PPR/PAT/AGR.
21
+ * - `fullTextXml()` — JATS full text for an EPMC record.
22
+ *
23
+ * Both honor `ctx.signal` for cancellation and retry transient failures with
24
+ * capped exponential backoff plus jitter.
25
+ */
26
+ export declare class EuropePmcService {
27
+ private readonly client;
28
+ private readonly queue;
29
+ private readonly maxRetries;
30
+ private readonly orderedXmlParser;
31
+ constructor(client: EuropePmcApiClient, queue: EuropePmcRequestQueue, maxRetries: number);
32
+ /**
33
+ * Search Europe PMC. Cursor-based pagination — pass `cursorMark: '*'` (or
34
+ * omit) for the first page; pass the returned `nextCursorMark` for the next.
35
+ */
36
+ search(params: EuropePmcSearchParams): Promise<EuropePmcSearchResult>;
37
+ /**
38
+ * Fetch the JATS full text for an EPMC record. Returns:
39
+ * - `{ kind: 'found', xml, epmcId, source }` — JATS XML string usable
40
+ * directly by tool callers that hold their own parser, or via
41
+ * `parseFullTextXml()` for the parsed tree.
42
+ * - `{ kind: 'not-available', reason }` — EPMC has the record but
43
+ * publishes no fullTextXML (404 or empty body).
44
+ */
45
+ fullTextXml(epmcId: string, source: EuropePmcSource, signal?: AbortSignal): Promise<EuropePmcFullTextResult>;
46
+ /**
47
+ * Parse a JATS XML string into the ordered node tree consumed by
48
+ * `parsePmcArticle`. Returns the `<article>` JatsNode, or `undefined` when
49
+ * the body doesn't contain an article element (malformed / empty).
50
+ *
51
+ * Throws `SerializationError` only for fundamentally invalid XML; an
52
+ * article-free but well-formed body returns `undefined` so callers can
53
+ * surface a `no-epmc-fulltext` outcome without a hard failure.
54
+ */
55
+ parseFullTextXml(xml: string): JatsNode | undefined;
56
+ /**
57
+ * Retry wrapper for transient errors. Mirrors NCBI's `withRetry` minus the
58
+ * service-level deadline — EPMC requests are cheaper individually and the
59
+ * caller (typically `ctx.signal`) bounds the total chain.
60
+ */
61
+ private withRetry;
62
+ }
63
+ /**
64
+ * Initialize the Europe PMC service when enabled. Safe to call regardless of
65
+ * config — `EUROPEPMC_ENABLED=false` leaves the service unset so callers see
66
+ * `undefined` and skip the chain step.
67
+ */
68
+ export declare function initEuropePmcService(): void;
69
+ /** Returns the initialized service, or `undefined` when EPMC is disabled. */
70
+ export declare function getEuropePmcService(): EuropePmcService | undefined;
71
+ //# sourceMappingURL=europe-pmc-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"europe-pmc-service.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/europe-pmc-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,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,qBAAqB,EAErB,qBAAqB,EACrB,eAAe,EAChB,MAAM,YAAY,CAAC;AA2BpB;;;;;;;GAOG;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;gBAG1B,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,qBAAqB,EAC5B,UAAU,EAAE,MAAM;IAmBrC;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA0C3E;;;;;;;OAOG;IACG,WAAW,CACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,uBAAuB,CAAC;IAkBnC;;;;;;;;OAQG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAgCnD;;;;OAIG;YACW,SAAS;CAsDxB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAuB3C;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * @fileoverview Europe PMC service. Wraps the EPMC REST API with rate-limiting,
3
+ * retries, and JATS XML parsing. Two methods: `search()` for keyword discovery
4
+ * across the EPMC corpus (MED/PMC/PPR/PAT/AGR) and `fullTextXml()` for fetching
5
+ * a record's full-text JATS. The XML parser matches NCBI's ordered config so
6
+ * `parsePmcArticle` consumes the result without modification.
7
+ *
8
+ * Optional service: only constructed when `EUROPEPMC_ENABLED=true` (the
9
+ * default). `getEuropePmcService()` returns `undefined` when disabled so
10
+ * callers can skip the chain step gracefully.
11
+ *
12
+ * @module src/services/europe-pmc/europe-pmc-service
13
+ */
14
+ import { internalError, JsonRpcErrorCode, McpError, serializationError, } from '@cyanheads/mcp-ts-core/errors';
15
+ import { logger, requestContextService } from '@cyanheads/mcp-ts-core/utils';
16
+ import { XMLParser, XMLValidator } from 'fast-xml-parser';
17
+ import { getServerConfig } from '../../config/server-config.js';
18
+ import { recoveryFor } from '../../services/error-contracts.js';
19
+ import { ensureArray } from '../../services/ncbi/parsing/xml-helpers.js';
20
+ import { EuropePmcApiClient } from './api-client.js';
21
+ import { EuropePmcRequestQueue } from './request-queue.js';
22
+ /** Retryable transient codes — same set NCBI uses. */
23
+ const RETRYABLE_CODES = new Set([
24
+ JsonRpcErrorCode.ServiceUnavailable,
25
+ JsonRpcErrorCode.Timeout,
26
+ JsonRpcErrorCode.RateLimited,
27
+ ]);
28
+ const MAX_BACKOFF_MS = 30_000;
29
+ function abortableSleep(ms, signal) {
30
+ if (!signal)
31
+ return new Promise((r) => setTimeout(r, ms));
32
+ if (signal.aborted)
33
+ return Promise.reject(signal.reason);
34
+ return new Promise((resolve, reject) => {
35
+ const onAbort = () => {
36
+ clearTimeout(timer);
37
+ reject(signal.reason);
38
+ };
39
+ const timer = setTimeout(() => {
40
+ signal.removeEventListener('abort', onAbort);
41
+ resolve();
42
+ }, ms);
43
+ signal.addEventListener('abort', onAbort, { once: true });
44
+ });
45
+ }
46
+ /**
47
+ * Facade over the Europe PMC REST API. Two methods:
48
+ * - `search()` — keyword search across MED/PMC/PPR/PAT/AGR.
49
+ * - `fullTextXml()` — JATS full text for an EPMC record.
50
+ *
51
+ * Both honor `ctx.signal` for cancellation and retry transient failures with
52
+ * capped exponential backoff plus jitter.
53
+ */
54
+ export class EuropePmcService {
55
+ client;
56
+ queue;
57
+ maxRetries;
58
+ orderedXmlParser;
59
+ constructor(client, queue, maxRetries) {
60
+ this.client = client;
61
+ this.queue = queue;
62
+ this.maxRetries = maxRetries;
63
+ /**
64
+ * EPMC's fullTextXML is JATS Z39.96 — same DTD PMC uses — so the parser
65
+ * config mirrors `NcbiResponseHandler.orderedXmlParser`. `preserveOrder`
66
+ * keeps inline mixed content readable; `trimValues: false` retains spaces
67
+ * between text and inline children.
68
+ */
69
+ this.orderedXmlParser = new XMLParser({
70
+ preserveOrder: true,
71
+ ignoreAttributes: false,
72
+ attributeNamePrefix: '@_',
73
+ parseTagValue: true,
74
+ trimValues: false,
75
+ processEntities: true,
76
+ htmlEntities: true,
77
+ });
78
+ }
79
+ /**
80
+ * Search Europe PMC. Cursor-based pagination — pass `cursorMark: '*'` (or
81
+ * omit) for the first page; pass the returned `nextCursorMark` for the next.
82
+ */
83
+ async search(params) {
84
+ const text = await this.queue.enqueue(() => this.withRetry(() => this.client.search(params), 'search', params.signal), 'search', params.signal);
85
+ let parsed;
86
+ try {
87
+ parsed = JSON.parse(text);
88
+ }
89
+ catch (error) {
90
+ throw serializationError('Failed to parse Europe PMC search JSON response.', {
91
+ reason: 'europepmc_invalid_response',
92
+ responseSnippet: text.substring(0, 200),
93
+ ...recoveryFor('europepmc_invalid_response'),
94
+ }, { cause: error });
95
+ }
96
+ const hits = ensureArray(parsed.resultList?.result);
97
+ const echoed = parsed.request?.queryString ?? params.query;
98
+ // EPMC echoes back the input cursor mark on the final page, so absence of
99
+ // an explicit "next" or equality with the request's cursor marks the end.
100
+ const cursorMark = parsed.request?.cursorMark ?? params.cursorMark ?? '*';
101
+ const nextCursor = parsed.nextCursorMark && parsed.nextCursorMark !== cursorMark
102
+ ? parsed.nextCursorMark
103
+ : undefined;
104
+ return {
105
+ hits,
106
+ hitCount: parsed.hitCount ?? hits.length,
107
+ ...(nextCursor && { nextCursorMark: nextCursor }),
108
+ cursorMark,
109
+ query: echoed,
110
+ };
111
+ }
112
+ /**
113
+ * Fetch the JATS full text for an EPMC record. Returns:
114
+ * - `{ kind: 'found', xml, epmcId, source }` — JATS XML string usable
115
+ * directly by tool callers that hold their own parser, or via
116
+ * `parseFullTextXml()` for the parsed tree.
117
+ * - `{ kind: 'not-available', reason }` — EPMC has the record but
118
+ * publishes no fullTextXML (404 or empty body).
119
+ */
120
+ async fullTextXml(epmcId, source, signal) {
121
+ const outcome = await this.queue.enqueue(() => this.withRetry(() => this.client.fullTextXml(epmcId, signal), `fullTextXml(${epmcId})`, signal), `fullTextXml(${epmcId})`, signal);
122
+ if (outcome.kind === 'not-available') {
123
+ return { kind: 'not-available', reason: outcome.reason };
124
+ }
125
+ return { kind: 'found', xml: outcome.xml, epmcId, source };
126
+ }
127
+ /**
128
+ * Parse a JATS XML string into the ordered node tree consumed by
129
+ * `parsePmcArticle`. Returns the `<article>` JatsNode, or `undefined` when
130
+ * the body doesn't contain an article element (malformed / empty).
131
+ *
132
+ * Throws `SerializationError` only for fundamentally invalid XML; an
133
+ * article-free but well-formed body returns `undefined` so callers can
134
+ * surface a `no-epmc-fulltext` outcome without a hard failure.
135
+ */
136
+ parseFullTextXml(xml) {
137
+ const validationResult = XMLValidator.validate(xml.replace(/<!DOCTYPE[^>]*>/gi, ''));
138
+ if (validationResult !== true) {
139
+ throw serializationError('Received invalid XML from Europe PMC.', {
140
+ reason: 'europepmc_invalid_response',
141
+ responseSnippet: xml.substring(0, 200),
142
+ ...recoveryFor('europepmc_invalid_response'),
143
+ });
144
+ }
145
+ let parsed;
146
+ try {
147
+ parsed = this.orderedXmlParser.parse(xml);
148
+ }
149
+ catch (error) {
150
+ const parserError = error instanceof Error ? error.message : String(error);
151
+ throw serializationError(`Failed to parse Europe PMC fullTextXML response: ${parserError}`, {
152
+ reason: 'europepmc_invalid_response',
153
+ parserError,
154
+ responseSnippet: xml.substring(0, 200),
155
+ ...recoveryFor('europepmc_invalid_response'),
156
+ }, { cause: error });
157
+ }
158
+ if (!Array.isArray(parsed))
159
+ return;
160
+ const nodes = parsed;
161
+ return nodes.find((n) => 'article' in n);
162
+ }
163
+ /**
164
+ * Retry wrapper for transient errors. Mirrors NCBI's `withRetry` minus the
165
+ * service-level deadline — EPMC requests are cheaper individually and the
166
+ * caller (typically `ctx.signal`) bounds the total chain.
167
+ */
168
+ async withRetry(execute, label, signal) {
169
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
170
+ if (signal?.aborted)
171
+ throw signal.reason;
172
+ try {
173
+ return await execute();
174
+ }
175
+ catch (error) {
176
+ if (signal?.aborted)
177
+ throw signal.reason;
178
+ if (!(error instanceof McpError))
179
+ throw error;
180
+ if (!RETRYABLE_CODES.has(error.code))
181
+ throw error;
182
+ if (attempt < this.maxRetries) {
183
+ const baseDelay = Math.min(1000 * 2 ** attempt, MAX_BACKOFF_MS);
184
+ const jitter = baseDelay * (0.75 + 0.5 * Math.random());
185
+ const retryDelay = Math.round(jitter);
186
+ logger.warning(`Europe PMC ${label} failed. Retrying (${attempt + 1}/${this.maxRetries}) in ${retryDelay}ms.`, requestContextService.createRequestContext({
187
+ operation: 'EuropePmcRetry',
188
+ label,
189
+ attempt: attempt + 1,
190
+ retryDelay,
191
+ }));
192
+ await abortableSleep(retryDelay, signal);
193
+ continue;
194
+ }
195
+ const attempts = this.maxRetries + 1;
196
+ const msg = error instanceof Error ? error.message : String(error);
197
+ throw new McpError(error.code, `${msg} (failed after ${attempts} attempts)`, {
198
+ reason: 'europepmc_unreachable',
199
+ label,
200
+ attempts,
201
+ ...recoveryFor('europepmc_unreachable'),
202
+ }, { cause: error });
203
+ }
204
+ }
205
+ throw internalError('Europe PMC request failed after all retries.', {
206
+ reason: 'europepmc_unreachable',
207
+ label,
208
+ ...recoveryFor('europepmc_unreachable'),
209
+ });
210
+ }
211
+ }
212
+ // ─── Init / Accessor ────────────────────────────────────────────────────────
213
+ let _service;
214
+ /**
215
+ * Initialize the Europe PMC service when enabled. Safe to call regardless of
216
+ * config — `EUROPEPMC_ENABLED=false` leaves the service unset so callers see
217
+ * `undefined` and skip the chain step.
218
+ */
219
+ export function initEuropePmcService() {
220
+ const config = getServerConfig();
221
+ if (!config.europepmcEnabled) {
222
+ logger.info('Europe PMC service disabled (EUROPEPMC_ENABLED=false).');
223
+ return;
224
+ }
225
+ const client = new EuropePmcApiClient({
226
+ timeoutMs: config.europepmcTimeoutMs,
227
+ ...(config.europepmcEmail && { email: config.europepmcEmail }),
228
+ });
229
+ const queue = new EuropePmcRequestQueue(config.europepmcRequestDelayMs);
230
+ _service = new EuropePmcService(client, queue, config.europepmcMaxRetries);
231
+ logger.info('Europe PMC service initialized.', requestContextService.createRequestContext({
232
+ operation: 'EuropePmcInit',
233
+ requestDelayMs: config.europepmcRequestDelayMs,
234
+ maxRetries: config.europepmcMaxRetries,
235
+ timeoutMs: config.europepmcTimeoutMs,
236
+ hasEmail: !!config.europepmcEmail,
237
+ }));
238
+ }
239
+ /** Returns the initialized service, or `undefined` when EPMC is disabled. */
240
+ export function getEuropePmcService() {
241
+ return _service;
242
+ }
243
+ //# sourceMappingURL=europe-pmc-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"europe-pmc-service.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/europe-pmc-service.ts"],"names":[],"mappings":"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"}