@djmima/pubmed-mcp 0.0.0-stage → 1.0.1

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 (69) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +803 -2
  4. package/dist/auth.d.ts +39 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +97 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/cli/commands.d.ts +42 -0
  9. package/dist/cli/commands.d.ts.map +1 -0
  10. package/dist/cli/commands.js +508 -0
  11. package/dist/cli/commands.js.map +1 -0
  12. package/dist/cli/run.d.ts +21 -0
  13. package/dist/cli/run.d.ts.map +1 -0
  14. package/dist/cli/run.js +154 -0
  15. package/dist/cli/run.js.map +1 -0
  16. package/dist/cli.d.ts +16 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +52 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/index.d.ts +32 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +126 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/pubmed/client.d.ts +150 -0
  25. package/dist/pubmed/client.d.ts.map +1 -0
  26. package/dist/pubmed/client.js +319 -0
  27. package/dist/pubmed/client.js.map +1 -0
  28. package/dist/pubmed/types.d.ts +159 -0
  29. package/dist/pubmed/types.d.ts.map +1 -0
  30. package/dist/pubmed/types.js +11 -0
  31. package/dist/pubmed/types.js.map +1 -0
  32. package/dist/server.d.ts +18 -0
  33. package/dist/server.d.ts.map +1 -0
  34. package/dist/server.js +157 -0
  35. package/dist/server.js.map +1 -0
  36. package/dist/tools/convertArticleIds.d.ts +25 -0
  37. package/dist/tools/convertArticleIds.d.ts.map +1 -0
  38. package/dist/tools/convertArticleIds.js +39 -0
  39. package/dist/tools/convertArticleIds.js.map +1 -0
  40. package/dist/tools/findRelatedArticles.d.ts +30 -0
  41. package/dist/tools/findRelatedArticles.d.ts.map +1 -0
  42. package/dist/tools/findRelatedArticles.js +77 -0
  43. package/dist/tools/findRelatedArticles.js.map +1 -0
  44. package/dist/tools/getArticleMetadata.d.ts +31 -0
  45. package/dist/tools/getArticleMetadata.d.ts.map +1 -0
  46. package/dist/tools/getArticleMetadata.js +72 -0
  47. package/dist/tools/getArticleMetadata.js.map +1 -0
  48. package/dist/tools/getCopyrightStatus.d.ts +25 -0
  49. package/dist/tools/getCopyrightStatus.d.ts.map +1 -0
  50. package/dist/tools/getCopyrightStatus.js +84 -0
  51. package/dist/tools/getCopyrightStatus.js.map +1 -0
  52. package/dist/tools/getFullTextArticle.d.ts +21 -0
  53. package/dist/tools/getFullTextArticle.d.ts.map +1 -0
  54. package/dist/tools/getFullTextArticle.js +89 -0
  55. package/dist/tools/getFullTextArticle.js.map +1 -0
  56. package/dist/tools/lookupArticleByCitation.d.ts +33 -0
  57. package/dist/tools/lookupArticleByCitation.d.ts.map +1 -0
  58. package/dist/tools/lookupArticleByCitation.js +96 -0
  59. package/dist/tools/lookupArticleByCitation.js.map +1 -0
  60. package/dist/tools/searchArticles.d.ts +49 -0
  61. package/dist/tools/searchArticles.d.ts.map +1 -0
  62. package/dist/tools/searchArticles.js +121 -0
  63. package/dist/tools/searchArticles.js.map +1 -0
  64. package/dist/utils/xml.d.ts +37 -0
  65. package/dist/utils/xml.d.ts.map +1 -0
  66. package/dist/utils/xml.js +516 -0
  67. package/dist/utils/xml.js.map +1 -0
  68. package/package.json +73 -4
  69. package/skills/pubmed-cli/SKILL.md +79 -0
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Command table for the pubmed CLI.
3
+ *
4
+ * Each command runs one MCP tool: it turns command-line input into the tool's
5
+ * parameters, validates them with the tool's zod schema (as the MCP server
6
+ * does), and calls the same tool function, so the JSON output is the same.
7
+ * Lists longer than a tool's per-request limit are split into batches that
8
+ * run one after another through the shared client and its rate limiter; the
9
+ * batch results are merged into one response of the same shape.
10
+ */
11
+ import { z } from 'zod';
12
+ import { searchArticles, searchArticlesSchema } from '../tools/searchArticles.js';
13
+ import { getArticleMetadata, getArticleMetadataSchema, MAX_METADATA_PMIDS, } from '../tools/getArticleMetadata.js';
14
+ import { findRelatedArticles, findRelatedArticlesSchema, } from '../tools/findRelatedArticles.js';
15
+ import { lookupArticleByCitation, lookupArticleByCitationSchema, MAX_CITATIONS, } from '../tools/lookupArticleByCitation.js';
16
+ import { convertArticleIds, convertArticleIdsSchema, MAX_CONVERT_IDS, } from '../tools/convertArticleIds.js';
17
+ import { getFullTextArticle, getFullTextArticleSchema, MAX_FULL_TEXT_IDS, } from '../tools/getFullTextArticle.js';
18
+ import { getCopyrightStatus, getCopyrightStatusSchema, MAX_COPYRIGHT_PMIDS, } from '../tools/getCopyrightStatus.js';
19
+ /** Invalid command-line input. The CLI exits with status 2 instead of 1. */
20
+ export class UsageError extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = 'UsageError';
24
+ }
25
+ }
26
+ // ─── search ─────────────────────────────────────────────────────────────────
27
+ const search = {
28
+ name: 'search',
29
+ tool: 'search_articles',
30
+ description: 'Search PubMed and return matching PMIDs',
31
+ options: {
32
+ 'max-results': { type: 'string' },
33
+ retstart: { type: 'string' },
34
+ sort: { type: 'string' },
35
+ field: { type: 'string' },
36
+ 'date-from': { type: 'string' },
37
+ 'date-to': { type: 'string' },
38
+ datetype: { type: 'string' },
39
+ },
40
+ help: `Usage: pubmed search <query> [options]
41
+ pubmed search - [options] (read the query from stdin)
42
+
43
+ Search PubMed. Prints PMIDs with total_count, query_translation, warnings,
44
+ and errors (same JSON as the search_articles MCP tool).
45
+
46
+ Options:
47
+ --max-results <n> PMIDs to return, 1-10000 (default 20)
48
+ --retstart <n> Offset of the first PMID, for paging (default 0)
49
+ --sort <order> relevance | pub_date | author | journal_name
50
+ --field <field> Restrict untagged terms to one field:
51
+ title | tiab | mesh | author | journal | affiliation
52
+ --date-from <date> Start date: YYYY, YYYY/MM, or YYYY/MM/DD
53
+ --date-to <date> End date, same formats (either bound may be omitted)
54
+ --datetype <type> pdat = publication date (default) | edat = Entrez date |
55
+ mdat = modification date
56
+
57
+ PubMed maps untagged terms through Automatic Term Mapping (ATM). Before
58
+ concluding that a low or zero total_count means no literature exists, check
59
+ query_translation, warnings, and errors. To search several words in one
60
+ field, tag each term; when a quoted phrase is not indexed, use a proximity
61
+ search such as "drug resistance"[tiab:~0].
62
+
63
+ Example:
64
+ pubmed search "TP53[tiab] AND myelodysplastic syndromes[mh]" --max-results 200 -o search.json
65
+ jq '{total_count, query_translation, warnings, errors}' search.json
66
+ `,
67
+ async execute(input, client) {
68
+ // A query read from stdin may span several lines.
69
+ const query = (input.positionals.length === 1 && input.positionals[0] === '-'
70
+ ? (await input.readText('-')).replace(/\s+/g, ' ')
71
+ : input.positionals.join(' ')).trim();
72
+ if (!query)
73
+ throw new UsageError('missing <query>');
74
+ const params = validate(searchArticlesSchema, {
75
+ query,
76
+ max_results: intOption(input, 'max-results'),
77
+ retstart: intOption(input, 'retstart'),
78
+ sort: input.values.sort,
79
+ field: input.values.field,
80
+ date_from: input.values['date-from'],
81
+ date_to: input.values['date-to'],
82
+ datetype: input.values.datetype,
83
+ });
84
+ const output = await searchArticles(params, client);
85
+ return { output, summary: `${output.returned_count} of ${output.total_count} PMIDs` };
86
+ },
87
+ };
88
+ // ─── metadata ───────────────────────────────────────────────────────────────
89
+ const metadata = {
90
+ name: 'metadata',
91
+ tool: 'get_article_metadata',
92
+ description: 'Get title, abstract, authors, MeSH terms, etc.',
93
+ options: {
94
+ authors: { type: 'string' },
95
+ affiliations: { type: 'string' },
96
+ },
97
+ help: `Usage: pubmed metadata <pmid>... | - [options]
98
+
99
+ Get title, abstract, authors, journal, publication date, MeSH terms,
100
+ keywords, and article types (same JSON as get_article_metadata).
101
+ Lists longer than ${MAX_METADATA_PMIDS} PMIDs are fetched in batches and merged.
102
+
103
+ Options:
104
+ --authors <mode> all (default) | first_last | first | none
105
+ author_count always gives the full number of authors
106
+ --affiliations <bool> false (default) | true: include the affiliations of
107
+ the returned authors
108
+
109
+ Records are large (abstracts, and with --affiliations also affiliations): save
110
+ them with -o and extract the fields you need with jq.
111
+
112
+ Example:
113
+ jq -r '.pmids[]' search.json | pubmed metadata - --authors first_last -o meta.json
114
+ jq -r '.articles[] | [.identifiers.pmid, .publication_date.year, .title] | @tsv' meta.json
115
+ `,
116
+ async execute(input, client) {
117
+ const pmids = await readIds(input, PMID);
118
+ const authors = input.values.authors;
119
+ const affiliations = boolOption(input, 'affiliations');
120
+ const batches = chunk(pmids, MAX_METADATA_PMIDS).map((batch) => validate(getArticleMetadataSchema, {
121
+ pmids: batch,
122
+ authors,
123
+ include_affiliations: affiliations,
124
+ }));
125
+ const results = await runBatches(batches, (params) => getArticleMetadata(params, client));
126
+ const articles = results.flatMap((r) => r.articles);
127
+ const output = {
128
+ articles,
129
+ count: articles.length,
130
+ important_legal_notice: results[0].important_legal_notice,
131
+ };
132
+ return { output, summary: plural(output.count, 'article') };
133
+ },
134
+ };
135
+ // ─── related ────────────────────────────────────────────────────────────────
136
+ /** ELink takes the IDs in the URL, so keep each request's list short. */
137
+ const RELATED_BATCH_SIZE = 200;
138
+ const related = {
139
+ name: 'related',
140
+ tool: 'find_related_articles',
141
+ description: 'Find similar or citing articles and linked records',
142
+ options: {
143
+ 'link-type': { type: 'string' },
144
+ 'max-results': { type: 'string' },
145
+ },
146
+ help: `Usage: pubmed related <pmid>... | - [options]
147
+
148
+ Find linked records for each PMID with NCBI ELink (same JSON as
149
+ find_related_articles). Lists longer than ${RELATED_BATCH_SIZE} PMIDs are split into batches.
150
+
151
+ Options:
152
+ --link-type <type> similar_articles (default) similar PubMed articles
153
+ citing_pmc_articles PMC articles citing the PMID
154
+ linked_gene_records NCBI Gene records
155
+ linked_refseq_proteins RefSeq protein records
156
+ linked_refseq_nucleotides RefSeq nucleotide records
157
+ --max-results <n> Cap on links per source PMID, 1-1000
158
+
159
+ citing_pmc_articles returns PMC IDs without the "PMC" prefix; pubmed fulltext
160
+ and pubmed convert --id-type pmcid accept them as they are.
161
+
162
+ Example:
163
+ pubmed related 34265844 --link-type citing_pmc_articles -o citing.json
164
+ jq -r '.linksets[].linksetdbs[].links[]' citing.json
165
+ `,
166
+ async execute(input, client) {
167
+ const pmids = await readIds(input, PMID);
168
+ const linkType = input.values['link-type'];
169
+ const maxResults = intOption(input, 'max-results');
170
+ const batches = chunk(pmids, RELATED_BATCH_SIZE).map((batch) => validate(findRelatedArticlesSchema, {
171
+ pmids: batch,
172
+ link_type: linkType,
173
+ max_results: maxResults,
174
+ }));
175
+ const results = await runBatches(batches, (params) => findRelatedArticles(params, client));
176
+ const output = { linksets: results.flatMap((r) => r.linksets) };
177
+ const links = output.linksets
178
+ .flatMap((linkset) => linkset.linksetdbs)
179
+ .reduce((total, linksetdb) => total + linksetdb.links.length, 0);
180
+ return { output, summary: `${plural(links, 'link')} for ${plural(pmids.length, 'PMID')}` };
181
+ },
182
+ };
183
+ // ─── citation ───────────────────────────────────────────────────────────────
184
+ const CITATION_SCHEMA = lookupArticleByCitationSchema.citations.element;
185
+ const citation = {
186
+ name: 'citation',
187
+ tool: 'lookup_article_by_citation',
188
+ description: 'Find PMIDs from journal, year, volume, page, author',
189
+ options: {
190
+ journal: { type: 'string' },
191
+ year: { type: 'string' },
192
+ volume: { type: 'string' },
193
+ 'first-page': { type: 'string' },
194
+ author: { type: 'string' },
195
+ key: { type: 'string' },
196
+ input: { type: 'string' },
197
+ },
198
+ help: `Usage: pubmed citation --journal <name> --year <yyyy> [options]
199
+ pubmed citation --input <file|->
200
+
201
+ Find PMIDs from citation details with NCBI ECitMatch (same JSON as
202
+ lookup_article_by_citation). Unmatched citations have an empty pmid.
203
+
204
+ Options:
205
+ --journal <name> Journal title or abbreviation, e.g. "N Engl J Med"
206
+ --year <yyyy> Publication year
207
+ --volume <v> Volume
208
+ --first-page <p> First page
209
+ --author <name> First author's last name
210
+ --key <key> Label echoed back in the result (default 1)
211
+ --input <file|-> Many citations: a JSON array or JSON Lines of objects
212
+ {journal, year, volume, first_page, author, key};
213
+ missing keys default to 1, 2, 3, ... Lists longer than
214
+ ${MAX_CITATIONS} are split into batches.
215
+
216
+ Example:
217
+ pubmed citation --journal Nature --year 2021 --volume 596 --first-page 583 --author Jumper
218
+ `,
219
+ async execute(input, client) {
220
+ const citations = (input.values.input !== undefined
221
+ ? await readCitationList(input, input.values.input)
222
+ : [citationFromOptions(input)]).map(validateCitation);
223
+ const results = await runBatches(chunk(citations, MAX_CITATIONS), (batch) => lookupArticleByCitation({ citations: batch }, client));
224
+ const output = { citations: results.flatMap((r) => r.citations) };
225
+ const matched = output.citations.filter((c) => c.pmid !== '').length;
226
+ return {
227
+ output,
228
+ summary: `${matched} of ${plural(output.citations.length, 'citation')} matched`,
229
+ };
230
+ },
231
+ };
232
+ const CITATION_OPTIONS = ['journal', 'year', 'volume', 'first-page', 'author', 'key'];
233
+ function citationFromOptions(input) {
234
+ const { journal } = input.values;
235
+ const year = intOption(input, 'year');
236
+ if (journal === undefined || year === undefined) {
237
+ throw new UsageError('give --journal and --year, or a list of citations with --input');
238
+ }
239
+ return {
240
+ journal,
241
+ year,
242
+ volume: input.values.volume,
243
+ first_page: input.values['first-page'],
244
+ author: input.values.author,
245
+ key: input.values.key ?? '1',
246
+ };
247
+ }
248
+ /** Read --input: one JSON document (an array or a single object), or JSON Lines. */
249
+ async function readCitationList(input, path) {
250
+ if (CITATION_OPTIONS.some((name) => input.values[name] !== undefined)) {
251
+ throw new UsageError('use either --input or the single-citation options, not both');
252
+ }
253
+ const text = stripBom(await input.readText(path)).trim();
254
+ if (!text)
255
+ throw new UsageError('--input is empty');
256
+ let items;
257
+ try {
258
+ items = parseJsonList(text);
259
+ }
260
+ catch (err) {
261
+ throw new UsageError(`--input is not JSON or JSON Lines: ${errorMessage(err)}`);
262
+ }
263
+ return items.map(normalizeCitation);
264
+ }
265
+ /** Drop a leading byte order mark, which some Windows editors write. */
266
+ function stripBom(text) {
267
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
268
+ }
269
+ function parseJsonList(text) {
270
+ try {
271
+ const value = JSON.parse(text);
272
+ return Array.isArray(value) ? value : [value];
273
+ }
274
+ catch {
275
+ // Not a single JSON document: read it as JSON Lines.
276
+ return text
277
+ .split(/\r?\n/)
278
+ .filter((line) => line.trim() !== '')
279
+ .map((line) => JSON.parse(line));
280
+ }
281
+ }
282
+ /**
283
+ * Fill in a missing key and accept the loose types that CSV-derived JSON often
284
+ * has: numbers for volume, first_page, and key, and a numeric string for year.
285
+ */
286
+ function normalizeCitation(item, index) {
287
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) {
288
+ throw new UsageError(`citation ${index + 1} in --input is not a JSON object`);
289
+ }
290
+ const c = item;
291
+ return {
292
+ ...c,
293
+ year: typeof c.year === 'string' && /^\d+$/.test(c.year.trim()) ? Number(c.year) : c.year,
294
+ volume: numberToString(c.volume),
295
+ first_page: numberToString(c.first_page),
296
+ key: c.key === undefined ? String(index + 1) : numberToString(c.key),
297
+ };
298
+ }
299
+ function numberToString(value) {
300
+ return typeof value === 'number' ? String(value) : value;
301
+ }
302
+ function validateCitation(item, index) {
303
+ const result = CITATION_SCHEMA.safeParse(item);
304
+ if (!result.success) {
305
+ const issue = result.error.issues[0];
306
+ throw new UsageError(`citation ${index + 1}: invalid ${issue.path.join('.') || 'value'}: ${issue.message}`);
307
+ }
308
+ return result.data;
309
+ }
310
+ // ─── convert ────────────────────────────────────────────────────────────────
311
+ const convert = {
312
+ name: 'convert',
313
+ tool: 'convert_article_ids',
314
+ description: 'Convert between PMID, PMCID, and DOI',
315
+ options: {
316
+ 'id-type': { type: 'string' },
317
+ },
318
+ help: `Usage: pubmed convert <id>... | - [--id-type pmid|pmcid|doi]
319
+
320
+ Convert between PMID, PMCID, and DOI with the NCBI PMC ID Converter (same
321
+ JSON as convert_article_ids). All IDs must be of the --id-type given
322
+ (default pmid). Lists longer than ${MAX_CONVERT_IDS} IDs are split into batches.
323
+
324
+ Example:
325
+ pubmed convert 34265844 | jq -r '.records[] | [.pmid, .pmcid // "", .doi // ""] | @tsv'
326
+ `,
327
+ async execute(input, client) {
328
+ const idType = input.values['id-type'] ?? 'pmid';
329
+ const kind = CONVERT_ID_KINDS.get(idType);
330
+ if (!kind) {
331
+ throw new UsageError(`invalid --id-type: expected pmid, pmcid, or doi, got ${JSON.stringify(idType)}`);
332
+ }
333
+ const ids = await readIds(input, kind);
334
+ const batches = chunk(ids, MAX_CONVERT_IDS).map((batch) => validate(convertArticleIdsSchema, { ids: batch, id_type: idType }));
335
+ const results = await runBatches(batches, (params) => convertArticleIds(params, client));
336
+ // Batches share one response envelope; only the ID lists grow.
337
+ const [first] = results;
338
+ const output = {
339
+ ...first,
340
+ request: { ...first.request, ids: results.flatMap((r) => r.request.ids) },
341
+ records: results.flatMap((r) => r.records),
342
+ };
343
+ return { output, summary: plural(output.records.length, 'record') };
344
+ },
345
+ };
346
+ // ─── fulltext ───────────────────────────────────────────────────────────────
347
+ const fulltext = {
348
+ name: 'fulltext',
349
+ tool: 'get_full_text_article',
350
+ description: 'Get the full text of PMC open-access articles',
351
+ options: {},
352
+ help: `Usage: pubmed fulltext <pmcid>... | -
353
+
354
+ Get the full text of PMC articles as plain text: all sections, figure and
355
+ table captions, and references (same JSON as get_full_text_article). Only the
356
+ PMC Open Access subset is available; other IDs are listed in errors. Lists
357
+ longer than ${MAX_FULL_TEXT_IDS} PMCIDs are fetched in batches and merged.
358
+
359
+ Each article is typically tens of thousands of characters. Save them with -o
360
+ and pull out only the passages you need. Get PMCIDs from PMIDs with pubmed
361
+ convert, or check which PMIDs have full text with pubmed copyright.
362
+
363
+ Example:
364
+ pubmed fulltext PMC8371605 -o full.json
365
+ jq -r '.articles[0].full_text' full.json | grep -n -i 'confidence'
366
+ `,
367
+ async execute(input, client) {
368
+ const pmcids = await readIds(input, PMCID);
369
+ const batches = chunk(pmcids, MAX_FULL_TEXT_IDS).map((batch) => validate(getFullTextArticleSchema, { pmc_ids: batch }));
370
+ const results = await runBatches(batches, (params) => getFullTextArticle(params, client));
371
+ const articles = results.flatMap((r) => r.articles);
372
+ const output = {
373
+ important_legal_notice: results[0].important_legal_notice,
374
+ articles,
375
+ errors: results.flatMap((r) => r.errors),
376
+ count: articles.length,
377
+ };
378
+ return {
379
+ output,
380
+ summary: `${plural(output.count, 'article')}, ${output.errors.length} unavailable`,
381
+ };
382
+ },
383
+ };
384
+ // ─── copyright ──────────────────────────────────────────────────────────────
385
+ const copyright = {
386
+ name: 'copyright',
387
+ tool: 'get_copyright_status',
388
+ description: 'Check full-text availability and license',
389
+ options: {},
390
+ help: `Usage: pubmed copyright <pmid>... | -
391
+
392
+ Check whether full text can be retrieved (has_full_text), plus the license,
393
+ copyright statement, and PMC open-access status (same JSON as
394
+ get_copyright_status). Lists longer than ${MAX_COPYRIGHT_PMIDS} PMIDs are split into batches.
395
+
396
+ Example:
397
+ pubmed copyright - < pmids.txt -o rights.json
398
+ jq -r '.statuses[] | select(.has_full_text) | .pmcid' rights.json | pubmed fulltext - -o full.json
399
+ `,
400
+ async execute(input, client) {
401
+ const pmids = await readIds(input, PMID);
402
+ const batches = chunk(pmids, MAX_COPYRIGHT_PMIDS).map((batch) => validate(getCopyrightStatusSchema, { pmids: batch }));
403
+ const results = await runBatches(batches, (params) => getCopyrightStatus(params, client));
404
+ const statuses = results.flatMap((r) => r.statuses);
405
+ const output = { statuses, count: statuses.length };
406
+ const withFullText = statuses.filter((s) => s.has_full_text).length;
407
+ return { output, summary: `${plural(output.count, 'PMID')}, ${withFullText} with full text` };
408
+ },
409
+ };
410
+ export const COMMANDS = [
411
+ search,
412
+ metadata,
413
+ related,
414
+ citation,
415
+ convert,
416
+ fulltext,
417
+ copyright,
418
+ ];
419
+ const PMID = { label: 'PMID', pattern: /^\d+$/ };
420
+ const PMCID = { label: 'PMCID', pattern: /^(PMC)?\d+$/i };
421
+ const DOI = { label: 'DOI', pattern: /^10\.\S+\/\S+$/ };
422
+ const CONVERT_ID_KINDS = new Map([
423
+ ['pmid', PMID],
424
+ ['pmcid', PMCID],
425
+ ['doi', DOI],
426
+ ]);
427
+ /**
428
+ * Collect IDs from the arguments, separated by whitespace or commas.
429
+ * "-" reads them from stdin, e.g. the output of jq -r '.pmids[]'.
430
+ */
431
+ async function readIds(input, kind) {
432
+ const ids = [];
433
+ for (const arg of input.positionals) {
434
+ const text = arg === '-' ? await input.readText('-') : arg;
435
+ ids.push(...text.split(/[\s,]+/).filter(Boolean));
436
+ }
437
+ if (ids.length === 0) {
438
+ throw new UsageError(`no ${kind.label}s given; pass them as arguments or use - to read stdin`);
439
+ }
440
+ const invalid = ids.find((id) => !kind.pattern.test(id));
441
+ if (invalid !== undefined) {
442
+ const hint = /^[[{"]/.test(invalid)
443
+ ? ` (IDs must be plain text; extract them from JSON first, e.g. jq -r '.pmids[]')`
444
+ : '';
445
+ throw new UsageError(`not a ${kind.label}: ${JSON.stringify(invalid)}${hint}`);
446
+ }
447
+ return ids;
448
+ }
449
+ /** Parse a whole-number option; the tool's schema checks the range. */
450
+ function intOption(input, name) {
451
+ const value = input.values[name];
452
+ if (value === undefined)
453
+ return undefined;
454
+ if (!/^\d+$/.test(value.trim())) {
455
+ throw new UsageError(`--${name} must be a whole number, got ${JSON.stringify(value)}`);
456
+ }
457
+ return Number(value);
458
+ }
459
+ function boolOption(input, name) {
460
+ const value = input.values[name];
461
+ if (value === undefined)
462
+ return undefined;
463
+ if (value === 'true')
464
+ return true;
465
+ if (value === 'false')
466
+ return false;
467
+ throw new UsageError(`--${name} must be true or false, got ${JSON.stringify(value)}`);
468
+ }
469
+ /** Validate params with a tool's zod schema, as the MCP server does before calling it. */
470
+ function validate(schema, params) {
471
+ const result = z.object(schema).safeParse(params);
472
+ if (!result.success) {
473
+ const issue = result.error.issues[0];
474
+ // Every schema key that can fail here comes from an option of the same name.
475
+ const option = `--${String(issue.path[0]).replace(/_/g, '-')}`;
476
+ throw new UsageError(`invalid ${option}: ${issue.message}`);
477
+ }
478
+ return result.data;
479
+ }
480
+ function chunk(items, size) {
481
+ const batches = [];
482
+ for (let i = 0; i < items.length; i += size) {
483
+ batches.push(items.slice(i, i + size));
484
+ }
485
+ return batches;
486
+ }
487
+ /** Run batches one at a time; an error names the batch that failed. */
488
+ async function runBatches(batches, run) {
489
+ const results = [];
490
+ for (const [index, params] of batches.entries()) {
491
+ try {
492
+ results.push(await run(params));
493
+ }
494
+ catch (err) {
495
+ if (batches.length === 1)
496
+ throw err;
497
+ throw new Error(`batch ${index + 1} of ${batches.length}: ${errorMessage(err)}`);
498
+ }
499
+ }
500
+ return results;
501
+ }
502
+ function plural(count, noun) {
503
+ return `${count} ${noun}${count === 1 ? '' : 's'}`;
504
+ }
505
+ export function errorMessage(err) {
506
+ return err instanceof Error ? err.message : String(err);
507
+ }
508
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Argument parsing, dispatch, and output for the pubmed CLI.
3
+ *
4
+ * Kept apart from the entrypoint (src/cli.ts) so tests can run commands with
5
+ * in-memory I/O and a mocked NCBI client.
6
+ *
7
+ * Output contract: the command's JSON goes to stdout (or to the -o file) and
8
+ * nothing else does; messages go to stderr. Exit status is 0 on success, 1
9
+ * when NCBI or a file operation fails, and 2 for invalid usage.
10
+ */
11
+ import { NcbiClient } from '../pubmed/client.js';
12
+ export interface CliIo {
13
+ stdout(text: string): void;
14
+ stderr(text: string): void;
15
+ /** Read a file, or stdin when path is "-". */
16
+ readText(path: string): Promise<string>;
17
+ writeText(path: string, text: string): Promise<void>;
18
+ }
19
+ /** Run one CLI invocation and return its exit status. */
20
+ export declare function runCli(argv: string[], io: CliIo, client?: NcbiClient): Promise<number>;
21
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AASjD,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAOD,yDAAyD;AACzD,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,EAAE,KAAK,EACT,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CA+EjB"}