@cyanheads/pubmed-mcp-server 2.10.8 → 2.10.10
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +6 -4
- package/dist/mcp-server/tools/definitions/_text.d.ts +23 -1
- package/dist/mcp-server/tools/definitions/_text.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_text.js +25 -1
- package/dist/mcp-server/tools/definitions/_text.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +35 -0
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +657 -44
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts +8 -0
- package/dist/mcp-server/tools/definitions/find-related.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/find-related.tool.js +131 -23
- package/dist/mcp-server/tools/definitions/find-related.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js +14 -3
- package/dist/mcp-server/tools/definitions/lookup-mesh.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/search-articles.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-articles.tool.js +15 -3
- package/dist/mcp-server/tools/definitions/search-articles.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/spell-check.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/spell-check.tool.js +15 -3
- package/dist/mcp-server/tools/definitions/spell-check.tool.js.map +1 -1
- package/dist/services/error-contracts.d.ts +22 -2
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +24 -2
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.d.ts +3 -3
- package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -1
- package/dist/services/europe-pmc/europe-pmc-service.js +8 -16
- package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -1
- package/dist/services/ncbi/parsing/ordered-xml-parser-options.d.ts +44 -0
- package/dist/services/ncbi/parsing/ordered-xml-parser-options.d.ts.map +1 -0
- package/dist/services/ncbi/parsing/ordered-xml-parser-options.js +41 -0
- package/dist/services/ncbi/parsing/ordered-xml-parser-options.js.map +1 -0
- package/dist/services/ncbi/parsing/pmc-article-parser.d.ts +70 -10
- package/dist/services/ncbi/parsing/pmc-article-parser.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/pmc-article-parser.js +795 -79
- package/dist/services/ncbi/parsing/pmc-article-parser.js.map +1 -1
- package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts +18 -0
- package/dist/services/ncbi/parsing/pmc-xml-helpers.d.ts.map +1 -1
- package/dist/services/ncbi/parsing/pmc-xml-helpers.js +39 -3
- package/dist/services/ncbi/parsing/pmc-xml-helpers.js.map +1 -1
- package/dist/services/ncbi/response-handler.d.ts +3 -9
- package/dist/services/ncbi/response-handler.d.ts.map +1 -1
- package/dist/services/ncbi/response-handler.js +6 -29
- package/dist/services/ncbi/response-handler.js.map +1 -1
- package/dist/services/ncbi/types.d.ts +82 -0
- package/dist/services/ncbi/types.d.ts.map +1 -1
- package/dist/services/openalex/api-client.d.ts +13 -4
- package/dist/services/openalex/api-client.d.ts.map +1 -1
- package/dist/services/openalex/api-client.js +19 -8
- package/dist/services/openalex/api-client.js.map +1 -1
- package/dist/services/openalex/openalex-service.d.ts +34 -17
- package/dist/services/openalex/openalex-service.d.ts.map +1 -1
- package/dist/services/openalex/openalex-service.js +119 -35
- package/dist/services/openalex/openalex-service.js.map +1 -1
- package/dist/services/openalex/types.d.ts +34 -1
- package/dist/services/openalex/types.d.ts.map +1 -1
- package/dist/services/openalex/types.js +20 -0
- package/dist/services/openalex/types.js.map +1 -1
- package/package.json +1 -1
- package/server.json +3 -3
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
8
8
|
import { fetchWithTimeout, logger, requestContextService } from '@cyanheads/mcp-ts-core/utils';
|
|
9
9
|
import { recoveryFor } from '../error-contracts.js';
|
|
10
|
-
import { OPENALEX_API_BASE } from './types.js';
|
|
10
|
+
import { OPENALEX_API_BASE, OPENALEX_MAX_PAGE_SIZE, } from './types.js';
|
|
11
11
|
const USER_AGENT = 'pubmed-mcp-server (+https://github.com/cyanheads/pubmed-mcp-server)';
|
|
12
12
|
/**
|
|
13
13
|
* Low-level HTTP client for OpenAlex. Single-attempt — retries upstream.
|
|
@@ -72,7 +72,12 @@ export class OpenAlexApiClient {
|
|
|
72
72
|
/**
|
|
73
73
|
* Batch-resolve a list of OpenAlex work IDs to their PMIDs.
|
|
74
74
|
* Uses the filter=openalex:W1|W2|... endpoint with select=id,ids.
|
|
75
|
-
*
|
|
75
|
+
*
|
|
76
|
+
* One call resolves one batch: the OR-filter accepts at most
|
|
77
|
+
* `OPENALEX_MAX_FILTER_VALUES` values and the endpoint serves at most
|
|
78
|
+
* `OPENALEX_MAX_PAGE_SIZE` rows, so splitting a longer list is the caller's
|
|
79
|
+
* job (`OpenAlexService`). Results arrive in OpenAlex's order, not the
|
|
80
|
+
* requested one.
|
|
76
81
|
*/
|
|
77
82
|
resolveOaIdsToPmids(oaIds, signal) {
|
|
78
83
|
if (oaIds.length === 0)
|
|
@@ -82,7 +87,7 @@ export class OpenAlexApiClient {
|
|
|
82
87
|
const params = new URLSearchParams({
|
|
83
88
|
filter: `openalex:${bareIds.join('|')}`,
|
|
84
89
|
select: 'id,ids',
|
|
85
|
-
per_page: String(Math.min(oaIds.length,
|
|
90
|
+
per_page: String(Math.min(oaIds.length, OPENALEX_MAX_PAGE_SIZE)),
|
|
86
91
|
});
|
|
87
92
|
if (this.config.email)
|
|
88
93
|
params.set('mailto', this.config.email);
|
|
@@ -90,10 +95,14 @@ export class OpenAlexApiClient {
|
|
|
90
95
|
return this.fetchWorksList(url, 'OpenAlexResolveOaIds', signal);
|
|
91
96
|
}
|
|
92
97
|
/**
|
|
93
|
-
* Fetch works that cite a given OpenAlex work ID (the cited_by
|
|
94
|
-
* Returns up to `perPage` records
|
|
98
|
+
* Fetch one page of works that cite a given OpenAlex work ID (the cited_by
|
|
99
|
+
* relationship). Returns up to `perPage` records plus OpenAlex's own total.
|
|
100
|
+
*
|
|
101
|
+
* `page` is 1-based. Basic paging is bounded by `page × per_page ≤ 10,000`;
|
|
102
|
+
* past that OpenAlex rejects the request and requires cursor paging, so the
|
|
103
|
+
* caller's page cap must keep the walk inside that ceiling.
|
|
95
104
|
*/
|
|
96
|
-
async getCitedBy(oaId, perPage, signal) {
|
|
105
|
+
async getCitedBy(oaId, perPage, page, signal) {
|
|
97
106
|
// Strip full URL prefix if needed
|
|
98
107
|
const bareId = oaId.startsWith('https://openalex.org/')
|
|
99
108
|
? oaId.slice('https://openalex.org/'.length)
|
|
@@ -101,14 +110,15 @@ export class OpenAlexApiClient {
|
|
|
101
110
|
const params = new URLSearchParams({
|
|
102
111
|
filter: `cites:${bareId}`,
|
|
103
112
|
select: 'id,ids',
|
|
104
|
-
per_page: String(Math.min(perPage,
|
|
113
|
+
per_page: String(Math.min(perPage, OPENALEX_MAX_PAGE_SIZE)),
|
|
114
|
+
page: String(page),
|
|
105
115
|
});
|
|
106
116
|
if (this.config.email)
|
|
107
117
|
params.set('mailto', this.config.email);
|
|
108
118
|
const url = `${OPENALEX_API_BASE}/works?${params.toString()}`;
|
|
109
119
|
const ctx = requestContextService.createRequestContext({
|
|
110
120
|
operation: 'OpenAlexGetCitedBy',
|
|
111
|
-
additionalContext: { oaId: bareId },
|
|
121
|
+
additionalContext: { oaId: bareId, page },
|
|
112
122
|
});
|
|
113
123
|
let response;
|
|
114
124
|
try {
|
|
@@ -135,6 +145,7 @@ export class OpenAlexApiClient {
|
|
|
135
145
|
operation: 'OpenAlexGetCitedByDone',
|
|
136
146
|
additionalContext: {
|
|
137
147
|
oaId: bareId,
|
|
148
|
+
page,
|
|
138
149
|
totalCount: parsed.meta?.count,
|
|
139
150
|
resultCount: parsed.results?.length,
|
|
140
151
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/openalex/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE/F,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../../src/services/openalex/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,13 +5,20 @@
|
|
|
5
5
|
* - `citedBy(pmid, n)`: cites:W<id> filter → PMIDs (mirrors pubmed_pubmed_citedin)
|
|
6
6
|
* - `references(pmid, n)`: referenced_works → PMIDs (mirrors pubmed_pubmed_refs)
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Each method returns at least `n` PubMed-addressable rows when OpenAlex has
|
|
9
|
+
* them: `citedBy` walks pages of the `cites:` filter, and the other two resolve
|
|
10
|
+
* the work's ID list in batches, both stopping once the window is covered,
|
|
11
|
+
* upstream runs out, or a cap is reached. Records with no PMID are dropped —
|
|
12
|
+
* never minted — and counted, so callers can disclose the shortfall instead of
|
|
13
|
+
* serving an unexplained empty window.
|
|
14
|
+
*
|
|
9
15
|
* Uses the NCBI_ADMIN_EMAIL config (adminEmail) as the OpenAlex polite-pool
|
|
10
16
|
* `mailto=` parameter when set; omits it when unset.
|
|
11
17
|
*
|
|
12
18
|
* @module src/services/openalex/openalex-service
|
|
13
19
|
*/
|
|
14
20
|
import { OpenAlexApiClient } from './api-client.js';
|
|
21
|
+
import { type OpenAlexRelatedResult } from './types.js';
|
|
15
22
|
/** Service facade over the OpenAlex API for the find-related provider chain. */
|
|
16
23
|
export declare class OpenAlexService {
|
|
17
24
|
private readonly client;
|
|
@@ -20,33 +27,43 @@ export declare class OpenAlexService {
|
|
|
20
27
|
/**
|
|
21
28
|
* Find works with similar content to the given PMID via OpenAlex `related_works`.
|
|
22
29
|
* Returns PMIDs only; drops any record with no PMID.
|
|
23
|
-
* `n` is the
|
|
30
|
+
* `n` is the size of the window the caller needs covered.
|
|
24
31
|
*/
|
|
25
|
-
similar(pmid: string, n: number, signal?: AbortSignal): Promise<
|
|
26
|
-
pmids: string[];
|
|
27
|
-
totalCount: number;
|
|
28
|
-
}>;
|
|
32
|
+
similar(pmid: string, n: number, signal?: AbortSignal): Promise<OpenAlexRelatedResult>;
|
|
29
33
|
/**
|
|
30
34
|
* Find works that cite the given PMID via OpenAlex `cites:W<id>` filter.
|
|
31
35
|
* Returns PMIDs only; drops any record with no PMID.
|
|
36
|
+
*
|
|
37
|
+
* Pages are all one size and are pulled until the filtered array covers `n`,
|
|
38
|
+
* upstream runs out, or `OPENALEX_MAX_UPSTREAM_REQUESTS` is hit — the rows OpenAlex
|
|
39
|
+
* serves count every citing work, but only those with a PubMed PMID are
|
|
40
|
+
* addressable, so a single page sized on the raw window can under-fill it.
|
|
32
41
|
*/
|
|
33
|
-
citedBy(pmid: string, n: number, signal?: AbortSignal): Promise<
|
|
34
|
-
pmids: string[];
|
|
35
|
-
totalCount: number;
|
|
36
|
-
}>;
|
|
42
|
+
citedBy(pmid: string, n: number, signal?: AbortSignal): Promise<OpenAlexRelatedResult>;
|
|
37
43
|
/**
|
|
38
44
|
* Find works referenced by the given PMID via OpenAlex `referenced_works`.
|
|
39
45
|
* Returns PMIDs only; drops any record with no PMID.
|
|
40
46
|
*/
|
|
41
|
-
references(pmid: string, n: number, signal?: AbortSignal): Promise<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
references(pmid: string, n: number, signal?: AbortSignal): Promise<OpenAlexRelatedResult>;
|
|
48
|
+
/** Look up the source work by PMID; `null` when OpenAlex doesn't index it. */
|
|
49
|
+
private fetchWork;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve an OpenAlex ID list (a work's references or related works) to PMIDs,
|
|
52
|
+
* in batches, until the window is covered, the list is exhausted, or the
|
|
53
|
+
* request cap is reached. The work record carries the whole list, so this
|
|
54
|
+
* paginates resolution rather than an upstream query.
|
|
55
|
+
*
|
|
56
|
+
* Each batch's rows are re-ordered to the requested IDs, so the result follows
|
|
57
|
+
* the source list — a window at any offset stays stable across calls, whatever
|
|
58
|
+
* order OpenAlex answers a batch in.
|
|
59
|
+
*/
|
|
60
|
+
private resolveIdsToPmids;
|
|
45
61
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
62
|
+
* Shape a walk's outcome. Exhausting the fetchable set makes the addressable
|
|
63
|
+
* count exact; short of that only OpenAlex's own total is known, and a window
|
|
64
|
+
* the walk never reached is flagged rather than served as a silent empty.
|
|
48
65
|
*/
|
|
49
|
-
private
|
|
66
|
+
private finish;
|
|
50
67
|
/** Retry wrapper for transient errors, mirroring the EPMC service pattern. */
|
|
51
68
|
private withRetry;
|
|
52
69
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openalex-service.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/openalex-service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openalex-service.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/openalex-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAIL,KAAK,qBAAqB,EAE3B,MAAM,YAAY,CAAC;AAmFpB,gFAAgF;AAChF,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAF7B,YACmB,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,MAAM,EACjC;IAEJ;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAW3F;IAED;;;;;;;;OAQG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA4B3F;IAED;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAW9F;IAED,8EAA8E;IAC9E,OAAO,CAAC,SAAS;IAQjB;;;;;;;;;OASG;YACW,iBAAiB;IAiC/B;;;;OAIG;IACH,OAAO,CAAC,MAAM;IAcd,8EAA8E;YAChE,SAAS;CAmDxB;AAMD,yEAAyE;AACzE,wBAAgB,mBAAmB,IAAI,IAAI,CAmB1C;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,eAAe,CAIpD;AAED,kFAAkF;AAClF,wBAAgB,0BAA0B,IAAI,eAAe,GAAG,SAAS,CAExE"}
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
* - `citedBy(pmid, n)`: cites:W<id> filter → PMIDs (mirrors pubmed_pubmed_citedin)
|
|
6
6
|
* - `references(pmid, n)`: referenced_works → PMIDs (mirrors pubmed_pubmed_refs)
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Each method returns at least `n` PubMed-addressable rows when OpenAlex has
|
|
9
|
+
* them: `citedBy` walks pages of the `cites:` filter, and the other two resolve
|
|
10
|
+
* the work's ID list in batches, both stopping once the window is covered,
|
|
11
|
+
* upstream runs out, or a cap is reached. Records with no PMID are dropped —
|
|
12
|
+
* never minted — and counted, so callers can disclose the shortfall instead of
|
|
13
|
+
* serving an unexplained empty window.
|
|
14
|
+
*
|
|
9
15
|
* Uses the NCBI_ADMIN_EMAIL config (adminEmail) as the OpenAlex polite-pool
|
|
10
16
|
* `mailto=` parameter when set; omits it when unset.
|
|
11
17
|
*
|
|
@@ -17,6 +23,7 @@ import { getServerConfig } from '../../config/server-config.js';
|
|
|
17
23
|
import { recoveryFor } from '../error-contracts.js';
|
|
18
24
|
import { isTransient } from '../retry-policy.js';
|
|
19
25
|
import { OpenAlexApiClient } from './api-client.js';
|
|
26
|
+
import { OPENALEX_MAX_FILTER_VALUES, OPENALEX_MAX_PAGE_SIZE, OPENALEX_MAX_UPSTREAM_REQUESTS, } from './types.js';
|
|
20
27
|
/** Transient codes eligible for retry. */
|
|
21
28
|
const RETRYABLE_CODES = new Set([
|
|
22
29
|
JsonRpcErrorCode.ServiceUnavailable,
|
|
@@ -24,6 +31,10 @@ const RETRYABLE_CODES = new Set([
|
|
|
24
31
|
JsonRpcErrorCode.RateLimited,
|
|
25
32
|
]);
|
|
26
33
|
const MAX_BACKOFF_MS = 30_000;
|
|
34
|
+
/** Strip the `https://openalex.org/` prefix an OA ID may carry. */
|
|
35
|
+
function bareOaId(id) {
|
|
36
|
+
return id.startsWith('https://openalex.org/') ? id.slice('https://openalex.org/'.length) : id;
|
|
37
|
+
}
|
|
27
38
|
function abortableSleep(ms, signal) {
|
|
28
39
|
if (!signal)
|
|
29
40
|
return new Promise((r) => setTimeout(r, ms));
|
|
@@ -54,6 +65,44 @@ function extractPmid(work) {
|
|
|
54
65
|
const match = /(\d+)\s*$/.exec(raw);
|
|
55
66
|
return match?.[1] ?? null;
|
|
56
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Nothing to page through: no source work, or an empty ID list. Built per call
|
|
70
|
+
* rather than shared — the `pmids` array escapes to the caller by identity, and
|
|
71
|
+
* one module-level instance would hand every empty answer the same array.
|
|
72
|
+
*/
|
|
73
|
+
function emptyResult() {
|
|
74
|
+
return { pmids: [], totalCount: 0, droppedNoPmid: 0, reachCapped: false };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Accumulates PubMed-addressable rows across pages or batches: dedupes, drops
|
|
78
|
+
* the source work, and counts the rows that carry no PMID. Kept as one object
|
|
79
|
+
* so a multi-request walk cannot reset the `seen` set between requests and
|
|
80
|
+
* re-emit a PMID an earlier page already returned.
|
|
81
|
+
*/
|
|
82
|
+
class PmidCollector {
|
|
83
|
+
excludePmid;
|
|
84
|
+
pmids = [];
|
|
85
|
+
droppedNoPmid = 0;
|
|
86
|
+
seen = new Set();
|
|
87
|
+
constructor(excludePmid) {
|
|
88
|
+
this.excludePmid = excludePmid;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Add one candidate row. `undefined` means OpenAlex returned nothing for a
|
|
92
|
+
* requested ID — unaddressable in PubMed exactly like a row with no PMID.
|
|
93
|
+
*/
|
|
94
|
+
add(work) {
|
|
95
|
+
const pmid = work ? extractPmid(work) : null;
|
|
96
|
+
if (!pmid) {
|
|
97
|
+
this.droppedNoPmid++;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (pmid === this.excludePmid || this.seen.has(pmid))
|
|
101
|
+
return;
|
|
102
|
+
this.seen.add(pmid);
|
|
103
|
+
this.pmids.push(pmid);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
57
106
|
/** Service facade over the OpenAlex API for the find-related provider chain. */
|
|
58
107
|
export class OpenAlexService {
|
|
59
108
|
client;
|
|
@@ -65,61 +114,96 @@ export class OpenAlexService {
|
|
|
65
114
|
/**
|
|
66
115
|
* Find works with similar content to the given PMID via OpenAlex `related_works`.
|
|
67
116
|
* Returns PMIDs only; drops any record with no PMID.
|
|
68
|
-
* `n` is the
|
|
117
|
+
* `n` is the size of the window the caller needs covered.
|
|
69
118
|
*/
|
|
70
119
|
async similar(pmid, n, signal) {
|
|
71
|
-
const work = await this.
|
|
120
|
+
const work = await this.fetchWork(pmid, signal);
|
|
72
121
|
if (!work)
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
if (relatedIds.length === 0)
|
|
76
|
-
return { pmids: [], totalCount: 0 };
|
|
77
|
-
const resolved = await this.withRetry(() => this.client.resolveOaIdsToPmids(relatedIds, signal), `resolveRelatedWorks(${pmid})`, signal);
|
|
78
|
-
const pmids = this.extractPmids(resolved, pmid).slice(0, n);
|
|
79
|
-
return { pmids, totalCount: work.related_works?.length ?? 0 };
|
|
122
|
+
return emptyResult();
|
|
123
|
+
return this.resolveIdsToPmids(pmid, work.related_works ?? [], n, `resolveRelatedWorks(${pmid})`, signal);
|
|
80
124
|
}
|
|
81
125
|
/**
|
|
82
126
|
* Find works that cite the given PMID via OpenAlex `cites:W<id>` filter.
|
|
83
127
|
* Returns PMIDs only; drops any record with no PMID.
|
|
128
|
+
*
|
|
129
|
+
* Pages are all one size and are pulled until the filtered array covers `n`,
|
|
130
|
+
* upstream runs out, or `OPENALEX_MAX_UPSTREAM_REQUESTS` is hit — the rows OpenAlex
|
|
131
|
+
* serves count every citing work, but only those with a PubMed PMID are
|
|
132
|
+
* addressable, so a single page sized on the raw window can under-fill it.
|
|
84
133
|
*/
|
|
85
134
|
async citedBy(pmid, n, signal) {
|
|
86
|
-
const work = await this.
|
|
135
|
+
const work = await this.fetchWork(pmid, signal);
|
|
87
136
|
if (!work)
|
|
88
|
-
return
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
137
|
+
return emptyResult();
|
|
138
|
+
const perPage = Math.min(n, OPENALEX_MAX_PAGE_SIZE);
|
|
139
|
+
const collector = new PmidCollector(pmid);
|
|
140
|
+
let upstreamCount = 0;
|
|
141
|
+
let exhausted = false;
|
|
142
|
+
for (let page = 1; page <= OPENALEX_MAX_UPSTREAM_REQUESTS && collector.pmids.length < n; page++) {
|
|
143
|
+
const { works, totalCount } = await this.withRetry(() => this.client.getCitedBy(work.id, perPage, page, signal), `getCitedBy(${work.id}, page ${page})`, signal);
|
|
144
|
+
upstreamCount = totalCount;
|
|
145
|
+
for (const w of works)
|
|
146
|
+
collector.add(w);
|
|
147
|
+
if (works.length < perPage || page * perPage >= totalCount) {
|
|
148
|
+
exhausted = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return this.finish(collector, exhausted, upstreamCount, n);
|
|
92
153
|
}
|
|
93
154
|
/**
|
|
94
155
|
* Find works referenced by the given PMID via OpenAlex `referenced_works`.
|
|
95
156
|
* Returns PMIDs only; drops any record with no PMID.
|
|
96
157
|
*/
|
|
97
158
|
async references(pmid, n, signal) {
|
|
98
|
-
const work = await this.
|
|
159
|
+
const work = await this.fetchWork(pmid, signal);
|
|
99
160
|
if (!work)
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return { pmids, totalCount: work.referenced_works?.length ?? 0 };
|
|
161
|
+
return emptyResult();
|
|
162
|
+
return this.resolveIdsToPmids(pmid, work.referenced_works ?? [], n, `resolveReferencedWorks(${pmid})`, signal);
|
|
163
|
+
}
|
|
164
|
+
/** Look up the source work by PMID; `null` when OpenAlex doesn't index it. */
|
|
165
|
+
fetchWork(pmid, signal) {
|
|
166
|
+
return this.withRetry(() => this.client.getWorkByPmid(pmid, signal), `getWorkByPmid(${pmid})`, signal);
|
|
107
167
|
}
|
|
108
168
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
169
|
+
* Resolve an OpenAlex ID list (a work's references or related works) to PMIDs,
|
|
170
|
+
* in batches, until the window is covered, the list is exhausted, or the
|
|
171
|
+
* request cap is reached. The work record carries the whole list, so this
|
|
172
|
+
* paginates resolution rather than an upstream query.
|
|
173
|
+
*
|
|
174
|
+
* Each batch's rows are re-ordered to the requested IDs, so the result follows
|
|
175
|
+
* the source list — a window at any offset stays stable across calls, whatever
|
|
176
|
+
* order OpenAlex answers a batch in.
|
|
111
177
|
*/
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
178
|
+
async resolveIdsToPmids(pmid, oaIds, n, label, signal) {
|
|
179
|
+
if (oaIds.length === 0)
|
|
180
|
+
return emptyResult();
|
|
181
|
+
const collector = new PmidCollector(pmid);
|
|
182
|
+
let consumed = 0;
|
|
183
|
+
for (let batch = 0; batch < OPENALEX_MAX_UPSTREAM_REQUESTS &&
|
|
184
|
+
consumed < oaIds.length &&
|
|
185
|
+
collector.pmids.length < n; batch++) {
|
|
186
|
+
const ids = oaIds.slice(consumed, consumed + OPENALEX_MAX_FILTER_VALUES);
|
|
187
|
+
const resolved = await this.withRetry(() => this.client.resolveOaIdsToPmids(ids, signal), label, signal);
|
|
188
|
+
const byId = new Map(resolved.map((w) => [bareOaId(w.id), w]));
|
|
189
|
+
for (const id of ids)
|
|
190
|
+
collector.add(byId.get(bareOaId(id)));
|
|
191
|
+
consumed += ids.length;
|
|
121
192
|
}
|
|
122
|
-
return
|
|
193
|
+
return this.finish(collector, consumed >= oaIds.length, oaIds.length, n);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Shape a walk's outcome. Exhausting the fetchable set makes the addressable
|
|
197
|
+
* count exact; short of that only OpenAlex's own total is known, and a window
|
|
198
|
+
* the walk never reached is flagged rather than served as a silent empty.
|
|
199
|
+
*/
|
|
200
|
+
finish(collector, exhausted, upstreamCount, n) {
|
|
201
|
+
return {
|
|
202
|
+
pmids: collector.pmids,
|
|
203
|
+
totalCount: exhausted ? collector.pmids.length : upstreamCount,
|
|
204
|
+
droppedNoPmid: collector.droppedNoPmid,
|
|
205
|
+
reachCapped: !exhausted && collector.pmids.length < n,
|
|
206
|
+
};
|
|
123
207
|
}
|
|
124
208
|
/** Retry wrapper for transient errors, mirroring the EPMC service pattern. */
|
|
125
209
|
async withRetry(execute, label, signal) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openalex-service.js","sourceRoot":"","sources":["../../../src/services/openalex/openalex-service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"openalex-service.js","sourceRoot":"","sources":["../../../src/services/openalex/openalex-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,26 @@
|
|
|
12
12
|
*/
|
|
13
13
|
/** Base URL for the OpenAlex public API. */
|
|
14
14
|
export declare const OPENALEX_API_BASE = "https://api.openalex.org";
|
|
15
|
+
/**
|
|
16
|
+
* Rows OpenAlex serves per request. The documented `per_page` range is 1-100,
|
|
17
|
+
* and basic paging is additionally bounded by `page × per_page ≤ 10,000` —
|
|
18
|
+
* beyond that the API returns HTTP 400 and demands cursor paging.
|
|
19
|
+
*/
|
|
20
|
+
export declare const OPENALEX_MAX_PAGE_SIZE = 100;
|
|
21
|
+
/**
|
|
22
|
+
* Values one `filter=openalex:W1|W2|…` OR-clause accepts. A 101st value is
|
|
23
|
+
* rejected with HTTP 400 ("Maximum number of values exceeded"), so a caller
|
|
24
|
+
* resolving a long ID list must split it into batches of this size.
|
|
25
|
+
*/
|
|
26
|
+
export declare const OPENALEX_MAX_FILTER_VALUES = 100;
|
|
27
|
+
/**
|
|
28
|
+
* Upstream requests one service call may spend reaching the window — pages of
|
|
29
|
+
* the `cites:` filter, or ID-resolution batches. Ten pages of 100 reach 1,000
|
|
30
|
+
* upstream rows: past the 50-row `maxResults` ceiling at any realistic offset,
|
|
31
|
+
* inside the `page × per_page ≤ 10,000` basic-paging limit, and ~3s of
|
|
32
|
+
* sequential requests at the ~0.3s a page costs (~0.7s for a 100-ID batch).
|
|
33
|
+
*/
|
|
34
|
+
export declare const OPENALEX_MAX_UPSTREAM_REQUESTS = 10;
|
|
15
35
|
/**
|
|
16
36
|
* Identifier block on an OpenAlex Work. Only `openalex` (the OA ID) is always
|
|
17
37
|
* present; `pmid` is absent for non-PubMed records. We only care about PMIDs —
|
|
@@ -48,9 +68,22 @@ export interface OpenAlexWorksResponse {
|
|
|
48
68
|
results?: OpenAlexWork[];
|
|
49
69
|
[key: string]: unknown;
|
|
50
70
|
}
|
|
51
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Resolved PMID list returned by service methods. `pmids` is indexed in
|
|
73
|
+
* PubMed-addressable rows only — upstream records with no PMID are dropped and
|
|
74
|
+
* counted in `droppedNoPmid`, so a caller can disclose the gap between
|
|
75
|
+
* OpenAlex's own totals and what it can actually return.
|
|
76
|
+
*/
|
|
52
77
|
export interface OpenAlexRelatedResult {
|
|
78
|
+
/** Candidate rows fetched for this request that carry no PubMed PMID. */
|
|
79
|
+
droppedNoPmid: number;
|
|
53
80
|
pmids: string[];
|
|
81
|
+
/** Paging/batching stopped at its cap before the requested window was covered. */
|
|
82
|
+
reachCapped: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Exact PubMed-addressable count once the fetchable set is exhausted;
|
|
85
|
+
* OpenAlex's own upstream count while rows remain unfetched.
|
|
86
|
+
*/
|
|
54
87
|
totalCount: number;
|
|
55
88
|
}
|
|
56
89
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,oEAAoE;AACpE,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,oEAAoE;AACpE,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -12,4 +12,24 @@
|
|
|
12
12
|
*/
|
|
13
13
|
/** Base URL for the OpenAlex public API. */
|
|
14
14
|
export const OPENALEX_API_BASE = 'https://api.openalex.org';
|
|
15
|
+
/**
|
|
16
|
+
* Rows OpenAlex serves per request. The documented `per_page` range is 1-100,
|
|
17
|
+
* and basic paging is additionally bounded by `page × per_page ≤ 10,000` —
|
|
18
|
+
* beyond that the API returns HTTP 400 and demands cursor paging.
|
|
19
|
+
*/
|
|
20
|
+
export const OPENALEX_MAX_PAGE_SIZE = 100;
|
|
21
|
+
/**
|
|
22
|
+
* Values one `filter=openalex:W1|W2|…` OR-clause accepts. A 101st value is
|
|
23
|
+
* rejected with HTTP 400 ("Maximum number of values exceeded"), so a caller
|
|
24
|
+
* resolving a long ID list must split it into batches of this size.
|
|
25
|
+
*/
|
|
26
|
+
export const OPENALEX_MAX_FILTER_VALUES = 100;
|
|
27
|
+
/**
|
|
28
|
+
* Upstream requests one service call may spend reaching the window — pages of
|
|
29
|
+
* the `cites:` filter, or ID-resolution batches. Ten pages of 100 reach 1,000
|
|
30
|
+
* upstream rows: past the 50-row `maxResults` ceiling at any realistic offset,
|
|
31
|
+
* inside the `page × per_page ≤ 10,000` basic-paging limit, and ~3s of
|
|
32
|
+
* sequential requests at the ~0.3s a page costs (~0.7s for a 100-ID batch).
|
|
33
|
+
*/
|
|
34
|
+
export const OPENALEX_MAX_UPSTREAM_REQUESTS = 10;
|
|
15
35
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/openalex/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/pubmed-mcp-server",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.10",
|
|
4
4
|
"mcpName": "io.github.cyanheads/pubmed-mcp-server",
|
|
5
5
|
"description": "Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms via MCP. STDIO or Streamable HTTP.",
|
|
6
6
|
"type": "module",
|
package/server.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/pubmed-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.10.
|
|
9
|
+
"version": "2.10.10",
|
|
10
10
|
"remotes": [
|
|
11
11
|
{
|
|
12
12
|
"type": "streamable-http",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
20
20
|
"identifier": "@cyanheads/pubmed-mcp-server",
|
|
21
21
|
"runtimeHint": "bun",
|
|
22
|
-
"version": "2.10.
|
|
22
|
+
"version": "2.10.10",
|
|
23
23
|
"packageArguments": [
|
|
24
24
|
{
|
|
25
25
|
"type": "positional",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
67
67
|
"identifier": "@cyanheads/pubmed-mcp-server",
|
|
68
68
|
"runtimeHint": "bun",
|
|
69
|
-
"version": "2.10.
|
|
69
|
+
"version": "2.10.10",
|
|
70
70
|
"packageArguments": [
|
|
71
71
|
{
|
|
72
72
|
"type": "positional",
|