@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
package/CLAUDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** @cyanheads/pubmed-mcp-server
4
- **Version:** 2.6.11
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.9.0`
4
+ **Version:** 2.6.12
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.9.1`
6
6
  **Engines:** Bun ≥1.3.2, Node ≥24.0.0
7
7
 
8
8
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  <div align="center">
2
2
  <h1>@cyanheads/pubmed-mcp-server</h1>
3
- <p><b>MCP server for the NCBI E-utilities API. Search PubMed, fetch article metadata and full text, generate citations, explore MeSH terms, and discover related research. STDIO or Streamable HTTP.</b>
4
- <div>9 Tools • 1 Resource • 1 Prompt</div>
3
+ <p><b>Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms via MCP. STDIO or Streamable HTTP.</b>
4
+ <div>10 Tools • 1 Resource • 1 Prompt</div>
5
5
  </p>
6
6
  </div>
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![npm](https://img.shields.io/npm/v/@cyanheads/pubmed-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pubmed-mcp-server) [![Version](https://img.shields.io/badge/Version-2.6.11-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
10
+ [![npm](https://img.shields.io/npm/v/@cyanheads/pubmed-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pubmed-mcp-server) [![Version](https://img.shields.io/badge/Version-2.7.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
11
11
 
12
12
  [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.2-blueviolet.svg?style=flat-square)](https://bun.sh/)
13
13
 
@@ -23,13 +23,14 @@
23
23
 
24
24
  ## Tools
25
25
 
26
- Nine tools for working with PubMed and NCBI data:
26
+ 10 tools for working with PubMed, PubMed Central, and Europe PMC data:
27
27
 
28
28
  | Tool | Description |
29
29
  |:---|:---|
30
30
  | `pubmed_search_articles` | Search PubMed with full query syntax, field-specific filters, date ranges, pagination, and optional brief summaries |
31
+ | `pubmed_europepmc_search` | Search Europe PMC for preprints, patents, Agricola, and EPMC-only OA records that don't surface in PubMed. Cursor-based pagination. |
31
32
  | `pubmed_fetch_articles` | Fetch full article metadata by PMIDs — abstract, authors, journal, MeSH terms, grants |
32
- | `pubmed_fetch_fulltext` | Fetch full-text articles from PubMed Central, with optional Unpaywall fallback for non-PMC DOIs (HTML Markdown or PDF text) |
33
+ | `pubmed_fetch_fulltext` | Fetch full-text articles via a chain: NCBI PMC EFetch Europe PMC `fullTextXML`Unpaywall. Accepts PMIDs, PMCIDs, or DOIs. |
33
34
  | `pubmed_format_citations` | Generate formatted citations in APA 7th, MLA 9th, BibTeX, or RIS |
34
35
  | `pubmed_find_related` | Find similar articles, citing articles, or references for a given PMID |
35
36
  | `pubmed_spell_check` | Spell-check biomedical queries using NCBI's ESpell service |
@@ -67,18 +68,32 @@ Fetch full article metadata by PubMed IDs.
67
68
 
68
69
  ### `pubmed_fetch_fulltext`
69
70
 
70
- Fetch full-text articles from PubMed Central (PMC), with an optional Unpaywall fallback for articles not in PMC.
71
+ Fetch full-text articles via a three-stage chain: NCBI PMC EFetch Europe PMC `fullTextXML` Unpaywall.
71
72
 
72
- - Accepts PMC IDs directly or PubMed IDs (auto-resolved to PMCIDs via ELink)
73
- - PMC path returns complete article body organized by sections and subsections, plus optional references from back matter
74
- - Unpaywall fallback (enabled by setting `UNPAYWALL_EMAIL`) resolves DOIs to legal open-access copies hosted by publishers or institutional repositories; extracts HTML landing pages to Markdown via Defuddle or PDFs to text via unpdf
75
- - Discriminated output contract `source: "pmc"` (structured sections) or `source: "unpaywall"` (single body + `contentFormat` metadata: `html-markdown` or `pdf-text`)
76
- - Structured unavailable reasons (`no-pmc-fallback-disabled`, `no-doi`, `no-oa`, `fetch-failed`, `parse-failed`, `service-error`) so callers can retry or explain to users without parsing text
73
+ - Accepts exactly one of `pmcids` (direct PMC IDs), `pmids` (PubMed IDs, auto-resolved), or `dois` (covers preprints and EPMC-only OA records that lack PMID/PMCID)
74
+ - NCBI PMC and Europe PMC both return structured JATS; output records origin via `viaSource: "pmc" | "europepmc" | "unpaywall"`
75
+ - Europe PMC layer (enabled by default; disable with `EUROPEPMC_ENABLED=false`) recovers PMC-counterpart records that NCBI PMC EFetch missed, and resolves DOI input to PMC counterparts when one exists. EPMC's `fullTextXML` is PMC-keyed, so preprints (PPR), patents (PAT), and Agricola (AGR) are reachable via `pubmed_europepmc_search` for metadata but have no full text via this chain.
76
+ - Unpaywall layer (enabled by setting `UNPAYWALL_EMAIL`) resolves DOIs to legal OA copies; extracts HTML landing pages to Markdown via Defuddle or PDFs to text via unpdf
77
+ - Discriminated output contract `source: "pmc"` (structured sections, regardless of whether it came from PMC or EPMC) or `source: "unpaywall"` (best-effort body + `contentFormat`: `html-markdown` or `pdf-text`)
78
+ - Structured unavailable reasons (`not-found`, `no-pmc-fallback-disabled`, `no-epmc-fulltext`, `no-doi`, `no-oa`, `fetch-failed`, `parse-failed`, `service-error`) so callers can retry or explain to users without parsing text
79
+ - Each `unavailable` entry carries `idType` (`pmid` / `pmcid` / `doi`) and `triedTiers` — per-tier outcomes (`not-attempted`, `miss`, `no-fulltext`, `service-error`, …) in execution order, so callers can see which stage failed and why
77
80
  - Section filtering by title (case-insensitive match, e.g. `["methods", "results"]`) and configurable max sections apply to PMC output
78
81
  - Up to 10 articles per request
79
82
 
80
83
  ---
81
84
 
85
+ ### `pubmed_europepmc_search`
86
+
87
+ Search Europe PMC (EBI/EMBL-EBI), a broader open-access biomedical corpus than PubMed alone.
88
+
89
+ - Surfaces records PubMed search can't reach: preprints (`source: PPR`), patents (`source: PAT`), Agricola (`source: AGR`), plus everything in PubMed (`MED`) and PMC (`PMC`)
90
+ - Default sources `["MED", "PMC", "PPR"]`; pass `sources` to include `PAT` / `AGR`
91
+ - Cursor-based pagination via `cursorMark` (unlike `pubmed_search_articles`, which uses offset) — `*` for the first page, return `nextCursorMark` for the next
92
+ - Output discriminator on `source` plus optional `pmid` / `pmcId` / `doi` cross-walking
93
+ - Disabled when `EUROPEPMC_ENABLED=false`; tool is not registered in that case
94
+
95
+ ---
96
+
82
97
  ### `pubmed_format_citations`
83
98
 
84
99
  Generate formatted citations for articles.
@@ -97,7 +112,7 @@ Find articles related to a source article via ELink.
97
112
 
98
113
  - Three relationship types: `similar` (content similarity), `cited_by`, `references`
99
114
  - Results enriched with title, authors, publication date, and source via ESummary
100
- - Similarity scores included for `similar` relationship type
115
+ - Results returned in NCBI's relevance order
101
116
 
102
117
  ---
103
118
 
@@ -285,12 +300,18 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
285
300
  | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
286
301
  | `NCBI_API_KEY` | NCBI API key for higher rate limits (10 req/s vs 3 req/s) | none |
287
302
  | `NCBI_ADMIN_EMAIL` | Contact email sent with NCBI requests (recommended by NCBI) | none |
288
- | `NCBI_REQUEST_DELAY_MS` | Delay between NCBI requests in ms | 334 (100 with key) |
303
+ | `NCBI_REQUEST_DELAY_MS` | Minimum gap between NCBI request starts in ms | 334 (100 with key) |
304
+ | `NCBI_MAX_CONCURRENT` | Max concurrent in-flight NCBI requests | `8` |
289
305
  | `NCBI_MAX_RETRIES` | Retry attempts for failed NCBI requests | 6 |
290
306
  | `NCBI_TIMEOUT_MS` | Per-request HTTP timeout in ms | `30000` |
291
307
  | `NCBI_TOTAL_DEADLINE_MS` | Total deadline across all retry attempts for one NCBI call, in ms | `60000` |
292
308
  | `UNPAYWALL_EMAIL` | Contact email for Unpaywall. When set, `pubmed_fetch_fulltext` falls back to Unpaywall open-access copies for non-PMC DOIs | none |
293
309
  | `UNPAYWALL_TIMEOUT_MS` | Per-request HTTP timeout for Unpaywall lookups and content fetches, in ms | `20000` |
310
+ | `EUROPEPMC_ENABLED` | Enable Europe PMC search tool and the `pubmed_fetch_fulltext` JATS fallback chain. Set `false` to disable all EPMC calls and skip tool registration. | `true` |
311
+ | `EUROPEPMC_EMAIL` | Optional contact email sent with Europe PMC requests (EBI courtesy). | none |
312
+ | `EUROPEPMC_REQUEST_DELAY_MS` | Minimum gap between Europe PMC request starts in ms | `200` |
313
+ | `EUROPEPMC_MAX_RETRIES` | Retry attempts for failed Europe PMC requests | `3` |
314
+ | `EUROPEPMC_TIMEOUT_MS` | Per-request HTTP timeout for Europe PMC calls, in ms | `20000` |
294
315
  | `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
295
316
 
296
317
  ## Running the server
@@ -319,10 +340,11 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
319
340
 
320
341
  | Directory | Purpose |
321
342
  |:---|:---|
322
- | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Nine PubMed tools. |
343
+ | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Ten tools across PubMed, PMC, and Europe PMC. |
323
344
  | `src/mcp-server/resources` | Resource definitions. Database info resource. |
324
345
  | `src/mcp-server/prompts` | Prompt definitions. Research plan prompt. |
325
346
  | `src/services/ncbi` | NCBI E-utilities service layer — API client, queue, parser, formatter. |
347
+ | `src/services/europe-pmc` | Europe PMC service — search + `fullTextXML` JATS retrieval. Reuses the NCBI JATS parser. |
326
348
  | `src/services/unpaywall` | Unpaywall service — DOI → OA location resolution and content fetch (HTML/PDF). |
327
349
  | `src/config` | Server-specific environment variable parsing and validation with Zod. |
328
350
  | `tests/` | Unit and integration tests, mirroring the `src/` structure. |
@@ -10,11 +10,17 @@ declare const ServerConfigSchema: z.ZodObject<{
10
10
  toolIdentifier: z.ZodDefault<z.ZodString>;
11
11
  adminEmail: z.ZodPreprocess<z.ZodOptional<z.ZodEmail>>;
12
12
  requestDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
13
+ maxConcurrent: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
13
14
  maxRetries: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
14
15
  timeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
15
16
  totalDeadlineMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
16
17
  unpaywallEmail: z.ZodPreprocess<z.ZodOptional<z.ZodEmail>>;
17
18
  unpaywallTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
19
+ europepmcEnabled: z.ZodDefault<z.ZodPreprocess<z.ZodBoolean>>;
20
+ europepmcEmail: z.ZodPreprocess<z.ZodOptional<z.ZodEmail>>;
21
+ europepmcRequestDelayMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
22
+ europepmcMaxRetries: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
23
+ europepmcTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
18
24
  }, z.core.$strip>;
19
25
  export type ServerConfig = z.infer<typeof ServerConfigSchema>;
20
26
  export declare function getServerConfig(): ServerConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAU3C,QAAA,MAAM,kBAAkB;;;;;;;;;;iBA2BtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAwB9C"}
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAyB3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;iBA2DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CA8B9C"}
@@ -12,11 +12,33 @@ import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
12
12
  * validation instead of being interpreted as "no admin email configured".
13
13
  */
14
14
  const emptyAsUndefined = (v) => (v === '' ? undefined : v);
15
+ /**
16
+ * Parse a string env var as a boolean. `z.coerce.boolean()` is unusable for
17
+ * env vars because it applies JavaScript truthy semantics — `"false"` coerces
18
+ * to `true`. This mirrors the framework's `envBoolean` so `EUROPEPMC_ENABLED=false`
19
+ * actually disables the service.
20
+ */
21
+ const envBoolean = z.preprocess((v) => {
22
+ if (typeof v === 'string') {
23
+ const s = v.trim().toLowerCase();
24
+ if (s === 'true' || s === '1')
25
+ return true;
26
+ if (s === 'false' || s === '0' || s === '')
27
+ return false;
28
+ }
29
+ return v;
30
+ }, z.boolean());
15
31
  const ServerConfigSchema = z.object({
16
32
  apiKey: z.preprocess(emptyAsUndefined, z.string().optional()).describe('NCBI API key'),
17
33
  toolIdentifier: z.string().default('pubmed-mcp-server').describe('NCBI tool identifier'),
18
34
  adminEmail: z.preprocess(emptyAsUndefined, z.email().optional()).describe('Admin contact email'),
19
35
  requestDelayMs: z.coerce.number().min(50).max(5000).default(334).describe('Request delay in ms'),
36
+ maxConcurrent: z.coerce
37
+ .number()
38
+ .min(1)
39
+ .max(16)
40
+ .default(8)
41
+ .describe('Max concurrent in-flight NCBI requests'),
20
42
  maxRetries: z.coerce.number().min(0).max(10).default(6).describe('Max retry attempts'),
21
43
  timeoutMs: z.coerce
22
44
  .number()
@@ -39,6 +61,30 @@ const ServerConfigSchema = z.object({
39
61
  .max(120000)
40
62
  .default(20000)
41
63
  .describe('Per-request HTTP timeout for Unpaywall lookups and content fetches, in ms'),
64
+ europepmcEnabled: envBoolean
65
+ .default(true)
66
+ .describe('Enable Europe PMC search tool and `pubmed_fetch_fulltext` JATS fallback chain. Set false to fully disable EPMC calls.'),
67
+ europepmcEmail: z
68
+ .preprocess(emptyAsUndefined, z.email().optional())
69
+ .describe('Optional contact email sent with Europe PMC requests'),
70
+ europepmcRequestDelayMs: z.coerce
71
+ .number()
72
+ .min(50)
73
+ .max(5000)
74
+ .default(200)
75
+ .describe('Minimum gap between Europe PMC request starts in ms'),
76
+ europepmcMaxRetries: z.coerce
77
+ .number()
78
+ .min(0)
79
+ .max(10)
80
+ .default(3)
81
+ .describe('Max retry attempts for failed Europe PMC requests'),
82
+ europepmcTimeoutMs: z.coerce
83
+ .number()
84
+ .min(1000)
85
+ .max(120000)
86
+ .default(20000)
87
+ .describe('Per-request HTTP timeout for Europe PMC calls, in ms'),
42
88
  });
43
89
  let _config;
44
90
  export function getServerConfig() {
@@ -48,11 +94,17 @@ export function getServerConfig() {
48
94
  toolIdentifier: 'NCBI_TOOL_IDENTIFIER',
49
95
  adminEmail: 'NCBI_ADMIN_EMAIL',
50
96
  requestDelayMs: 'NCBI_REQUEST_DELAY_MS',
97
+ maxConcurrent: 'NCBI_MAX_CONCURRENT',
51
98
  maxRetries: 'NCBI_MAX_RETRIES',
52
99
  timeoutMs: 'NCBI_TIMEOUT_MS',
53
100
  totalDeadlineMs: 'NCBI_TOTAL_DEADLINE_MS',
54
101
  unpaywallEmail: 'UNPAYWALL_EMAIL',
55
102
  unpaywallTimeoutMs: 'UNPAYWALL_TIMEOUT_MS',
103
+ europepmcEnabled: 'EUROPEPMC_ENABLED',
104
+ europepmcEmail: 'EUROPEPMC_EMAIL',
105
+ europepmcRequestDelayMs: 'EUROPEPMC_REQUEST_DELAY_MS',
106
+ europepmcMaxRetries: 'EUROPEPMC_MAX_RETRIES',
107
+ europepmcTimeoutMs: 'EUROPEPMC_TIMEOUT_MS',
56
108
  });
57
109
  /**
58
110
  * An API key raises NCBI's rate ceiling from ~3 req/s to ~10 req/s. If the
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * @module index
5
5
  */
6
6
  import { createApp } from '@cyanheads/mcp-ts-core';
7
+ import { getServerConfig } from './config/server-config.js';
7
8
  import { researchPlanPrompt } from './mcp-server/prompts/definitions/research-plan.prompt.js';
8
9
  import { databaseInfoResource } from './mcp-server/resources/definitions/database-info.resource.js';
9
10
  import { convertIdsTool } from './mcp-server/tools/definitions/convert-ids.tool.js';
@@ -13,25 +14,30 @@ import { findRelatedTool } from './mcp-server/tools/definitions/find-related.too
13
14
  import { formatCitationsTool } from './mcp-server/tools/definitions/format-citations.tool.js';
14
15
  import { lookupCitationTool } from './mcp-server/tools/definitions/lookup-citation.tool.js';
15
16
  import { lookupMeshTool } from './mcp-server/tools/definitions/lookup-mesh.tool.js';
17
+ import { pubmedEuropepmcSearchTool } from './mcp-server/tools/definitions/pubmed-europepmc-search.tool.js';
16
18
  import { searchArticlesTool } from './mcp-server/tools/definitions/search-articles.tool.js';
17
19
  import { spellCheckTool } from './mcp-server/tools/definitions/spell-check.tool.js';
20
+ import { initEuropePmcService } from './services/europe-pmc/europe-pmc-service.js';
18
21
  import { initNcbiService } from './services/ncbi/ncbi-service.js';
19
22
  import { initUnpaywallService } from './services/unpaywall/unpaywall-service.js';
23
+ const config = getServerConfig();
24
+ const tools = [
25
+ searchArticlesTool,
26
+ fetchArticlesTool,
27
+ fetchFulltextTool,
28
+ formatCitationsTool,
29
+ findRelatedTool,
30
+ spellCheckTool,
31
+ lookupMeshTool,
32
+ lookupCitationTool,
33
+ convertIdsTool,
34
+ ...(config.europepmcEnabled ? [pubmedEuropepmcSearchTool] : []),
35
+ ];
20
36
  await createApp({
21
- tools: [
22
- searchArticlesTool,
23
- fetchArticlesTool,
24
- fetchFulltextTool,
25
- formatCitationsTool,
26
- findRelatedTool,
27
- spellCheckTool,
28
- lookupMeshTool,
29
- lookupCitationTool,
30
- convertIdsTool,
31
- ],
37
+ tools,
32
38
  resources: [databaseInfoResource],
33
39
  prompts: [researchPlanPrompt],
34
- instructions: "PubMed/NCBI E-utilities wrapper. Typical research flow: `pubmed_search_articles` → `pubmed_fetch_articles` (metadata) → `pubmed_fetch_fulltext`. Prefer `pubmed_lookup_citation` over free-text search when you have a partial known reference it's deterministic. Use `pubmed_lookup_mesh` and `pubmed_spell_check` to refine queries before searching, when needed.",
40
+ instructions: "Use the pubmed_* tools to search PubMed and PubMed Central, fetch article metadata and full text, format citations, and find related articles via NCBI's E-utilities. Articles are keyed by PMID (integer); PMC full text by PMCID (`PMC` prefix); most also carry a DOI. Typical flow: `pubmed_search_articles` → `pubmed_fetch_articles` → `pubmed_fetch_fulltext`. When PubMed itself comes up empty (preprints, EPMC-only OA), broaden via `pubmed_europepmc_search`. Prefer deterministic resolvers when inputs are structured: `pubmed_lookup_citation` for partial references, `pubmed_convert_ids` to crosswalk IDs. Refine queries with `pubmed_lookup_mesh` and `pubmed_spell_check`.",
35
41
  landing: {
36
42
  tagline: 'Search PubMed, fetch articles, generate citations, explore MeSH terms, and discover related research.',
37
43
  repoRoot: 'https://github.com/cyanheads/pubmed-mcp-server',
@@ -58,6 +64,7 @@ await createApp({
58
64
  setup() {
59
65
  initNcbiService();
60
66
  initUnpaywallService();
67
+ initEuropePmcService();
61
68
  },
62
69
  });
63
70
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,MAAM,SAAS,CAAC;IACd,KAAK,EAAE;QACL,kBAAkB;QAClB,iBAAiB;QACjB,iBAAiB;QACjB,mBAAmB;QACnB,eAAe;QACf,cAAc;QACd,cAAc;QACd,kBAAkB;QAClB,cAAc;KACf;IACD,SAAS,EAAE,CAAC,oBAAoB,CAAC;IACjC,OAAO,EAAE,CAAC,kBAAkB,CAAC;IAC7B,YAAY,EACV,yWAAyW;IAC3W,OAAO,EAAE;QACP,OAAO,EACL,uGAAuG;QACzG,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE;YACL,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC7E;gBACE,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,8CAA8C;gBACpD,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,gDAAgD;gBACtD,QAAQ,EAAE,IAAI;aACf;YACD,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC9E;QACD,UAAU,EAAE;YACV,YAAY,EAAE,mBAAmB;YACjC,gBAAgB,EAAE,iBAAiB;YACnC,eAAe,EAAE,iBAAiB;SACnC;KACF;IACD,KAAK;QACH,eAAe,EAAE,CAAC;QAClB,oBAAoB,EAAE,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gEAAgE,CAAC;AAC3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;AACjC,MAAM,KAAK,GAAG;IACZ,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;IACjB,mBAAmB;IACnB,eAAe;IACf,cAAc;IACd,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;CAChE,CAAC;AAEF,MAAM,SAAS,CAAC;IACd,KAAK;IACL,SAAS,EAAE,CAAC,oBAAoB,CAAC;IACjC,OAAO,EAAE,CAAC,kBAAkB,CAAC;IAC7B,YAAY,EACV,iqBAAiqB;IACnqB,OAAO,EAAE;QACP,OAAO,EACL,uGAAuG;QACzG,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE;YACL,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC7E;gBACE,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,8CAA8C;gBACpD,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,gDAAgD;gBACtD,QAAQ,EAAE,IAAI;aACf;YACD,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC9E;QACD,UAAU,EAAE;YACV,YAAY,EAAE,mBAAmB;YACjC,gBAAgB,EAAE,iBAAiB;YACnC,eAAe,EAAE,iBAAiB;SACnC;KACF;IACD,KAAK;QACH,eAAe,EAAE,CAAC;QAClB,oBAAoB,EAAE,CAAC;QACvB,oBAAoB,EAAE,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
@@ -1,24 +1,39 @@
1
1
  /**
2
- * @fileoverview Full-text fetch tool. Primary source is PubMed Central (NCBI
3
- * EFetch, `db=pmc`). When a PMID has no PMC copy but does have a DOI,
4
- * transparently falls back to Unpaywall to retrieve a legally-deposited
5
- * open-access copy (HTML or PDF). Output uses a discriminated union on
6
- * `source` so callers can reason about structural reliability per article.
2
+ * @fileoverview Full-text fetch tool. Resolves full-text articles through a
3
+ * three-stage chain: NCBI PMC EFetch Europe PMC `fullTextXML` Unpaywall.
4
+ * Accepts three mutually-exclusive input shapes:
5
+ *
6
+ * - `pmcids` fetch directly by PMC ID. Articles not in PMC fall through to
7
+ * EPMC by PMC ID, then to Unpaywall when the DOI is available.
8
+ * - `pmids` — resolve PMID → PMCID via PMC ID Converter, then run the chain.
9
+ * - `dois` — skip PMC EFetch (no PMCID); resolve via EPMC search-by-DOI →
10
+ * fullTextXML, then Unpaywall. Covers EPMC-only OA and preprints with no
11
+ * PubMed presence.
12
+ *
13
+ * Output uses a discriminated union on `source` (`pmc` | `unpaywall`) with an
14
+ * extra `viaSource` discriminator that records which layer produced the
15
+ * content. EPMC's JATS reuses the `pmc` schema shape because it's the same
16
+ * DTD; `viaSource: 'europepmc'` distinguishes it from PMC EFetch output.
17
+ *
7
18
  * @module src/mcp-server/tools/definitions/fetch-fulltext.tool
8
19
  */
9
20
  import { z } from '@cyanheads/mcp-ts-core';
10
- import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
11
21
  export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
12
22
  pmcids: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
23
  pmids: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
+ dois: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
25
  includeReferences: z.ZodDefault<z.ZodBoolean>;
15
26
  maxSections: z.ZodOptional<z.ZodNumber>;
16
27
  sections: z.ZodOptional<z.ZodArray<z.ZodString>>;
17
28
  }, z.core.$strip>, z.ZodObject<{
18
29
  articles: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
19
30
  source: z.ZodLiteral<"pmc">;
20
- pmcId: z.ZodString;
21
- pmcUrl: z.ZodString;
31
+ viaSource: z.ZodEnum<{
32
+ pmc: "pmc";
33
+ europepmc: "europepmc";
34
+ }>;
35
+ pmcId: z.ZodOptional<z.ZodString>;
36
+ pmcUrl: z.ZodOptional<z.ZodString>;
22
37
  pmid: z.ZodOptional<z.ZodString>;
23
38
  pubmedUrl: z.ZodOptional<z.ZodString>;
24
39
  doi: z.ZodOptional<z.ZodString>;
@@ -59,14 +74,17 @@ export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDef
59
74
  id: z.ZodOptional<z.ZodString>;
60
75
  label: z.ZodOptional<z.ZodString>;
61
76
  }, z.core.$strip>>>;
77
+ epmcId: z.ZodOptional<z.ZodString>;
78
+ epmcSource: z.ZodOptional<z.ZodString>;
62
79
  }, z.core.$strip>, z.ZodObject<{
63
80
  source: z.ZodLiteral<"unpaywall">;
81
+ viaSource: z.ZodLiteral<"unpaywall">;
64
82
  contentFormat: z.ZodEnum<{
65
83
  "html-markdown": "html-markdown";
66
84
  "pdf-text": "pdf-text";
67
85
  }>;
68
- pmid: z.ZodString;
69
- pubmedUrl: z.ZodString;
86
+ pmid: z.ZodOptional<z.ZodString>;
87
+ pubmedUrl: z.ZodOptional<z.ZodString>;
70
88
  doi: z.ZodString;
71
89
  sourceUrl: z.ZodString;
72
90
  title: z.ZodOptional<z.ZodString>;
@@ -79,58 +97,88 @@ export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDef
79
97
  }, z.core.$strip>], "source">>;
80
98
  totalReturned: z.ZodNumber;
81
99
  unavailable: z.ZodOptional<z.ZodArray<z.ZodObject<{
82
- pmid: z.ZodString;
100
+ id: z.ZodString;
101
+ idType: z.ZodEnum<{
102
+ pmid: "pmid";
103
+ pmcid: "pmcid";
104
+ doi: "doi";
105
+ }>;
83
106
  reason: z.ZodEnum<{
84
107
  "no-oa": "no-oa";
108
+ "not-found": "not-found";
85
109
  "no-pmc-fallback-disabled": "no-pmc-fallback-disabled";
110
+ "no-epmc-fulltext": "no-epmc-fulltext";
86
111
  "no-doi": "no-doi";
87
112
  "fetch-failed": "fetch-failed";
88
113
  "parse-failed": "parse-failed";
89
114
  "service-error": "service-error";
90
115
  }>;
91
- detail: z.ZodOptional<z.ZodString>;
116
+ triedTiers: z.ZodArray<z.ZodObject<{
117
+ tier: z.ZodEnum<{
118
+ pmc: "pmc";
119
+ europepmc: "europepmc";
120
+ unpaywall: "unpaywall";
121
+ }>;
122
+ outcome: z.ZodEnum<{
123
+ "no-oa": "no-oa";
124
+ "no-doi": "no-doi";
125
+ "fetch-failed": "fetch-failed";
126
+ "parse-failed": "parse-failed";
127
+ "service-error": "service-error";
128
+ "not-attempted": "not-attempted";
129
+ miss: "miss";
130
+ "no-fulltext": "no-fulltext";
131
+ }>;
132
+ detail: z.ZodOptional<z.ZodString>;
133
+ }, z.core.$strip>>;
92
134
  }, z.core.$strip>>>;
93
- unavailablePmcIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
135
  }, z.core.$strip>, readonly [{
95
136
  readonly reason: "queue_full";
96
- readonly code: JsonRpcErrorCode.RateLimited;
137
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.RateLimited;
97
138
  readonly when: "Local NCBI request queue is at capacity.";
98
139
  readonly recovery: "Retry after 1-2 seconds; the request queue hit the NCBI rate limit.";
99
140
  readonly retryable: true;
100
141
  }, {
101
142
  readonly reason: "ncbi_unreachable";
102
- readonly code: JsonRpcErrorCode.ServiceUnavailable;
143
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
103
144
  readonly when: "NCBI E-utilities is unreachable after all retry attempts.";
104
145
  readonly recovery: "Retry after a brief delay; NCBI was unreachable across all retry attempts.";
105
146
  readonly retryable: true;
106
147
  }, {
107
148
  readonly reason: "ncbi_deadline_exceeded";
108
- readonly code: JsonRpcErrorCode.Timeout;
149
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.Timeout;
109
150
  readonly when: "Total request deadline expired before NCBI returned a response.";
110
151
  readonly recovery: "Reduce batch size or retry; NCBI may be under temporary load.";
111
152
  readonly retryable: true;
112
153
  }, {
113
154
  readonly reason: "ncbi_invalid_response";
114
- readonly code: JsonRpcErrorCode.SerializationError;
155
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.SerializationError;
115
156
  readonly when: "NCBI returned a body that could not be parsed (invalid XML/JSON).";
116
157
  readonly recovery: "Retry the request; NCBI returned a malformed response that could not be parsed.";
117
158
  readonly retryable: true;
118
159
  }, {
119
160
  readonly reason: "ncbi_resource_not_found";
120
- readonly code: JsonRpcErrorCode.NotFound;
161
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.NotFound;
121
162
  readonly when: "NCBI returned a structured \"not found\" error for the requested ID(s).";
122
163
  readonly recovery: "Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help.";
123
164
  readonly retryable: false;
124
165
  }, {
125
166
  readonly reason: "unpaywall_unreachable";
126
- readonly code: JsonRpcErrorCode.ServiceUnavailable;
167
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
127
168
  readonly when: "Unpaywall was unreachable when resolving a DOI or fetching content.";
128
169
  readonly recovery: "Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.";
129
170
  readonly retryable: true;
130
171
  }, {
131
- readonly reason: "invalid_pmc_efetch_response";
132
- readonly code: JsonRpcErrorCode.SerializationError;
133
- readonly when: "PMC EFetch returned a payload missing the pmc-articleset wrapper.";
134
- readonly recovery: "Retry once; if it persists, NCBI returned malformed data try fewer PMC IDs at once.";
172
+ readonly reason: "europepmc_unreachable";
173
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
174
+ readonly when: "Europe PMC was unreachable after all retry attempts.";
175
+ readonly recovery: "Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.";
176
+ readonly retryable: true;
177
+ }, {
178
+ readonly reason: "europepmc_invalid_response";
179
+ readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.SerializationError;
180
+ readonly when: "Europe PMC returned a body that could not be parsed (invalid JSON or XML).";
181
+ readonly recovery: "Retry the request; Europe PMC returned a malformed response that could not be parsed.";
182
+ readonly retryable: true;
135
183
  }]>;
136
184
  //# sourceMappingURL=fetch-fulltext.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-fulltext.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-fulltext.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAiLjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8P5B,CAAC"}
1
+ {"version":3,"file":"fetch-fulltext.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-fulltext.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAoR/D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsc5B,CAAC"}