@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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_full_text_article
|
|
3
|
+
*
|
|
4
|
+
* Retrieve the full text of articles from PubMed Central (PMC).
|
|
5
|
+
* Full text is available for PMC articles whose publisher allows full-text
|
|
6
|
+
* download (the same test get_copyright_status uses for has_full_text).
|
|
7
|
+
* Articles not in PMC will return an error entry.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { IMPORTANT_LEGAL_NOTICE } from '../pubmed/types.js';
|
|
11
|
+
import { parsePmcEfetchXml } from '../utils/xml.js';
|
|
12
|
+
/** PMCIDs per request; the CLI splits longer lists into batches of this size. */
|
|
13
|
+
export const MAX_FULL_TEXT_IDS = 20;
|
|
14
|
+
export const getFullTextArticleSchema = {
|
|
15
|
+
pmc_ids: z
|
|
16
|
+
.array(z.string().min(1))
|
|
17
|
+
.min(1)
|
|
18
|
+
.max(MAX_FULL_TEXT_IDS)
|
|
19
|
+
.describe('Array of PubMed Central IDs (PMCIDs) to retrieve full text for. ' +
|
|
20
|
+
'PMCIDs must include the "PMC" prefix (e.g., ["PMC9583675", "PMC7984072"]). ' +
|
|
21
|
+
'Articles without downloadable full text are excluded from articles ' +
|
|
22
|
+
'and reported in errors.'),
|
|
23
|
+
};
|
|
24
|
+
/** Strip the PMC prefix to get the numeric portion NCBI expects. */
|
|
25
|
+
function stripPmcPrefix(id) {
|
|
26
|
+
const trimmed = id.trim();
|
|
27
|
+
return trimmed.toUpperCase().startsWith('PMC') ? trimmed.slice(3) : trimmed;
|
|
28
|
+
}
|
|
29
|
+
function normalizePmcId(id) {
|
|
30
|
+
return `PMC${stripPmcPrefix(id)}`.toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
export async function getFullTextArticle(params, client) {
|
|
33
|
+
// NCBI efetch for PMC expects numeric IDs (without "PMC" prefix)
|
|
34
|
+
const numericIds = params.pmc_ids.map(stripPmcPrefix);
|
|
35
|
+
const xml = await client.efetch({
|
|
36
|
+
db: 'pmc',
|
|
37
|
+
id: numericIds,
|
|
38
|
+
retmode: 'xml',
|
|
39
|
+
});
|
|
40
|
+
let parsedArticles;
|
|
41
|
+
try {
|
|
42
|
+
parsedArticles = parsePmcEfetchXml(xml);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
process.stderr.write(`PMC XML parse error: ${String(err)}\n`);
|
|
46
|
+
return {
|
|
47
|
+
important_legal_notice: IMPORTANT_LEGAL_NOTICE,
|
|
48
|
+
articles: [],
|
|
49
|
+
errors: params.pmc_ids.map((pmcid) => ({
|
|
50
|
+
pmcid: normalizePmcId(pmcid),
|
|
51
|
+
error: 'Failed to parse the PMC EFetch response.',
|
|
52
|
+
})),
|
|
53
|
+
count: 0,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const articles = [];
|
|
57
|
+
const errors = [];
|
|
58
|
+
const returnedIds = new Set();
|
|
59
|
+
for (const [index, article] of parsedArticles.entries()) {
|
|
60
|
+
const pmcid = article.identifiers.pmcid
|
|
61
|
+
? normalizePmcId(article.identifiers.pmcid)
|
|
62
|
+
: normalizePmcId(params.pmc_ids[index] ?? '');
|
|
63
|
+
returnedIds.add(pmcid);
|
|
64
|
+
if (article.full_text.trim()) {
|
|
65
|
+
articles.push(article);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
errors.push({
|
|
69
|
+
pmcid,
|
|
70
|
+
error: 'Full text is not available from PMC EFetch for this article.',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const pmcid of new Set(params.pmc_ids.map(normalizePmcId))) {
|
|
75
|
+
if (!returnedIds.has(pmcid)) {
|
|
76
|
+
errors.push({
|
|
77
|
+
pmcid,
|
|
78
|
+
error: 'PMC EFetch did not return this article.',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
important_legal_notice: IMPORTANT_LEGAL_NOTICE,
|
|
84
|
+
articles,
|
|
85
|
+
errors,
|
|
86
|
+
count: articles.length,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=getFullTextArticle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFullTextArticle.js","sourceRoot":"","sources":["../../src/tools/getFullTextArticle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: lookup_article_by_citation
|
|
3
|
+
*
|
|
4
|
+
* Look up PubMed IDs for articles given structured citation metadata
|
|
5
|
+
* (journal, year, volume, first page, author). Uses NCBI's ECitmatch endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { NcbiClient } from '../pubmed/client.js';
|
|
9
|
+
import type { LookupArticleByCitationResponse } from '../pubmed/types.js';
|
|
10
|
+
/** Citations per request; the CLI splits longer lists into batches of this size. */
|
|
11
|
+
export declare const MAX_CITATIONS = 100;
|
|
12
|
+
export declare const lookupArticleByCitationSchema: {
|
|
13
|
+
citations: z.ZodArray<z.ZodObject<{
|
|
14
|
+
journal: z.ZodString;
|
|
15
|
+
year: z.ZodNumber;
|
|
16
|
+
volume: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
17
|
+
first_page: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
18
|
+
author: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
19
|
+
key: z.ZodString;
|
|
20
|
+
}, z.core.$strip>>;
|
|
21
|
+
};
|
|
22
|
+
export type LookupArticleByCitationParams = {
|
|
23
|
+
citations: Array<{
|
|
24
|
+
journal: string;
|
|
25
|
+
year: number;
|
|
26
|
+
volume?: string;
|
|
27
|
+
first_page?: string;
|
|
28
|
+
author?: string;
|
|
29
|
+
key: string;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
export declare function lookupArticleByCitation(params: LookupArticleByCitationParams, client: NcbiClient): Promise<LookupArticleByCitationResponse>;
|
|
33
|
+
//# sourceMappingURL=lookupArticleByCitation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookupArticleByCitation.d.ts","sourceRoot":"","sources":["../../src/tools/lookupArticleByCitation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,+BAA+B,EAAkB,MAAM,oBAAoB,CAAC;AAE1F,oFAAoF;AACpF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,eAAO,MAAM,6BAA6B;;;;;;;;;CAmDzC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,KAAK,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;CACJ,CAAC;AA2CF,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,EACrC,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,+BAA+B,CAAC,CAc1C"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: lookup_article_by_citation
|
|
3
|
+
*
|
|
4
|
+
* Look up PubMed IDs for articles given structured citation metadata
|
|
5
|
+
* (journal, year, volume, first page, author). Uses NCBI's ECitmatch endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
/** Citations per request; the CLI splits longer lists into batches of this size. */
|
|
9
|
+
export const MAX_CITATIONS = 100;
|
|
10
|
+
export const lookupArticleByCitationSchema = {
|
|
11
|
+
citations: z
|
|
12
|
+
.array(z.object({
|
|
13
|
+
journal: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('Journal name or abbreviation (e.g., "Nature", "J Immunol", ' +
|
|
16
|
+
'"N Engl J Med"). Spaces should be included as-is.'),
|
|
17
|
+
year: z
|
|
18
|
+
.number()
|
|
19
|
+
.int()
|
|
20
|
+
.min(1800)
|
|
21
|
+
.max(2100)
|
|
22
|
+
.describe('Publication year (e.g., 2023).'),
|
|
23
|
+
volume: z
|
|
24
|
+
.string()
|
|
25
|
+
.default('')
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Journal volume number (e.g., "204"). Leave empty if unknown.'),
|
|
28
|
+
first_page: z
|
|
29
|
+
.string()
|
|
30
|
+
.default('')
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('First page number of the article (e.g., "1234"). ' +
|
|
33
|
+
'Leave empty if unknown.'),
|
|
34
|
+
author: z
|
|
35
|
+
.string()
|
|
36
|
+
.default('')
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('First author last name (e.g., "Smith"). ' +
|
|
39
|
+
'Leave empty if unknown.'),
|
|
40
|
+
key: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe('Your own identifier for this citation (e.g., "ref1", "paper_A"). ' +
|
|
43
|
+
'Echoed back in the response to correlate results.'),
|
|
44
|
+
}))
|
|
45
|
+
.min(1)
|
|
46
|
+
.max(MAX_CITATIONS)
|
|
47
|
+
.describe('Array of citation objects to look up. ' +
|
|
48
|
+
'Provide as much information as available for best match accuracy.'),
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Parse the plain-text response from ecitmatch.cgi.
|
|
52
|
+
*
|
|
53
|
+
* Format: journal|year|volume|first_page|author|key|PMID_or_NOT_FOUND
|
|
54
|
+
* One line per citation (empty lines ignored).
|
|
55
|
+
*/
|
|
56
|
+
function parseEcitmatchResponse(text, originalCitations) {
|
|
57
|
+
if (/^\s*(?:<!doctype\s+html|<html\b)/i.test(text)) {
|
|
58
|
+
throw new Error('ECitMatch returned HTML instead of citation match text');
|
|
59
|
+
}
|
|
60
|
+
const lines = text
|
|
61
|
+
.split(/\r?\n/)
|
|
62
|
+
.map((l) => l.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
return lines.map((line, idx) => {
|
|
65
|
+
const parts = line.split('|');
|
|
66
|
+
// Fields: [0]journal, [1]year, [2]volume, [3]first_page, [4]author, [5]key, [6]result
|
|
67
|
+
const result = parts.at(-1)?.trim() ?? '';
|
|
68
|
+
const isFound = result !== '' && result !== 'NOT_FOUND' && /^\d+$/.test(result);
|
|
69
|
+
// Use original citation data where available (ecitmatch may alter formatting)
|
|
70
|
+
const original = originalCitations[idx];
|
|
71
|
+
const echoedKey = parts.length >= 7 ? parts[5] : parts[0];
|
|
72
|
+
return {
|
|
73
|
+
journal: original?.journal ?? parts[0] ?? '',
|
|
74
|
+
year: String(original?.year ?? parts[1] ?? ''),
|
|
75
|
+
volume: original?.volume ?? parts[2] ?? '',
|
|
76
|
+
first_page: original?.first_page ?? parts[3] ?? '',
|
|
77
|
+
author: original?.author ?? parts[4] ?? '',
|
|
78
|
+
pmid: isFound ? result : '',
|
|
79
|
+
key: original?.key ?? echoedKey ?? '',
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export async function lookupArticleByCitation(params, client) {
|
|
84
|
+
const citations = params.citations.map((c) => ({
|
|
85
|
+
journal: c.journal,
|
|
86
|
+
year: c.year,
|
|
87
|
+
volume: c.volume ?? '',
|
|
88
|
+
first_page: c.first_page ?? '',
|
|
89
|
+
author: c.author ?? '',
|
|
90
|
+
key: c.key,
|
|
91
|
+
}));
|
|
92
|
+
const rawText = await client.ecitmatch(citations);
|
|
93
|
+
const results = parseEcitmatchResponse(rawText, params.citations);
|
|
94
|
+
return { citations: results };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=lookupArticleByCitation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookupArticleByCitation.js","sourceRoot":"","sources":["../../src/tools/lookupArticleByCitation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: search_articles
|
|
3
|
+
*
|
|
4
|
+
* Search PubMed for biomedical articles using standard PubMed syntax or
|
|
5
|
+
* keywords. Returns a list of PMIDs plus metadata about the result set.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { NcbiClient } from '../pubmed/client.js';
|
|
9
|
+
import type { SearchArticlesResponse } from '../pubmed/types.js';
|
|
10
|
+
declare const SEARCH_FIELDS: readonly ["title", "tiab", "mesh", "author", "journal", "affiliation"];
|
|
11
|
+
export declare const searchArticlesSchema: {
|
|
12
|
+
query: z.ZodString;
|
|
13
|
+
max_results: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
14
|
+
retstart: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
15
|
+
sort: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
relevance: "relevance";
|
|
17
|
+
pub_date: "pub_date";
|
|
18
|
+
author: "author";
|
|
19
|
+
journal_name: "journal_name";
|
|
20
|
+
}>>;
|
|
21
|
+
field: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
author: "author";
|
|
23
|
+
title: "title";
|
|
24
|
+
tiab: "tiab";
|
|
25
|
+
mesh: "mesh";
|
|
26
|
+
journal: "journal";
|
|
27
|
+
affiliation: "affiliation";
|
|
28
|
+
}>>;
|
|
29
|
+
date_from: z.ZodOptional<z.ZodString>;
|
|
30
|
+
date_to: z.ZodOptional<z.ZodString>;
|
|
31
|
+
datetype: z.ZodDefault<z.ZodEnum<{
|
|
32
|
+
pdat: "pdat";
|
|
33
|
+
edat: "edat";
|
|
34
|
+
mdat: "mdat";
|
|
35
|
+
}>>;
|
|
36
|
+
};
|
|
37
|
+
export type SearchArticlesParams = {
|
|
38
|
+
query: string;
|
|
39
|
+
max_results?: number;
|
|
40
|
+
retstart?: number;
|
|
41
|
+
sort?: 'relevance' | 'pub_date' | 'author' | 'journal_name';
|
|
42
|
+
field?: (typeof SEARCH_FIELDS)[number];
|
|
43
|
+
date_from?: string;
|
|
44
|
+
date_to?: string;
|
|
45
|
+
datetype?: 'pdat' | 'edat' | 'mdat';
|
|
46
|
+
};
|
|
47
|
+
export declare function searchArticles(params: SearchArticlesParams, client: NcbiClient): Promise<SearchArticlesResponse>;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=searchArticles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchArticles.d.ts","sourceRoot":"","sources":["../../src/tools/searchArticles.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAUjE,QAAA,MAAM,aAAa,wEAOT,CAAC;AAEX,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;CAqEhC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,cAAc,CAAC;IAC5D,KAAK,CAAC,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACrC,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,sBAAsB,CAAC,CAmCjC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: search_articles
|
|
3
|
+
*
|
|
4
|
+
* Search PubMed for biomedical articles using standard PubMed syntax or
|
|
5
|
+
* keywords. Returns a list of PMIDs plus metadata about the result set.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
// Sort parameter mapping: tool value → NCBI API value
|
|
9
|
+
const SORT_MAP = {
|
|
10
|
+
relevance: 'relevance',
|
|
11
|
+
pub_date: 'pub_date',
|
|
12
|
+
author: 'Author',
|
|
13
|
+
journal_name: 'JournalName',
|
|
14
|
+
};
|
|
15
|
+
const SEARCH_FIELDS = [
|
|
16
|
+
'title',
|
|
17
|
+
'tiab',
|
|
18
|
+
'mesh',
|
|
19
|
+
'author',
|
|
20
|
+
'journal',
|
|
21
|
+
'affiliation',
|
|
22
|
+
];
|
|
23
|
+
export const searchArticlesSchema = {
|
|
24
|
+
query: z
|
|
25
|
+
.string()
|
|
26
|
+
.min(1)
|
|
27
|
+
.describe('PubMed search query using standard PubMed syntax or plain keywords. ' +
|
|
28
|
+
'Supports Boolean operators (AND, OR, NOT), field tags ([Title], [Author], ' +
|
|
29
|
+
'[MeSH Terms], etc.), and date ranges.'),
|
|
30
|
+
max_results: z
|
|
31
|
+
.number()
|
|
32
|
+
.int()
|
|
33
|
+
.min(1)
|
|
34
|
+
.max(10000)
|
|
35
|
+
.default(20)
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('Maximum number of article IDs to return (default: 20, max: 10000).'),
|
|
38
|
+
retstart: z
|
|
39
|
+
.number()
|
|
40
|
+
.int()
|
|
41
|
+
.min(0)
|
|
42
|
+
.default(0)
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Zero-based offset of the first result to return. ' +
|
|
45
|
+
'Use with max_results for pagination.'),
|
|
46
|
+
sort: z
|
|
47
|
+
.enum(['relevance', 'pub_date', 'author', 'journal_name'])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Sort order for results. ' +
|
|
50
|
+
'relevance: most relevant first; ' +
|
|
51
|
+
'pub_date: most recent first; ' +
|
|
52
|
+
'author: alphabetical by first author; ' +
|
|
53
|
+
'journal_name: alphabetical by journal.'),
|
|
54
|
+
field: z
|
|
55
|
+
.enum(SEARCH_FIELDS)
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Optional PubMed field applied to every untagged term in the query: ' +
|
|
58
|
+
'title, tiab (title/abstract), mesh, author, journal, or affiliation. ' +
|
|
59
|
+
'This prevents cross-field automatic term mapping for those terms. ' +
|
|
60
|
+
'An explicit field tag in the query takes precedence for the tagged term.'),
|
|
61
|
+
date_from: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('Start of date range filter (YYYY/MM/DD, YYYY/MM, or YYYY). ' +
|
|
65
|
+
'Applied to the date type specified by datetype. May be given without date_to.'),
|
|
66
|
+
date_to: z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe('End of date range filter (YYYY/MM/DD, YYYY/MM, or YYYY). ' +
|
|
70
|
+
'Applied to the date type specified by datetype. May be given without date_from.'),
|
|
71
|
+
datetype: z
|
|
72
|
+
.enum(['pdat', 'edat', 'mdat'])
|
|
73
|
+
.default('pdat')
|
|
74
|
+
.describe('Date type to filter on: ' +
|
|
75
|
+
'pdat = publication date (default); ' +
|
|
76
|
+
'edat = Entrez date (date added to PubMed); ' +
|
|
77
|
+
'mdat = modification date.'),
|
|
78
|
+
};
|
|
79
|
+
export async function searchArticles(params, client) {
|
|
80
|
+
const retmax = params.max_results ?? 20;
|
|
81
|
+
const retstart = params.retstart ?? 0;
|
|
82
|
+
const result = await client.esearch({
|
|
83
|
+
term: params.query,
|
|
84
|
+
retmax,
|
|
85
|
+
retstart,
|
|
86
|
+
sort: params.sort ? SORT_MAP[params.sort] : undefined,
|
|
87
|
+
...(params.field ? { field: params.field } : {}),
|
|
88
|
+
datetype: params.datetype ?? 'pdat',
|
|
89
|
+
...dateRange(params.date_from, params.date_to),
|
|
90
|
+
});
|
|
91
|
+
const total_count = parseInt(result.count, 10);
|
|
92
|
+
const returned_count = result.idlist.length;
|
|
93
|
+
const has_more = retstart + returned_count < total_count;
|
|
94
|
+
return {
|
|
95
|
+
pmids: result.idlist,
|
|
96
|
+
total_count,
|
|
97
|
+
returned_count,
|
|
98
|
+
query: params.query,
|
|
99
|
+
query_translation: result.querytranslation ?? '',
|
|
100
|
+
has_more,
|
|
101
|
+
warnings: {
|
|
102
|
+
phrases_ignored: result.warninglist?.phrasesignored ?? [],
|
|
103
|
+
quoted_phrases_not_found: result.warninglist?.quotedphrasesnotfound ?? [],
|
|
104
|
+
output_messages: result.warninglist?.outputmessages ?? [],
|
|
105
|
+
},
|
|
106
|
+
errors: {
|
|
107
|
+
phrases_not_found: result.errorlist?.phrasesnotfound ?? [],
|
|
108
|
+
fields_not_found: result.errorlist?.fieldsnotfound ?? [],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// NCBI ESearch silently ignores mindate or maxdate unless both are sent, so a
|
|
113
|
+
// one-sided range is completed with an open bound on the other side.
|
|
114
|
+
const OPEN_MIN_DATE = '1000';
|
|
115
|
+
const OPEN_MAX_DATE = '3000';
|
|
116
|
+
function dateRange(from, to) {
|
|
117
|
+
if (!from && !to)
|
|
118
|
+
return {};
|
|
119
|
+
return { mindate: from || OPEN_MIN_DATE, maxdate: to || OPEN_MAX_DATE };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=searchArticles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchArticles.js","sourceRoot":"","sources":["../../src/tools/searchArticles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML parsing utilities for NCBI E-utilities responses.
|
|
3
|
+
*
|
|
4
|
+
* PubMed returns XML by default from efetch.fcgi.
|
|
5
|
+
* PMC full-text articles use the JATS XML format.
|
|
6
|
+
*/
|
|
7
|
+
import type { ArticleMetadata, FullTextArticle } from '../pubmed/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parse the XML returned by efetch.fcgi (db=pubmed) into ArticleMetadata objects.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parsePubmedEfetchXml(xml: string): ArticleMetadata[];
|
|
12
|
+
/**
|
|
13
|
+
* Parse the XML returned by efetch.fcgi (db=pmc) into FullTextArticle objects.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parsePmcEfetchXml(xml: string): FullTextArticle[];
|
|
16
|
+
export interface PmcAccessInfo {
|
|
17
|
+
pmcid: string;
|
|
18
|
+
/** PMC's own flag (custom-meta "pmc-prop-open-access"). */
|
|
19
|
+
isOpenAccess: boolean;
|
|
20
|
+
/** Same test get_full_text_article applies: EFetch returned body/back text. */
|
|
21
|
+
hasFullText: boolean;
|
|
22
|
+
/** Short license code from custom-meta "pmc-license-ref", e.g. "CC BY-NC-ND". */
|
|
23
|
+
license?: string;
|
|
24
|
+
copyrightStatement?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read access and license status from efetch.fcgi (db=pmc) XML.
|
|
28
|
+
* Replaces the PMC OA Web Service (oa.fcgi), which NCBI retired on 2026-08-25.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parsePmcAccessXml(xml: string): PmcAccessInfo[];
|
|
31
|
+
/**
|
|
32
|
+
* Recursively extract all plain text from a parsed XML node.
|
|
33
|
+
* Inline elements (italic, bold, sup, sub, etc.) are flattened to text.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractText(node: unknown): string;
|
|
36
|
+
export declare function cleanPmcText(text: string): string;
|
|
37
|
+
//# sourceMappingURL=xml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../../src/utils/xml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAKV,eAAe,EAEf,eAAe,EAEhB,MAAM,oBAAoB,CAAC;AA2G5B;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAOnE;AAyID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAOhE;AAqJD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,YAAY,EAAE,OAAO,CAAC;IACtB,+EAA+E;IAC/E,WAAW,EAAE,OAAO,CAAC;IACrB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,CA+B9D;AAWD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CA4BjD;AA+CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQjD"}
|