@cyanheads/pubmed-mcp-server 2.10.16 → 2.10.18

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 (47) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/README.md +7 -5
  4. package/changelog/2.10.x/2.10.17.md +19 -0
  5. package/changelog/2.10.x/2.10.18.md +14 -0
  6. package/dist/config/server-config.d.ts.map +1 -1
  7. package/dist/config/server-config.js +7 -3
  8. package/dist/config/server-config.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/_text.d.ts +20 -3
  10. package/dist/mcp-server/tools/definitions/_text.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/definitions/_text.js +35 -3
  12. package/dist/mcp-server/tools/definitions/_text.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +23 -31
  14. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +349 -130
  16. package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/find-related.tool.js +22 -7
  19. package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
  20. package/dist/services/error-contracts.d.ts +5 -3
  21. package/dist/services/error-contracts.d.ts.map +1 -1
  22. package/dist/services/error-contracts.js +5 -3
  23. package/dist/services/error-contracts.js.map +1 -1
  24. package/dist/services/europe-pmc/europe-pmc-service.d.ts +18 -12
  25. package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
  26. package/dist/services/europe-pmc/europe-pmc-service.js +82 -67
  27. package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
  28. package/dist/services/europe-pmc/request-queue.d.ts +11 -27
  29. package/dist/services/europe-pmc/request-queue.d.ts.map +1 -1
  30. package/dist/services/europe-pmc/request-queue.js +17 -104
  31. package/dist/services/europe-pmc/request-queue.js.map +1 -1
  32. package/dist/services/ncbi/parsing/pmc-article-parser.d.ts +8 -0
  33. package/dist/services/ncbi/parsing/pmc-article-parser.d.ts.map +1 -1
  34. package/dist/services/ncbi/parsing/pmc-article-parser.js +159 -16
  35. package/dist/services/ncbi/parsing/pmc-article-parser.js.map +1 -1
  36. package/dist/services/openalex/openalex-service.d.ts +11 -1
  37. package/dist/services/openalex/openalex-service.d.ts.map +1 -1
  38. package/dist/services/openalex/openalex-service.js +19 -10
  39. package/dist/services/openalex/openalex-service.js.map +1 -1
  40. package/dist/services/unpaywall/types.d.ts +16 -2
  41. package/dist/services/unpaywall/types.d.ts.map +1 -1
  42. package/dist/services/unpaywall/unpaywall-service.d.ts +4 -3
  43. package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
  44. package/dist/services/unpaywall/unpaywall-service.js +15 -4
  45. package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
  46. package/package.json +1 -1
  47. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** @cyanheads/pubmed-mcp-server
4
- **Version:** 2.10.16
4
+ **Version:** 2.10.18
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
 
@@ -115,7 +115,7 @@ const ServerConfigSchema = z.object({
115
115
  apiKey: z.string().optional().describe('NCBI API key'),
116
116
  toolIdentifier: z.string().default('pubmed-mcp-server').describe('NCBI tool identifier'),
117
117
  adminEmail: z.email().optional().describe('Admin contact email'),
118
- requestDelayMs: z.coerce.number().min(50).max(5000).default(334).describe('Request delay in ms'),
118
+ requestDelayMs: z.coerce.number().min(50).max(5000).default(400).describe('Request delay in ms'),
119
119
  maxRetries: z.coerce.number().min(0).max(10).default(6).describe('Max retry attempts'),
120
120
  timeoutMs: z.coerce.number().min(1000).max(120000).default(30000).describe('Request timeout in ms'),
121
121
  });
package/CLAUDE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** @cyanheads/pubmed-mcp-server
4
- **Version:** 2.10.16
4
+ **Version:** 2.10.18
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
 
@@ -115,7 +115,7 @@ const ServerConfigSchema = z.object({
115
115
  apiKey: z.string().optional().describe('NCBI API key'),
116
116
  toolIdentifier: z.string().default('pubmed-mcp-server').describe('NCBI tool identifier'),
117
117
  adminEmail: z.email().optional().describe('Admin contact email'),
118
- requestDelayMs: z.coerce.number().min(50).max(5000).default(334).describe('Request delay in ms'),
118
+ requestDelayMs: z.coerce.number().min(50).max(5000).default(400).describe('Request delay in ms'),
119
119
  maxRetries: z.coerce.number().min(0).max(10).default(6).describe('Max retry attempts'),
120
120
  timeoutMs: z.coerce.number().min(1000).max(120000).default(30000).describe('Request timeout in ms'),
121
121
  });
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
 
