@djmima/pubmed-mcp 0.0.0-stage → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +793 -3
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +97 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli/commands.d.ts +42 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +508 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/run.d.ts +21 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +154 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/pubmed/client.d.ts +150 -0
- package/dist/pubmed/client.d.ts.map +1 -0
- package/dist/pubmed/client.js +315 -0
- package/dist/pubmed/client.js.map +1 -0
- package/dist/pubmed/types.d.ts +159 -0
- package/dist/pubmed/types.d.ts.map +1 -0
- package/dist/pubmed/types.js +11 -0
- package/dist/pubmed/types.js.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +157 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/convertArticleIds.d.ts +25 -0
- package/dist/tools/convertArticleIds.d.ts.map +1 -0
- package/dist/tools/convertArticleIds.js +39 -0
- package/dist/tools/convertArticleIds.js.map +1 -0
- package/dist/tools/findRelatedArticles.d.ts +30 -0
- package/dist/tools/findRelatedArticles.d.ts.map +1 -0
- package/dist/tools/findRelatedArticles.js +77 -0
- package/dist/tools/findRelatedArticles.js.map +1 -0
- package/dist/tools/getArticleMetadata.d.ts +31 -0
- package/dist/tools/getArticleMetadata.d.ts.map +1 -0
- package/dist/tools/getArticleMetadata.js +72 -0
- package/dist/tools/getArticleMetadata.js.map +1 -0
- package/dist/tools/getCopyrightStatus.d.ts +25 -0
- package/dist/tools/getCopyrightStatus.d.ts.map +1 -0
- package/dist/tools/getCopyrightStatus.js +84 -0
- package/dist/tools/getCopyrightStatus.js.map +1 -0
- package/dist/tools/getFullTextArticle.d.ts +21 -0
- package/dist/tools/getFullTextArticle.d.ts.map +1 -0
- package/dist/tools/getFullTextArticle.js +89 -0
- package/dist/tools/getFullTextArticle.js.map +1 -0
- package/dist/tools/lookupArticleByCitation.d.ts +33 -0
- package/dist/tools/lookupArticleByCitation.d.ts.map +1 -0
- package/dist/tools/lookupArticleByCitation.js +96 -0
- package/dist/tools/lookupArticleByCitation.js.map +1 -0
- package/dist/tools/searchArticles.d.ts +49 -0
- package/dist/tools/searchArticles.d.ts.map +1 -0
- package/dist/tools/searchArticles.js +121 -0
- package/dist/tools/searchArticles.js.map +1 -0
- package/dist/utils/xml.d.ts +37 -0
- package/dist/utils/xml.d.ts.map +1 -0
- package/dist/utils/xml.js +515 -0
- package/dist/utils/xml.js.map +1 -0
- package/package.json +72 -4
- 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"}
|