@cyanheads/pubmed-mcp-server 2.6.12 → 2.7.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/README.md +33 -12
- package/dist/config/server-config.d.ts +5 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +45 -0
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +19 -12
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts +71 -23
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js +671 -141
- package/dist/mcp-server/tools/definitions/fetch-fulltext.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts +87 -0
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js +217 -0
- package/dist/mcp-server/tools/definitions/pubmed-europepmc-search.tool.js.map +1 -0
- package/dist/services/error-contracts.d.ts +18 -1
- package/dist/services/error-contracts.d.ts.map +1 -1
- package/dist/services/error-contracts.js +21 -4
- package/dist/services/error-contracts.js.map +1 -1
- package/dist/services/europe-pmc/api-client.d.ts +47 -0
- package/dist/services/europe-pmc/api-client.d.ts.map +1 -0
- package/dist/services/europe-pmc/api-client.js +123 -0
- package/dist/services/europe-pmc/api-client.js.map +1 -0
- package/dist/services/europe-pmc/europe-pmc-service.d.ts +71 -0
- package/dist/services/europe-pmc/europe-pmc-service.d.ts.map +1 -0
- package/dist/services/europe-pmc/europe-pmc-service.js +243 -0
- package/dist/services/europe-pmc/europe-pmc-service.js.map +1 -0
- package/dist/services/europe-pmc/request-queue.d.ts +33 -0
- package/dist/services/europe-pmc/request-queue.d.ts.map +1 -0
- package/dist/services/europe-pmc/request-queue.js +107 -0
- package/dist/services/europe-pmc/request-queue.js.map +1 -0
- package/dist/services/europe-pmc/types.d.ts +123 -0
- package/dist/services/europe-pmc/types.d.ts.map +1 -0
- package/dist/services/europe-pmc/types.js +16 -0
- package/dist/services/europe-pmc/types.js.map +1 -0
- package/dist/services/ncbi/response-handler.js +1 -1
- package/dist/services/unpaywall/unpaywall-service.d.ts.map +1 -1
- package/dist/services/unpaywall/unpaywall-service.js +14 -13
- package/dist/services/unpaywall/unpaywall-service.js.map +1 -1
- package/package.json +5 -4
- package/server.json +4 -4
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Types for the Europe PMC REST API. Covers the search response
|
|
3
|
+
* shape and the fullTextXML endpoint contract.
|
|
4
|
+
*
|
|
5
|
+
* See https://europepmc.org/RestfulWebService for the full API. We use only
|
|
6
|
+
* the search endpoint (`/webservices/rest/search`) and the fullText endpoint
|
|
7
|
+
* (`/webservices/rest/{id}/fullTextXML`).
|
|
8
|
+
*
|
|
9
|
+
* @module src/services/europe-pmc/types
|
|
10
|
+
*/
|
|
11
|
+
/** Base URL for Europe PMC's RESTful web service. */
|
|
12
|
+
export declare const EUROPEPMC_API_BASE = "https://www.ebi.ac.uk/europepmc/webservices/rest";
|
|
13
|
+
/**
|
|
14
|
+
* Europe PMC source taxonomy. Determines which corpus a record belongs to and
|
|
15
|
+
* what identifier scheme applies. PubMed-derived records carry a `pmid`; PMC
|
|
16
|
+
* preprints carry an EPMC id (`source: PPR`) but no PMID; patents and Agricola
|
|
17
|
+
* records may carry neither.
|
|
18
|
+
*/
|
|
19
|
+
export type EuropePmcSource = 'MED' | 'PMC' | 'PPR' | 'PAT' | 'AGR' | 'CTX' | 'CBA' | 'ETH' | 'HIR';
|
|
20
|
+
/** Allowed sources for `pubmed_europepmc_search` input. */
|
|
21
|
+
export declare const EUROPEPMC_DEFAULT_SOURCES: readonly ["MED", "PMC", "PPR"];
|
|
22
|
+
export declare const EUROPEPMC_ALL_SOURCES: readonly ["MED", "PMC", "PPR", "PAT", "AGR"];
|
|
23
|
+
/**
|
|
24
|
+
* Single record from a Europe PMC search response. Only fields the server
|
|
25
|
+
* actually consumes are typed; the upstream payload carries many more under
|
|
26
|
+
* `resultType=core`. Field optionality reflects EPMC's real-world sparsity —
|
|
27
|
+
* preprints often lack `journalTitle`; patents lack `authorString`.
|
|
28
|
+
*/
|
|
29
|
+
export interface EuropePmcSearchHit {
|
|
30
|
+
abstractText?: string;
|
|
31
|
+
authorString?: string;
|
|
32
|
+
bookOrReportDetails?: unknown;
|
|
33
|
+
citedByCount?: number;
|
|
34
|
+
doi?: string;
|
|
35
|
+
/** ISO date (YYYY-MM-DD) of first index in EPMC. */
|
|
36
|
+
firstIndexDate?: string;
|
|
37
|
+
/** ISO date (YYYY-MM-DD) of publication where known. */
|
|
38
|
+
firstPublicationDate?: string;
|
|
39
|
+
hasBook?: 'Y' | 'N';
|
|
40
|
+
hasPDF?: 'Y' | 'N';
|
|
41
|
+
/** "Y"/"N" — whether EPMC believes a full-text XML is available for this record. */
|
|
42
|
+
hasTextMinedTerms?: 'Y' | 'N';
|
|
43
|
+
/** EPMC's own record identifier — unique within `source`. Always present. */
|
|
44
|
+
id: string;
|
|
45
|
+
inEPMC?: 'Y' | 'N';
|
|
46
|
+
inPMC?: 'Y' | 'N';
|
|
47
|
+
isOpenAccess?: 'Y' | 'N';
|
|
48
|
+
issue?: string;
|
|
49
|
+
journalTitle?: string;
|
|
50
|
+
journalVolume?: string;
|
|
51
|
+
/** Some hits carry their own license. Free-form per EPMC. */
|
|
52
|
+
license?: string;
|
|
53
|
+
pageInfo?: string;
|
|
54
|
+
pmcid?: string;
|
|
55
|
+
pmid?: string;
|
|
56
|
+
pubYear?: string;
|
|
57
|
+
source: EuropePmcSource;
|
|
58
|
+
title?: string;
|
|
59
|
+
[extra: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
/** Wrapper for the `resultList` block in a search response. */
|
|
62
|
+
export interface EuropePmcResultList {
|
|
63
|
+
result?: EuropePmcSearchHit[] | EuropePmcSearchHit;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Top-level shape of a Europe PMC JSON search response. EPMC returns scalar
|
|
67
|
+
* `result` when there's exactly one hit; `request.queryString` echoes the
|
|
68
|
+
* effective query so callers can preview what EPMC actually searched.
|
|
69
|
+
*/
|
|
70
|
+
export interface EuropePmcSearchResponse {
|
|
71
|
+
/** Total matching records across all pages. */
|
|
72
|
+
hitCount?: number;
|
|
73
|
+
/** Cursor token for the next page; absent on the final page. */
|
|
74
|
+
nextCursorMark?: string;
|
|
75
|
+
/** Cursor used for this response, echoed back from the request. */
|
|
76
|
+
request?: {
|
|
77
|
+
queryString?: string;
|
|
78
|
+
resultType?: string;
|
|
79
|
+
cursorMark?: string;
|
|
80
|
+
pageSize?: number;
|
|
81
|
+
sort?: string;
|
|
82
|
+
[extra: string]: unknown;
|
|
83
|
+
};
|
|
84
|
+
resultList?: EuropePmcResultList;
|
|
85
|
+
version?: string;
|
|
86
|
+
[extra: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
/** Normalized search result returned by the service layer. */
|
|
89
|
+
export interface EuropePmcSearchResult {
|
|
90
|
+
/** Cursor used for this response. Returned as-is so callers can echo it. */
|
|
91
|
+
cursorMark?: string;
|
|
92
|
+
hitCount: number;
|
|
93
|
+
hits: EuropePmcSearchHit[];
|
|
94
|
+
/** Cursor for the next page. `undefined` when no more pages remain. */
|
|
95
|
+
nextCursorMark?: string;
|
|
96
|
+
query: string;
|
|
97
|
+
}
|
|
98
|
+
/** Parameters accepted by the EPMC search endpoint. */
|
|
99
|
+
export interface EuropePmcSearchParams {
|
|
100
|
+
/** EPMC's cursor pagination token; start with `*` for the first page. */
|
|
101
|
+
cursorMark?: string;
|
|
102
|
+
/** Max 100 (per EPMC docs). */
|
|
103
|
+
pageSize?: number;
|
|
104
|
+
query: string;
|
|
105
|
+
/** Default `core` — richest payload with abstract, IDs, dates, license. */
|
|
106
|
+
resultType?: 'core' | 'lite' | 'idlist';
|
|
107
|
+
signal?: AbortSignal;
|
|
108
|
+
/** Optional sort spec, e.g. `FIRST_PIDATE desc`. */
|
|
109
|
+
sort?: string;
|
|
110
|
+
/** Allowed sources, comma-joined into the query as `(SRC:"MED" OR ...)`. */
|
|
111
|
+
sources?: readonly EuropePmcSource[];
|
|
112
|
+
}
|
|
113
|
+
/** Outcome of a fullTextXML lookup. */
|
|
114
|
+
export type EuropePmcFullTextResult = {
|
|
115
|
+
kind: 'found';
|
|
116
|
+
xml: string;
|
|
117
|
+
epmcId: string;
|
|
118
|
+
source: EuropePmcSource;
|
|
119
|
+
} | {
|
|
120
|
+
kind: 'not-available';
|
|
121
|
+
reason: string;
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/europe-pmc/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Types for the Europe PMC REST API. Covers the search response
|
|
3
|
+
* shape and the fullTextXML endpoint contract.
|
|
4
|
+
*
|
|
5
|
+
* See https://europepmc.org/RestfulWebService for the full API. We use only
|
|
6
|
+
* the search endpoint (`/webservices/rest/search`) and the fullText endpoint
|
|
7
|
+
* (`/webservices/rest/{id}/fullTextXML`).
|
|
8
|
+
*
|
|
9
|
+
* @module src/services/europe-pmc/types
|
|
10
|
+
*/
|
|
11
|
+
/** Base URL for Europe PMC's RESTful web service. */
|
|
12
|
+
export const EUROPEPMC_API_BASE = 'https://www.ebi.ac.uk/europepmc/webservices/rest';
|
|
13
|
+
/** Allowed sources for `pubmed_europepmc_search` input. */
|
|
14
|
+
export const EUROPEPMC_DEFAULT_SOURCES = ['MED', 'PMC', 'PPR'];
|
|
15
|
+
export const EUROPEPMC_ALL_SOURCES = ['MED', 'PMC', 'PPR', 'PAT', 'AGR'];
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/europe-pmc/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qDAAqD;AACrD,MAAM,CAAC,MAAM,kBAAkB,GAAG,kDAAkD,CAAC;AAUrF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC"}
|
|
@@ -133,7 +133,7 @@ function sanitizeNcbiError(message) {
|
|
|
133
133
|
* E-utilities use `<ERROR>` for response-level failures, while PMC EFetch
|
|
134
134
|
* uses lowercase `<error id="…">` to flag a single unavailable PMCID. The
|
|
135
135
|
* latter is data (a missing ID), not a transport error, so it falls through
|
|
136
|
-
* to the caller which reports it via `
|
|
136
|
+
* to the caller which reports it via the unified `unavailable[]` list.
|
|
137
137
|
*/
|
|
138
138
|
const ERROR_TAG_REGEX = /<ERROR(?:\s[^>]*)?>/;
|
|
139
139
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unpaywall-service.d.ts","sourceRoot":"","sources":["../../../src/services/unpaywall/unpaywall-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAEzB,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM;IAGpC;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"unpaywall-service.d.ts","sourceRoot":"","sources":["../../../src/services/unpaywall/unpaywall-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAEzB,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM;IAGpC;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA2D9E;;;;;OAKG;IACG,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAsBlF,OAAO;CAkDtB;AAiBD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAc3C;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAElE"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* resolution; only genuine service failures (5xx, network, timeout) throw.
|
|
8
8
|
* @module src/services/unpaywall/unpaywall-service
|
|
9
9
|
*/
|
|
10
|
-
import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
10
|
+
import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
11
11
|
import { fetchWithTimeout, httpErrorFromResponse, logger, requestContextService, } from '@cyanheads/mcp-ts-core/utils';
|
|
12
12
|
import { getServerConfig } from '../../config/server-config.js';
|
|
13
13
|
import { recoveryFor } from '../../services/error-contracts.js';
|
|
@@ -35,6 +35,11 @@ export class UnpaywallService {
|
|
|
35
35
|
return { kind: 'no-oa', reason: 'Invalid DOI' };
|
|
36
36
|
const url = `${UNPAYWALL_API_BASE}/${encodeURIComponent(normalized)}?email=${encodeURIComponent(this.email)}`;
|
|
37
37
|
const ctx = requestContextService.createRequestContext({ operation: 'UnpaywallResolve', doi });
|
|
38
|
+
// `fetchWithTimeout` throws on any non-2xx response, so the explicit
|
|
39
|
+
// `response.status === 404` / `=== 422` checks the API actually returns
|
|
40
|
+
// arrive as `McpError(NotFound)` / `McpError(ValidationError)` here, not as
|
|
41
|
+
// a response object. Translate those into the documented `no-oa` outcomes
|
|
42
|
+
// before falling back to the service-unavailable wrap.
|
|
38
43
|
let response;
|
|
39
44
|
try {
|
|
40
45
|
response = await fetchWithTimeout(url, this.timeoutMs, ctx, {
|
|
@@ -43,6 +48,14 @@ export class UnpaywallService {
|
|
|
43
48
|
});
|
|
44
49
|
}
|
|
45
50
|
catch (error) {
|
|
51
|
+
if (error instanceof McpError) {
|
|
52
|
+
if (error.code === JsonRpcErrorCode.NotFound) {
|
|
53
|
+
return { kind: 'no-oa', reason: 'DOI unknown to Unpaywall' };
|
|
54
|
+
}
|
|
55
|
+
if (error.code === JsonRpcErrorCode.ValidationError) {
|
|
56
|
+
return { kind: 'no-oa', reason: 'Invalid DOI format' };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
46
59
|
const msg = error instanceof Error ? error.message : String(error);
|
|
47
60
|
throw serviceUnavailable(`Unpaywall request failed: ${msg}`, {
|
|
48
61
|
reason: 'unpaywall_unreachable',
|
|
@@ -50,18 +63,6 @@ export class UnpaywallService {
|
|
|
50
63
|
...recoveryFor('unpaywall_unreachable'),
|
|
51
64
|
}, { cause: error });
|
|
52
65
|
}
|
|
53
|
-
if (response.status === 404)
|
|
54
|
-
return { kind: 'no-oa', reason: 'DOI unknown to Unpaywall' };
|
|
55
|
-
if (response.status === 422)
|
|
56
|
-
return { kind: 'no-oa', reason: 'Invalid DOI format' };
|
|
57
|
-
if (!response.ok) {
|
|
58
|
-
const body = await response.text().catch(() => '');
|
|
59
|
-
throw await httpErrorFromResponse(response, {
|
|
60
|
-
service: 'Unpaywall',
|
|
61
|
-
captureBody: false,
|
|
62
|
-
data: { doi: normalized, body: body.substring(0, 300) },
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
66
|
const data = (await response.json());
|
|
66
67
|
if (!data.is_oa)
|
|
67
68
|
return { kind: 'no-oa', reason: 'No open-access copy indexed' };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unpaywall-service.js","sourceRoot":"","sources":["../../../src/services/unpaywall/unpaywall-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"unpaywall-service.js","sourceRoot":"","sources":["../../../src/services/unpaywall/unpaywall-service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/pubmed-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"mcpName": "io.github.cyanheads/pubmed-mcp-server",
|
|
5
|
-
"description": "
|
|
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",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"e-utilities",
|
|
41
41
|
"open-access",
|
|
42
42
|
"full-text",
|
|
43
|
-
"unpaywall"
|
|
43
|
+
"unpaywall",
|
|
44
|
+
"europe-pmc"
|
|
44
45
|
],
|
|
45
46
|
"repository": {
|
|
46
47
|
"type": "git",
|
|
@@ -81,7 +82,7 @@
|
|
|
81
82
|
},
|
|
82
83
|
"devDependencies": {
|
|
83
84
|
"@biomejs/biome": "^2.4.15",
|
|
84
|
-
"@types/node": "^25.
|
|
85
|
+
"@types/node": "^25.9.0",
|
|
85
86
|
"@types/sanitize-html": "^2.16.1",
|
|
86
87
|
"@vitest/coverage-istanbul": "^4.1.6",
|
|
87
88
|
"depcheck": "^1.4.7",
|
package/server.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.cyanheads/pubmed-mcp-server",
|
|
4
|
-
"description": "Search PubMed, fetch articles and full text,
|
|
4
|
+
"description": "Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/cyanheads/pubmed-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.7.0",
|
|
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.
|
|
22
|
+
"version": "2.7.0",
|
|
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.
|
|
69
|
+
"version": "2.7.0",
|
|
70
70
|
"packageArguments": [
|
|
71
71
|
{
|
|
72
72
|
"type": "positional",
|