11
11
 
12
- [![Version](https://img.shields.io/badge/Version-2.10.16-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pubmed-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![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) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
12
+ [![Version](https://img.shields.io/badge/Version-2.10.18-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pubmed-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![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) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
13
13
 
14
14
  </div>
15
15
 
@@ -89,12 +89,14 @@ The biomedical literature via NCBI's E-utilities, PubMed Central, and Europe PMC
89
89
 
90
90
  ### `pubmed_fetch_fulltext` <sub>tool</sub>
91
91
 
92
- - Exactly one of `pmcids`, `pmids`, or `dois` (one id per element), up to 10 per request; a zero-padded PMID resolves as the PMID it spells, and `unavailable[].id` keeps your spelling
92
+ - Exactly one of `pmcids`, `pmids`, or `dois` (one id per element), up to 10 per request; a zero-padded PMID or PMC ID resolves as the ID it spells, DOIs match case-insensitively, and PMC IDs match with or without the `PMC` prefix in any case. Each record is fetched once however many spellings name it; `unavailable[].id` keeps your PMID or DOI spelling, and reports a PMC ID as `PMC<digits>`
93
93
  - Three-tier chain: NCBI PMC EFetch → Europe PMC `fullTextXML` (`EUROPEPMC_ENABLED`, default on) → Unpaywall (needs `UNPAYWALL_EMAIL`); `viaSource` names which tier served each article
94
94
  - Preprints, patents, and Agricola records have metadata via `pubmed_europepmc_search` but no full text through this chain — Europe PMC's `fullTextXML` is PMC-keyed
95
- - `source: "pmc"` returns structured sections plus `tables[]` (cells, caption, label, footnotes) and `assets[]` (figures and supplementary material, with `[Figure: <label>]` markers left in the body); `source: "unpaywall"` returns a best-effort body with `contentFormat` (`html-markdown` / `pdf-text`)
95
+ - `source: "pmc"` returns structured sections plus `tables[]` (cells, caption, label, footnotes) and `assets[]` (figures and supplementary material, with `[Figure: <label>]` markers left in the body, and the file pointer read through `<alternatives>` when a figure offers several formats); `source: "unpaywall"` returns a best-effort body with `contentFormat` (`html-markdown` / `pdf-text`), a `title` from Unpaywall's record (else the Europe PMC record, else the HTML page), and `journalName` / `year` when Unpaywall has them
96
+ - A titled list, definition list, or boxed text that sits in the body outside any section, or is the only content of an untitled section, such as an abbreviations list, becomes a section under its own title, and a figure or table inside it names that section; a section with no title is headed `untitled section`, the same name the `truncation` ledger gives it, and section titles in headings and ledger lines are Markdown-escaped
96
97
  - Unavailable entries carry a typed `reason` (`not-found`, `no-doi`, `doi-lookup-failed`, `no-oa`, `service-error`, …), `idType`, `triedTiers` (per-tier outcome in execution order), and `unqueriedTiers` when an unconfigured tier could have served the id
97
- - Filters and budgets: `sections` (case-insensitive title match), `maxSections`, `includeTables`, `includeAssets`, `maxCharacters`, `maxCharactersPerSection`, `overflowMode` (`truncate` / `outline`), and `maxResponseCharacters`, which defers whole articles past the ceiling to `deferred.ids`; a `truncation` object reports what was shortened or omitted
98
+ - Filters and budgets: `sections` (case-insensitive title match), `maxSections`, `includeTables`, `includeAssets`, `maxCharacters`, `maxCharactersPerSection`, `overflowMode` (`truncate` / `outline`), and `maxResponseCharacters`, which defers whole articles past the ceiling to `deferred.ids`; a `truncation` object reports what was shortened or omitted, per section and subsection
99
+ - A budget cut ends at a word boundary; `truncate` drops every section and subsection past the cut, while `outline` keeps each heading and marks one the budget left empty
98
100
 
99
101
  ---
100
102
 
@@ -321,7 +323,7 @@ Key environment variables:
321
323
  | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
322
324
  | `NCBI_API_KEY` | NCBI API key for higher rate limits (10 req/s vs 3 req/s) | none |
323
325
  | `NCBI_ADMIN_EMAIL` | Contact email sent with NCBI requests (recommended by NCBI) | none |
324
- | `NCBI_REQUEST_DELAY_MS` | Minimum gap between NCBI request starts in ms | 334 (100 with key) |
326
+ | `NCBI_REQUEST_DELAY_MS` | Minimum gap between NCBI request starts in ms | 400 (100 with key) |
325
327
  | `NCBI_MAX_CONCURRENT` | Max concurrent in-flight NCBI requests | `8` |
326
328
  | `NCBI_MAX_RETRIES` | Retry attempts for failed NCBI requests | 6 |
327
329
  | `NCBI_TIMEOUT_MS` | Per-request HTTP timeout in ms | `30000` |
@@ -0,0 +1,19 @@
1
+ ---
2
+ summary: "Fixes pubmed_fetch_fulltext body parsing (alternatives figures, titled body-level blocks), word-boundary truncation with per-subsection accounting, Unpaywall title/journal/year, and case-insensitive DOI and zero-padded PMC ID matching."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.17 — 2026-09-22
8
+
9
+ ## Fixed
10
+
11
+ - **`pubmed_fetch_fulltext` reads a figure's or supplementary file's pointer from `<alternatives>`** when it has no direct `<graphic>`/`<media>`, preferring the rendering whose `specific-use` names the web, else the first. Its `href`, and a label or caption hung on that pointer, are no longer lost. [#142](https://github.com/cyanheads/pubmed-mcp-server/issues/142)
12
+ - **Character-budget cuts end at the last word boundary inside the allowance**, in sections and Unpaywall bodies alike; a single token longer than the allowance is still cut at it. In `truncate` mode every subsection past the cut is dropped and counted in `omittedSections`, where only whole top-level sections were before; `outline` mode keeps those headings and marks them in `content[]`. The `truncation` ledger lists a shortened section's `subsections` with their own accounting, and each entry carries its `label`. [#143](https://github.com/cyanheads/pubmed-mcp-server/issues/143)
13
+ - **Unpaywall articles carry a `title`**, from Unpaywall's own DOI record, else the Europe PMC record the chain searched, else — for HTML content only — the page's own title, **plus `journalName` and `year`** when Unpaywall's record has them. [#144](https://github.com/cyanheads/pubmed-mcp-server/issues/144)
14
+ - **A titled list, definition list, or boxed text sitting outside any `<sec>`, or alone inside an untitled one, becomes a section under its own title** instead of an untitled block with no heading to separate it from its neighbors. A figure or table inside a titled box names that section as its `sectionTitle`, so a `sections` filter on the box keeps them. [#148](https://github.com/cyanheads/pubmed-mcp-server/issues/148), [#169](https://github.com/cyanheads/pubmed-mcp-server/issues/169)
15
+ - **Every section gets a heading in `content[]`**: an untitled one is headed `untitled section` and a label-only one by its label, the same name its truncation-ledger line uses, and section titles are Markdown-escaped in both places. [#148](https://github.com/cyanheads/pubmed-mcp-server/issues/148), [#169](https://github.com/cyanheads/pubmed-mcp-server/issues/169)
16
+ - **A `<boxed-text>` caption's title and paragraph render as separate lines** instead of running together. [#169](https://github.com/cyanheads/pubmed-mcp-server/issues/169)
17
+ - **DOI input matches case-insensitively**: every casing of one DOI runs the chain once, and `unavailable[]` reports each casing as submitted. Two different DOIs the ID Converter places on one PMC record also share one chain, and that record is fetched once. [#166](https://github.com/cyanheads/pubmed-mcp-server/issues/166)
18
+ - **`errors[]` no longer declares Unpaywall or Europe PMC failure reasons the handler never throws** — both are folded into each id's `triedTiers` instead of propagated. [#168](https://github.com/cyanheads/pubmed-mcp-server/issues/168)
19
+ - **A zero-padded PMC ID (`PMC0123`) resolves as the record it spells** instead of being returned and reported unavailable in the same response. Every spelling of one PMC ID — with or without the `PMC` prefix, in any case, zero-padded or not — is fetched once and reported in `PMC<digits>` form. [#170](https://github.com/cyanheads/pubmed-mcp-server/issues/170)
@@ -0,0 +1,14 @@
1
+ ---
2
+ summary: "Moves Europe PMC's request queue onto the framework pacer with retries outside it, so a backoff no longer holds a concurrency slot and an upstream Retry-After is honored; also widens the unkeyed NCBI request delay to 400ms and limits openalex_unreachable to exhausted ServiceUnavailable errors."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 2.10.18 — 2026-09-22
8
+
9
+ ## Fixed
10
+
11
+ - **Europe PMC's request queue moves onto the framework pacer, with retries outside it.** A backoff no longer holds one of the four concurrency slots, and every attempt waits out the start gap from the last request any caller started. An upstream `Retry-After` now sets the retry wait; one over the 30 s backoff cap fails without another attempt, `retryAfter` included. [#163](https://github.com/cyanheads/pubmed-mcp-server/issues/163)
12
+ - **The unkeyed `NCBI_REQUEST_DELAY_MS` default rises from 334ms to 400ms.** 334ms sat exactly at NCBI's 3 req/s ceiling, so small bursts drew 429s that closed the request queue's cooldown gate. The keyed 100ms default and an explicit override still apply. [#162](https://github.com/cyanheads/pubmed-mcp-server/issues/162)
13
+ - **OpenAlex stamps `openalex_unreachable` only on an exhausted `ServiceUnavailable`.** An exhausted `Timeout` or `RateLimited` keeps its own code with no reason, and every exhausted error keeps its upstream `retryAfter`. [#160](https://github.com/cyanheads/pubmed-mcp-server/issues/160)
14
+ - **`pubmed_find_related` names a provider failure that carries no reason by its code** — `rate_limited` or `timed_out`, not `unclassified_error` — in the coverage summary and the all-providers-failed `data.attempted`. The coverage summary takes such a failure's retryability from its own `retryable` flag. [#160](https://github.com/cyanheads/pubmed-mcp-server/issues/160)
@@ -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;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;iBA8DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CA+B9C"}
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;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;iBA8DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAmC9C"}
@@ -10,7 +10,7 @@ const ServerConfigSchema = z.object({
10
10
  apiKey: z.string().optional().describe('NCBI API key'),
11
11
  toolIdentifier: z.string().default('pubmed-mcp-server').describe('NCBI tool identifier'),
12
12
  adminEmail: z.email().optional().describe('Admin contact email'),
13
- requestDelayMs: z.coerce.number().min(50).max(5000).default(334).describe('Request delay in ms'),
13
+ requestDelayMs: z.coerce.number().min(50).max(5000).default(400).describe('Request delay in ms'),
14
14
  maxConcurrent: z.coerce
15
15
  .number()
16
16
  .min(1)
@@ -89,9 +89,13 @@ export function getServerConfig() {
89
89
  });
90
90
  /**
91
91
  * An API key raises NCBI's rate ceiling from ~3 req/s to ~10 req/s. If the
92
- * operator hasn't explicitly overridden the delay, tighten from the 334ms
93
- * safe default to 100ms when a key is present. A set-but-blank delay counts
92
+ * operator hasn't explicitly overridden the delay, tighten from the 400ms
93
+ * keyless default to 100ms when a key is present. A set-but-blank delay counts
94
94
  * as not overridden, matching how `parseEnvConfig` reads it.
95
+ *
96
+ * The keyless default sits above the 334ms that 3 req/s works out to, leaving
97
+ * margin for arrival jitter: spaced at exactly the ceiling, a small burst of
98
+ * concurrent calls draws 429s.
95
99
  */
96
100
  _config =
97
101
  parsed.apiKey && !process.env.NCBI_REQUEST_DELAY_MS?.trim()
@@ -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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"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Text helpers shared by the tool definitions: the surrogate-safe
3
- * character cut used to bound returned text to a budget, and the render-time
4
- * Markdown escapes applied to upstream strings interpolated into `format()` —
5
- * one for an inline position, one for a table cell.
3
+ * character cut and the word-boundary cut built on it, which bound returned text
4
+ * to a budget, and the render-time Markdown escapes applied to upstream strings
5
+ * interpolated into `format()` — one for an inline position, one for a table
6
+ * cell.
6
7
  * @module src/mcp-server/tools/definitions/_text
7
8
  */
8
9
  /**
@@ -21,6 +22,22 @@
21
22
  * than assume the allowance was spent exactly. (#93)
22
23
  */
23
24
  export declare function sliceCodeUnits(text: string, limit: number): string;
25
+ /**
26
+ * Take at most `limit` UTF-16 code units from `text`, ending at the last word
27
+ * boundary inside the allowance rather than partway through a word.
28
+ *
29
+ * The cut is {@link sliceCodeUnits}' cut, backed off past the partial word it
30
+ * would end on — unless the character after it is whitespace, in which case
31
+ * the last word is already whole — with the whitespace before that word
32
+ * trimmed. A cut is therefore never mid-word and never ends in whitespace.
33
+ * `limit` stays a ceiling: the result can come back several units short of it,
34
+ * so callers reporting character counts measure the returned string. (#143)
35
+ *
36
+ * An allowance holding no boundary at all — one token longer than the
37
+ * allowance, such as a sequence or a URL — is cut at the allowance instead:
38
+ * backing off would return nothing, dropping a field that has text to give.
39
+ */
40
+ export declare function sliceAtWordBoundary(text: string, limit: number): string;
24
41
  /**
25
42
  * Escape the Markdown-significant characters in an upstream string that is
26
43
  * about to be interpolated *inline* into a `format()` line — a `### `/`#### `
@@ -1 +1 @@
1
- {"version":3,"file":"_text.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlE;AA2CD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAazD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D"}
1
+ {"version":3,"file":"_text.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUvE;AA2CD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAazD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Text helpers shared by the tool definitions: the surrogate-safe
3
- * character cut used to bound returned text to a budget, and the render-time
4
- * Markdown escapes applied to upstream strings interpolated into `format()` —
5
- * one for an inline position, one for a table cell.
3
+ * character cut and the word-boundary cut built on it, which bound returned text
4
+ * to a budget, and the render-time Markdown escapes applied to upstream strings
5
+ * interpolated into `format()` — one for an inline position, one for a table
6
+ * cell.
6
7
  * @module src/mcp-server/tools/definitions/_text
7
8
  */
8
9
  const HIGH_SURROGATE_FIRST = 0xd800;
@@ -31,6 +32,37 @@ export function sliceCodeUnits(text, limit) {
31
32
  const splitsPair = last >= HIGH_SURROGATE_FIRST && last <= HIGH_SURROGATE_LAST;
32
33
  return text.slice(0, splitsPair ? limit - 1 : limit);
33
34
  }
35
+ /** A whitespace character — the boundary a word-boundary cut backs off to. */
36
+ const WHITESPACE_RE = /\s/;
37
+ const isWhitespace = (text, index) => WHITESPACE_RE.test(text.charAt(index));
38
+ /**
39
+ * Take at most `limit` UTF-16 code units from `text`, ending at the last word
40
+ * boundary inside the allowance rather than partway through a word.
41
+ *
42
+ * The cut is {@link sliceCodeUnits}' cut, backed off past the partial word it
43
+ * would end on — unless the character after it is whitespace, in which case
44
+ * the last word is already whole — with the whitespace before that word
45
+ * trimmed. A cut is therefore never mid-word and never ends in whitespace.
46
+ * `limit` stays a ceiling: the result can come back several units short of it,
47
+ * so callers reporting character counts measure the returned string. (#143)
48
+ *
49
+ * An allowance holding no boundary at all — one token longer than the
50
+ * allowance, such as a sequence or a URL — is cut at the allowance instead:
51
+ * backing off would return nothing, dropping a field that has text to give.
52
+ */
53
+ export function sliceAtWordBoundary(text, limit) {
54
+ const cut = sliceCodeUnits(text, limit);
55
+ if (cut.length === text.length)
56
+ return cut;
57
+ let end = cut.length;
58
+ if (!isWhitespace(text, end)) {
59
+ while (end > 0 && !isWhitespace(cut, end - 1))
60
+ end -= 1;
61
+ }
62
+ while (end > 0 && isWhitespace(cut, end - 1))
63
+ end -= 1;
64
+ return end > 0 ? cut.slice(0, end) : cut.trimEnd();
65
+ }
34
66
  /** Any line break, including the Unicode line and paragraph separators. */
35
67
  const LINE_BREAK_RE = /[\r\n\u2028\u2029]+/g;
36
68
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"_text.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,IAAI,oBAAoB,IAAI,IAAI,IAAI,mBAAmB,CAAC;IAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,2EAA2E;AAC3E,MAAM,aAAa,GAAG,sBAAsB,CAAC;AAC7C;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AACtD,mFAAmF;AACnF,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,SAAS,OAAO,CAAC,IAAY,EAAE,SAAiB;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,OAAe;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,IAAY;IACvC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,MAAM,SAAS,GACb,QAAQ,KAAK,SAAS;YACtB,IAAI,KAAK,SAAS;YAClB,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC3B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,SAAS;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAElE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,KAAa,EAAE,EAAE,CACzD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CACrD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"_text.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AACpC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,IAAI,IAAI,oBAAoB,IAAI,IAAI,IAAI,mBAAmB,CAAC;IAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,KAAa,EAAW,EAAE,CAC5D,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAEzC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAa;IAC7D,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IAE3C,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;YAAE,GAAG,IAAI,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,IAAI,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IACvD,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;AACrD,CAAC;AAED,2EAA2E;AAC3E,MAAM,aAAa,GAAG,sBAAsB,CAAC;AAC7C;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AACtD,mFAAmF;AACnF,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,SAAS,OAAO,CAAC,IAAY,EAAE,SAAiB;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,OAAe;IAChD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,IAAY;IACvC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,MAAM,SAAS,GACb,QAAQ,KAAK,SAAS;YACtB,IAAI,KAAK,SAAS;YAClB,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC3B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,SAAS;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAElE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,KAAa,EAAE,EAAE,CACzD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CACrD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC1D,CAAC"}
@@ -3,19 +3,29 @@
3
3
  * three-stage chain: NCBI PMC EFetch → Europe PMC `fullTextXML` → Unpaywall.
4
4
  * Accepts three mutually-exclusive input shapes:
5
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.
6
+ * - `pmcids` — fetch directly by PMC ID, once per record however it is
7
+ * spelled (`PMC123`, `pmc123`, `123`, zero-padded `PMC0123`), and reported
8
+ * in `PMC<digits>` form. Articles not in PMC fall through to EPMC by PMC ID,
9
+ * then to Unpaywall when the DOI is available.
8
10
  * - `pmids` — resolve PMID → PMCID via PMC ID Converter, then run the chain.
9
11
  * A zero-padded PMID runs as the PMID it spells; `unavailable[]` and
10
12
  * `deferred.ids` report it as the caller wrote it.
11
13
  * - `dois` — resolve DOI → PMCID via the PMC ID Converter (mirroring `pmids`),
12
14
  * then run the chain. DOIs with no PMC counterpart fall through to EPMC
13
15
  * search-by-DOI → fullTextXML, then Unpaywall (EPMC-only OA, preprints).
16
+ * DOIs are case-insensitive: every casing of one DOI runs the chain once,
17
+ * and `unavailable[]` reports each casing as the caller wrote it.
14
18
  *
15
19
  * Output uses a discriminated union on `source` (`pmc` | `unpaywall`) with an
16
20
  * extra `viaSource` discriminator that records which layer produced the
17
21
  * content. EPMC's JATS reuses the `pmc` schema shape because it's the same
18
- * DTD; `viaSource: 'europepmc'` distinguishes it from PMC EFetch output.
22
+ * DTD; `viaSource: 'europepmc'` distinguishes it from PMC EFetch output. An
23
+ * Unpaywall article takes its title from Unpaywall's record, else the Europe
24
+ * PMC record the chain searched, else (HTML only) the page itself.
25
+ *
26
+ * Europe PMC and Unpaywall failures are folded into each id's `triedTiers`
27
+ * rather than thrown, so the declared `errors[]` covers the NCBI ID routing
28
+ * alone.
19
29
  *
20
30
  * @module src/mcp-server/tools/definitions/fetch-fulltext.tool
21
31
  */
@@ -137,6 +147,8 @@ export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDef
137
147
  doi: z.ZodString;
138
148
  sourceUrl: z.ZodString;
139
149
  title: z.ZodOptional<z.ZodString>;
150
+ journalName: z.ZodOptional<z.ZodString>;
151
+ year: z.ZodOptional<z.ZodNumber>;
140
152
  content: z.ZodString;
141
153
  wordCount: z.ZodOptional<z.ZodNumber>;
142
154
  totalPages: z.ZodOptional<z.ZodNumber>;
@@ -211,9 +223,17 @@ export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDef
211
223
  returnedCharacters: z.ZodNumber;
212
224
  sections: z.ZodOptional<z.ZodArray<z.ZodObject<{
213
225
  title: z.ZodOptional<z.ZodString>;
226
+ label: z.ZodOptional<z.ZodString>;
214
227
  originalCharacters: z.ZodNumber;
215
228
  returnedCharacters: z.ZodNumber;
216
229
  truncated: z.ZodBoolean;
230
+ subsections: z.ZodOptional<z.ZodArray<z.ZodObject<{
231
+ title: z.ZodOptional<z.ZodString>;
232
+ label: z.ZodOptional<z.ZodString>;
233
+ originalCharacters: z.ZodNumber;
234
+ returnedCharacters: z.ZodNumber;
235
+ truncated: z.ZodBoolean;
236
+ }, z.core.$strip>>>;
217
237
  }, z.core.$strip>>>;
218
238
  omittedTables: z.ZodOptional<z.ZodNumber>;
219
239
  omittedTableNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -268,34 +288,6 @@ export declare const fetchFulltextTool: import("@cyanheads/mcp-ts-core").ToolDef
268
288
  readonly recovery: 'Verify the ID exists in PubMed; the resource was not found in NCBI and retrying will not help.';
269
289
  readonly retryable: false;
270
290
  readonly thrownBy: 'service';
271
- }, {
272
- readonly reason: 'unpaywall_unreachable';
273
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
274
- readonly when: 'Unpaywall was unreachable when resolving a DOI or fetching content.';
275
- readonly recovery: 'Retry after a brief delay; Unpaywall was unreachable. The PMC source remains the primary path.';
276
- readonly retryable: true;
277
- readonly thrownBy: 'service';
278
- }, {
279
- readonly reason: 'europepmc_unreachable';
280
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ServiceUnavailable;
281
- 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.';
282
- readonly recovery: 'Retry after a brief delay; Europe PMC was unreachable. NCBI PMC and Unpaywall remain available.';
283
- readonly retryable: true;
284
- readonly thrownBy: 'service';
285
- }, {
286
- readonly reason: 'europepmc_invalid_response';
287
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.SerializationError;
288
- readonly when: 'Europe PMC returned a body that could not be parsed (invalid JSON or XML).';
289
- readonly recovery: 'Retry the request; Europe PMC returned a malformed response that could not be parsed.';
290
- readonly retryable: true;
291
- readonly thrownBy: 'service';
292
- }, {
293
- readonly reason: 'europepmc_invalid_input';
294
- readonly code: import("@cyanheads/mcp-ts-core/errors").JsonRpcErrorCode.ValidationError;
295
- 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.';
296
- readonly recovery: 'Adjust the input — the query, the sort, or the cursorMark — before retrying; the same input will be rejected again.';
297
- readonly retryable: false;
298
- readonly thrownBy: 'service';
299
291
  }], {
300
292
  readonly notice: z.ZodOptional<z.ZodString>;
301
293
  readonly truncated: z.ZodOptional<z.ZodBoolean>;
@@ -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;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAu0C/D;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,MAAM,CAiCT;AAID,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAm4B5B,CAAC"}
1
+ {"version":3,"file":"fetch-fulltext.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fetch-fulltext.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAi7C/D;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,MAAM,CAiCT;AAID,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA66B5B,CAAC"}