ictrp-mcp-server 0.1.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/CHANGELOG.md +65 -0
- package/LICENSE +37 -0
- package/README.md +208 -0
- package/README_ZH.md +189 -0
- package/dist/cli/setup-cli.d.ts +14 -0
- package/dist/cli/setup-cli.js +230 -0
- package/dist/cli/setup-cli.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +477 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts +99 -0
- package/dist/runtime/bootstrap.js +350 -0
- package/dist/runtime/bootstrap.js.map +1 -0
- package/dist/runtime/env-probe.d.ts +108 -0
- package/dist/runtime/env-probe.js +479 -0
- package/dist/runtime/env-probe.js.map +1 -0
- package/dist/runtime/sidecar-client.d.ts +50 -0
- package/dist/runtime/sidecar-client.js +120 -0
- package/dist/runtime/sidecar-client.js.map +1 -0
- package/dist/runtime/supervisor.d.ts +47 -0
- package/dist/runtime/supervisor.js +248 -0
- package/dist/runtime/supervisor.js.map +1 -0
- package/package.json +60 -0
- package/sidecar/ictrp_sidecar.py +602 -0
- package/sidecar/vendor/ictrp_mcp/__init__.py +3 -0
- package/sidecar/vendor/ictrp_mcp/cache/__init__.py +0 -0
- package/sidecar/vendor/ictrp_mcp/cache/store.py +313 -0
- package/sidecar/vendor/ictrp_mcp/data/__init__.py +0 -0
- package/sidecar/vendor/ictrp_mcp/data/columns.py +108 -0
- package/sidecar/vendor/ictrp_mcp/data/jsonio.py +213 -0
- package/sidecar/vendor/ictrp_mcp/data/normalize.py +348 -0
- package/sidecar/vendor/ictrp_mcp/data/query.py +307 -0
- package/sidecar/vendor/ictrp_mcp/errors.py +123 -0
- package/sidecar/vendor/ictrp_mcp/ictrp/__init__.py +0 -0
- package/sidecar/vendor/ictrp_mcp/ictrp/export_guard.py +269 -0
- package/sidecar/vendor/ictrp_mcp/ictrp/htmlstate.py +143 -0
- package/sidecar/vendor/ictrp_mcp/ictrp/session.py +245 -0
- package/sidecar/vendor/ictrp_mcp/offline.py +133 -0
- package/sidecar/vendor/ictrp_mcp/provenance.py +182 -0
- package/sidecar/vendor/ictrp_mcp/server.py +368 -0
- package/sidecar/vendor/ictrp_mcp/tools.py +712 -0
- package/sidecar/vendor/pyproject.toml +25 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP server for the WHO ICTRP, over stdio.
|
|
4
|
+
*
|
|
5
|
+
* This file is the MCP surface and nothing else. It validates arguments,
|
|
6
|
+
* ensures the Python sidecar is up, forwards the call, and shapes the result.
|
|
7
|
+
* All domain logic -- the postback chain, the incompleteness accounting, the
|
|
8
|
+
* local query engine -- lives in Python, where it is tested.
|
|
9
|
+
*
|
|
10
|
+
* The one rule that shapes every handler here: **a failure must never be
|
|
11
|
+
* presented as a successful empty result.** ICTRP's export silently omits
|
|
12
|
+
* records, so an empty answer and a failed answer are dangerously easy to
|
|
13
|
+
* confuse. Failures are therefore reported with `isError: true` and a
|
|
14
|
+
* machine-readable `error_code`, and the incompleteness notice is attached to
|
|
15
|
+
* every successful result that carries rows.
|
|
16
|
+
*/
|
|
17
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
18
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import { getEnvReport } from "./runtime/env-probe.js";
|
|
21
|
+
import { callSidecar, pingSidecar, SEARCH_TIMEOUT_MS, toErrorText, } from "./runtime/sidecar-client.js";
|
|
22
|
+
import { ensureSidecar, installShutdownHooks, sidecarLogTail } from "./runtime/supervisor.js";
|
|
23
|
+
const SERVER_NAME = "ictrp-mcp-server";
|
|
24
|
+
const SERVER_VERSION = "0.1.0";
|
|
25
|
+
/**
|
|
26
|
+
* Attached to every payload that returns rows.
|
|
27
|
+
*
|
|
28
|
+
* This is not boilerplate. A model that receives 12 rows and no warning will
|
|
29
|
+
* say "there are 12 trials"; the measured shortfall against the portal's own
|
|
30
|
+
* counts reaches 29% for some queries, so that sentence can be badly wrong.
|
|
31
|
+
*/
|
|
32
|
+
const INCOMPLETENESS_NOTICE = "Counts describe retrieved rows only. The ICTRP CSV export is known to omit records " +
|
|
33
|
+
"the portal itself reports as matches, so absence is not evidence of nonexistence.";
|
|
34
|
+
const ATTRIBUTION_NOTICE = "Source: WHO ICTRP. Attribute as 'WHO ICTRP' and display the processing date. " +
|
|
35
|
+
"Chinese-registry records must be attributed as 'ChiCTR via WHO ICTRP'.";
|
|
36
|
+
const FILTER_SCHEMA = {
|
|
37
|
+
type: "array",
|
|
38
|
+
description: "Filters applied with AND. Multiple filters on the same field are also ANDed.",
|
|
39
|
+
items: {
|
|
40
|
+
type: "object",
|
|
41
|
+
required: ["field", "op"],
|
|
42
|
+
properties: {
|
|
43
|
+
field: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Normalized field name, e.g. source_register, phase_code, recruitment_status_normalized, " +
|
|
46
|
+
"registration_date, target_size_total, countries, condition.",
|
|
47
|
+
},
|
|
48
|
+
op: {
|
|
49
|
+
type: "string",
|
|
50
|
+
enum: [
|
|
51
|
+
"eq",
|
|
52
|
+
"ne",
|
|
53
|
+
"contains",
|
|
54
|
+
"not_contains",
|
|
55
|
+
"in",
|
|
56
|
+
"not_in",
|
|
57
|
+
"gt",
|
|
58
|
+
"gte",
|
|
59
|
+
"lt",
|
|
60
|
+
"lte",
|
|
61
|
+
"exists",
|
|
62
|
+
"not_exists",
|
|
63
|
+
"is_null",
|
|
64
|
+
"is_not_null",
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
value: {
|
|
68
|
+
description: "Comparison value. For `in`/`not_in` supply an array.",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const TOOLS = [
|
|
74
|
+
{
|
|
75
|
+
name: "ictrp_search",
|
|
76
|
+
description: "Search the WHO ICTRP by keyword and materialize the entire result set locally. " +
|
|
77
|
+
"This is the only expensive operation: it drives a full CSV export (tens of megabytes) " +
|
|
78
|
+
"and returns a page plus a set_id. Every later refinement works on that set without " +
|
|
79
|
+
"touching the network again. IMPORTANT: the returned count is rows retrieved, NOT the " +
|
|
80
|
+
"number of matching trials -- the export is known to omit records, so never present the " +
|
|
81
|
+
"count as a total and never treat absence from the result as evidence a trial does not exist.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
keyword: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Free-text query, e.g. 'pancreatic cancer', 'KRAS', 'IBI343'.",
|
|
88
|
+
},
|
|
89
|
+
limit: { type: "number", description: "Rows to return in this page. Default 25." },
|
|
90
|
+
offset: { type: "number", description: "Rows to skip. Default 0." },
|
|
91
|
+
refresh: {
|
|
92
|
+
type: "boolean",
|
|
93
|
+
description: "Ignore any cached export for this exact keyword and re-fetch. Default false.",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ["keyword"],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "ictrp_filter",
|
|
101
|
+
description: "Filter, sort and page a materialized set. No upstream request. " +
|
|
102
|
+
"Filter on normalized fields (phase_code, recruitment_status_normalized) rather than raw " +
|
|
103
|
+
"strings, because upstream vocabulary is inconsistent in case and wording.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
set_id: { type: "string" },
|
|
108
|
+
filters: FILTER_SCHEMA,
|
|
109
|
+
sort_by: { type: "string", description: "Field to sort by." },
|
|
110
|
+
descending: { type: "boolean", description: "Sort descending. Default false." },
|
|
111
|
+
limit: { type: "number", description: "Rows to return. Default 25." },
|
|
112
|
+
offset: { type: "number", description: "Rows to skip. Default 0." },
|
|
113
|
+
},
|
|
114
|
+
required: ["set_id"],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "ictrp_field_query",
|
|
119
|
+
description: "Distinct values for one field, with how many rows carry each, plus population coverage. " +
|
|
120
|
+
"Use this before filtering to learn the real vocabulary and whether a field is populated " +
|
|
121
|
+
"enough to be worth filtering on.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
set_id: { type: "string" },
|
|
126
|
+
field: { type: "string", description: "Field name." },
|
|
127
|
+
limit: { type: "number", description: "Maximum distinct values. Default 50." },
|
|
128
|
+
},
|
|
129
|
+
required: ["set_id", "field"],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "ictrp_registry_summary",
|
|
134
|
+
description: "Composition of a set by registry, phase, recruitment status, registration year and " +
|
|
135
|
+
"country, plus per-field population coverage. The fastest way to see what a set contains " +
|
|
136
|
+
"and which fields are too sparse to trust.",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: { set_id: { type: "string" } },
|
|
140
|
+
required: ["set_id"],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "ictrp_find_duplicates",
|
|
145
|
+
description: "Find the same trial registered in more than one registry, by cross-referencing " +
|
|
146
|
+
"identifiers. Useful before counting, since the same trial can appear several times.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: "object",
|
|
149
|
+
properties: { set_id: { type: "string" } },
|
|
150
|
+
required: ["set_id"],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "ictrp_export",
|
|
155
|
+
description: "Export a materialized set as csv, json, jsonl or markdown, with a provenance header " +
|
|
156
|
+
"recording the source and processing date that WHO's terms require.",
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: {
|
|
160
|
+
set_id: { type: "string" },
|
|
161
|
+
fmt: { type: "string", enum: ["csv", "json", "jsonl", "markdown"] },
|
|
162
|
+
filters: FILTER_SCHEMA,
|
|
163
|
+
fields: {
|
|
164
|
+
type: "array",
|
|
165
|
+
items: { type: "string" },
|
|
166
|
+
description: "Fields to include. Defaults to a standard column set.",
|
|
167
|
+
},
|
|
168
|
+
destination: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "File to write. When omitted the content is returned inline.",
|
|
171
|
+
},
|
|
172
|
+
include_provenance_header: {
|
|
173
|
+
type: "boolean",
|
|
174
|
+
description: "Prepend the WHO ICTRP source and processing-date header. Default true; " +
|
|
175
|
+
"turn it off only when the consumer adds its own attribution.",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
required: ["set_id"],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "ictrp_cache_status",
|
|
183
|
+
description: "List the cached result sets held by the running service, or purge them.",
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: "object",
|
|
186
|
+
properties: {
|
|
187
|
+
action: {
|
|
188
|
+
type: "string",
|
|
189
|
+
enum: ["list", "purge"],
|
|
190
|
+
description: "List held sets, or delete them. Default list.",
|
|
191
|
+
},
|
|
192
|
+
set_id: { type: "string", description: "With action=purge, remove only this set." },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "ictrp_snapshot",
|
|
198
|
+
description: "Write a materialized set to a JSON snapshot file for offline or bundled use, or " +
|
|
199
|
+
"materialize a keyword and snapshot it in one step.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
keyword: { type: "string", description: "Keyword to snapshot." },
|
|
204
|
+
set_id: { type: "string", description: "Snapshot an already-materialized set instead." },
|
|
205
|
+
path: { type: "string", description: "Output path. Defaults to ICTRP_BUNDLE_DIR." },
|
|
206
|
+
if_stale: {
|
|
207
|
+
type: "boolean",
|
|
208
|
+
description: "Re-materialize when the existing snapshot is stale. Default true.",
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: "ictrp_bundle_status",
|
|
215
|
+
description: "Report which offline snapshots are available and how old they are. Use before " +
|
|
216
|
+
"relying on a bundled answer, since WHO refreshes ICTRP weekly and a stale snapshot " +
|
|
217
|
+
"misrepresents the registry.",
|
|
218
|
+
inputSchema: { type: "object", properties: {} },
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "ictrp_check_environment",
|
|
222
|
+
description: "Report whether this server can actually reach ICTRP: Python version, installed " +
|
|
223
|
+
"dependencies, sidecar status. Call this first when a search fails, to tell an " +
|
|
224
|
+
"environment problem from an upstream one.",
|
|
225
|
+
inputSchema: { type: "object", properties: {} },
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
function asString(args, key, required = false) {
|
|
229
|
+
const raw = args[key];
|
|
230
|
+
if (raw === undefined || raw === null) {
|
|
231
|
+
if (required)
|
|
232
|
+
throw new InvalidArgs(`missing required argument: ${key}`);
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
if (typeof raw !== "string")
|
|
236
|
+
throw new InvalidArgs(`argument '${key}' must be a string`);
|
|
237
|
+
return raw;
|
|
238
|
+
}
|
|
239
|
+
function asNumber(args, key, fallback) {
|
|
240
|
+
const raw = args[key];
|
|
241
|
+
if (raw === undefined || raw === null)
|
|
242
|
+
return fallback;
|
|
243
|
+
const n = Number(raw);
|
|
244
|
+
if (!Number.isFinite(n))
|
|
245
|
+
throw new InvalidArgs(`argument '${key}' must be a number`);
|
|
246
|
+
return n;
|
|
247
|
+
}
|
|
248
|
+
function asBoolean(args, key, fallback) {
|
|
249
|
+
const raw = args[key];
|
|
250
|
+
if (raw === undefined || raw === null)
|
|
251
|
+
return fallback;
|
|
252
|
+
if (typeof raw === "boolean")
|
|
253
|
+
return raw;
|
|
254
|
+
if (raw === "true")
|
|
255
|
+
return true;
|
|
256
|
+
if (raw === "false")
|
|
257
|
+
return false;
|
|
258
|
+
throw new InvalidArgs(`argument '${key}' must be a boolean`);
|
|
259
|
+
}
|
|
260
|
+
function asFilters(args) {
|
|
261
|
+
const raw = args["filters"];
|
|
262
|
+
if (raw === undefined || raw === null)
|
|
263
|
+
return [];
|
|
264
|
+
if (!Array.isArray(raw))
|
|
265
|
+
throw new InvalidArgs("argument 'filters' must be an array");
|
|
266
|
+
return raw.map((item, index) => {
|
|
267
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
268
|
+
throw new InvalidArgs(`filters[${index}] must be an object`);
|
|
269
|
+
}
|
|
270
|
+
return item;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function asStringArray(args, key) {
|
|
274
|
+
const raw = args[key];
|
|
275
|
+
if (raw === undefined || raw === null)
|
|
276
|
+
return undefined;
|
|
277
|
+
if (!Array.isArray(raw))
|
|
278
|
+
throw new InvalidArgs(`argument '${key}' must be an array`);
|
|
279
|
+
return raw.map((v, i) => {
|
|
280
|
+
if (typeof v !== "string")
|
|
281
|
+
throw new InvalidArgs(`${key}[${i}] must be a string`);
|
|
282
|
+
return v;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
class InvalidArgs extends Error {
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Run a tool call.
|
|
289
|
+
*
|
|
290
|
+
* Environment-backed tools (`check_environment`) work without a sidecar. Every
|
|
291
|
+
* other tool calls `ensureSidecar` first, which starts the Python process on
|
|
292
|
+
* demand and returns a typed failure rather than throwing.
|
|
293
|
+
*/
|
|
294
|
+
async function dispatch(name, args) {
|
|
295
|
+
if (name === "ictrp_check_environment") {
|
|
296
|
+
const report = await getEnvReport(true);
|
|
297
|
+
const ping = await pingSidecar();
|
|
298
|
+
return {
|
|
299
|
+
...report,
|
|
300
|
+
sidecar_health: ping.ok
|
|
301
|
+
? ping.data
|
|
302
|
+
: { error_code: ping.code, message: ping.message, log_tail: sidecarLogTail().slice(-10) },
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const ready = await ensureSidecar();
|
|
306
|
+
if (!ready.ok)
|
|
307
|
+
throw new SidecarFailure(ready);
|
|
308
|
+
switch (name) {
|
|
309
|
+
case "ictrp_search": {
|
|
310
|
+
const keyword = asString(args, "keyword", true);
|
|
311
|
+
if (!keyword.trim())
|
|
312
|
+
throw new InvalidArgs("keyword must not be empty");
|
|
313
|
+
return call({
|
|
314
|
+
endpoint: "/search",
|
|
315
|
+
params: {
|
|
316
|
+
keyword,
|
|
317
|
+
limit: asNumber(args, "limit", 25),
|
|
318
|
+
offset: asNumber(args, "offset", 0),
|
|
319
|
+
refresh: asBoolean(args, "refresh", false),
|
|
320
|
+
},
|
|
321
|
+
timeoutMs: SEARCH_TIMEOUT_MS,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
case "ictrp_filter":
|
|
325
|
+
return call({
|
|
326
|
+
endpoint: "/filter",
|
|
327
|
+
params: {
|
|
328
|
+
set_id: asString(args, "set_id", true),
|
|
329
|
+
filters: asFilters(args),
|
|
330
|
+
sort_by: asString(args, "sort_by") ?? null,
|
|
331
|
+
descending: asBoolean(args, "descending", false),
|
|
332
|
+
limit: asNumber(args, "limit", 25),
|
|
333
|
+
offset: asNumber(args, "offset", 0),
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
case "ictrp_field_query":
|
|
337
|
+
return call({
|
|
338
|
+
endpoint: "/field-query",
|
|
339
|
+
params: {
|
|
340
|
+
set_id: asString(args, "set_id", true),
|
|
341
|
+
field: asString(args, "field", true),
|
|
342
|
+
limit: asNumber(args, "limit", 50),
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
case "ictrp_registry_summary":
|
|
346
|
+
return call({
|
|
347
|
+
endpoint: "/registry-summary",
|
|
348
|
+
params: { set_id: asString(args, "set_id", true) },
|
|
349
|
+
});
|
|
350
|
+
case "ictrp_find_duplicates":
|
|
351
|
+
return call({
|
|
352
|
+
endpoint: "/find-duplicates",
|
|
353
|
+
params: { set_id: asString(args, "set_id", true) },
|
|
354
|
+
});
|
|
355
|
+
case "ictrp_export":
|
|
356
|
+
return call({
|
|
357
|
+
endpoint: "/export",
|
|
358
|
+
params: {
|
|
359
|
+
set_id: asString(args, "set_id", true),
|
|
360
|
+
fmt: asString(args, "fmt") ?? "csv",
|
|
361
|
+
filters: asFilters(args),
|
|
362
|
+
fields: asStringArray(args, "fields") ?? null,
|
|
363
|
+
include_provenance_header: asBoolean(args, "include_provenance_header", true),
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
case "ictrp_cache_status":
|
|
367
|
+
return call({
|
|
368
|
+
endpoint: "/cache-status",
|
|
369
|
+
params: {
|
|
370
|
+
action: asString(args, "action") ?? "list",
|
|
371
|
+
set_id: asString(args, "set_id") ?? null,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
case "ictrp_snapshot":
|
|
375
|
+
return call({
|
|
376
|
+
endpoint: "/snapshot",
|
|
377
|
+
params: {
|
|
378
|
+
keyword: asString(args, "keyword") ?? null,
|
|
379
|
+
set_id: asString(args, "set_id") ?? null,
|
|
380
|
+
path: asString(args, "path") ?? null,
|
|
381
|
+
if_stale: asBoolean(args, "if_stale", true),
|
|
382
|
+
},
|
|
383
|
+
timeoutMs: SEARCH_TIMEOUT_MS,
|
|
384
|
+
});
|
|
385
|
+
case "ictrp_bundle_status":
|
|
386
|
+
return call({ endpoint: "/bundle-status", params: {} });
|
|
387
|
+
default:
|
|
388
|
+
throw new InvalidArgs(`unknown tool: ${name}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
class SidecarFailure extends Error {
|
|
392
|
+
failure;
|
|
393
|
+
constructor(failure) {
|
|
394
|
+
super(failure.message);
|
|
395
|
+
this.failure = failure;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Forward one call to the sidecar.
|
|
400
|
+
*
|
|
401
|
+
* A failed call becomes a thrown error, never a value. This is the same rule
|
|
402
|
+
* the Python side enforces and it matters for the same reason: a caller that
|
|
403
|
+
* receives `[]` cannot tell "no matches" from "the export failed".
|
|
404
|
+
*/
|
|
405
|
+
async function call(opts) {
|
|
406
|
+
const execOpts = {};
|
|
407
|
+
if (opts.timeoutMs !== undefined)
|
|
408
|
+
execOpts.timeoutMs = opts.timeoutMs;
|
|
409
|
+
const result = await callSidecar(opts.endpoint, opts.params, execOpts);
|
|
410
|
+
if (!result.ok)
|
|
411
|
+
throw new SidecarFailure(result);
|
|
412
|
+
const data = result.data;
|
|
413
|
+
// Attach the notices the service is obliged to surface, without overwriting
|
|
414
|
+
// anything the Python layer already set.
|
|
415
|
+
if (data["ok"] !== false) {
|
|
416
|
+
data["incompleteness_notice"] ??= INCOMPLETENESS_NOTICE;
|
|
417
|
+
data["attribution_notice"] ??= ATTRIBUTION_NOTICE;
|
|
418
|
+
}
|
|
419
|
+
return data;
|
|
420
|
+
}
|
|
421
|
+
function textResult(value) {
|
|
422
|
+
return {
|
|
423
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function errorResult(code, message, hint) {
|
|
427
|
+
const payload = { status: "error", error_code: code, message };
|
|
428
|
+
if (hint)
|
|
429
|
+
payload["hint"] = hint;
|
|
430
|
+
return {
|
|
431
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
432
|
+
isError: true,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
async function main() {
|
|
436
|
+
installShutdownHooks();
|
|
437
|
+
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
|
|
438
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
439
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
440
|
+
const name = request.params.name;
|
|
441
|
+
const args = (request.params.arguments ?? {});
|
|
442
|
+
try {
|
|
443
|
+
return textResult(await dispatch(name, args));
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
if (err instanceof InvalidArgs) {
|
|
447
|
+
return errorResult("INVALID_ARGUMENT", err.message);
|
|
448
|
+
}
|
|
449
|
+
if (err instanceof SidecarFailure) {
|
|
450
|
+
return errorResult(err.failure.code, err.failure.message, err.failure.hint);
|
|
451
|
+
}
|
|
452
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
453
|
+
// Any other throw is a bug here, not an upstream condition. Saying so
|
|
454
|
+
// stops the caller from retrying something that will never work.
|
|
455
|
+
return errorResult("INTERNAL_ERROR", message, "This is a bug in the MCP server, not an upstream failure.");
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
const transport = new StdioServerTransport();
|
|
459
|
+
await server.connect(transport);
|
|
460
|
+
// Fire-and-forget, stderr only, fully swallowed: a boot-time probe must
|
|
461
|
+
// never delay the handshake or pollute stdout.
|
|
462
|
+
void getEnvReport()
|
|
463
|
+
.then((report) => {
|
|
464
|
+
process.stderr.write(`[${SERVER_NAME}] ${report.summary}\n`);
|
|
465
|
+
if (!report.ready) {
|
|
466
|
+
for (const action of report.actions)
|
|
467
|
+
process.stderr.write(` - ${action}\n`);
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
.catch(() => { });
|
|
471
|
+
}
|
|
472
|
+
main().catch((err) => {
|
|
473
|
+
process.stderr.write(`[${SERVER_NAME}] fatal: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
});
|
|
476
|
+
export { toErrorText };
|
|
477
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime self-setup: create the private venv and install the vendored package.
|
|
3
|
+
*
|
|
4
|
+
* This exists because **a packaged desktop application does not run npm's
|
|
5
|
+
* `postinstall`**. A dmg or an exe is a copied directory tree; npm lifecycle
|
|
6
|
+
* scripts ran on the developer's machine, not on the user's. Anything the
|
|
7
|
+
* package needs at runtime must therefore be able to set itself up on first
|
|
8
|
+
* use, at runtime, with visible progress and a definite failure mode.
|
|
9
|
+
*
|
|
10
|
+
* Two rules govern the whole module:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Never roll back a completed step.** Steps are ordered cheapest-first
|
|
13
|
+
* (venv, then dependencies, then anything large). Re-running is meant to
|
|
14
|
+
* resume, so a failure late in the sequence does not throw away work that
|
|
15
|
+
* succeeded earlier.
|
|
16
|
+
*
|
|
17
|
+
* 2. **A step that did not run is not a step that failed.** `skipped` is
|
|
18
|
+
* reported as its own outcome so the operator can tell "already fine" from
|
|
19
|
+
* "we installed it just now" from "we tried and it broke".
|
|
20
|
+
*/
|
|
21
|
+
export interface ProgressEvent {
|
|
22
|
+
step: string;
|
|
23
|
+
message: string;
|
|
24
|
+
percent?: number;
|
|
25
|
+
}
|
|
26
|
+
export type StepOutcome = "ok" | "skipped" | "failed";
|
|
27
|
+
export interface StepResult {
|
|
28
|
+
step: string;
|
|
29
|
+
outcome: StepOutcome;
|
|
30
|
+
message: string;
|
|
31
|
+
attempts: number;
|
|
32
|
+
/** Mirrors actually tried, in order. Only present for install steps. */
|
|
33
|
+
triedMirrors?: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface BootstrapResult {
|
|
36
|
+
ok: boolean;
|
|
37
|
+
steps: StepResult[];
|
|
38
|
+
/** Non-empty when the run did not fully succeed. */
|
|
39
|
+
errors: string[];
|
|
40
|
+
}
|
|
41
|
+
export type ProgressFn = (event: ProgressEvent) => void;
|
|
42
|
+
/**
|
|
43
|
+
* PyPI mirrors, most-likely-to-work first for the users this ships to.
|
|
44
|
+
*
|
|
45
|
+
* The fallback chain is not a nicety. A mirror can fail in a way that looks
|
|
46
|
+
* exactly like "this package does not exist" -- notably a 403 on a modern pip
|
|
47
|
+
* user-agent surfaces as `from versions: none`, which sends the operator
|
|
48
|
+
* hunting for a typo instead of a mirror problem. Trying the next mirror turns
|
|
49
|
+
* that misdiagnosis into a non-event.
|
|
50
|
+
*/
|
|
51
|
+
export declare const PYPI_MIRRORS: Record<string, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Build the mirror order: the preferred mirror first, then everything else
|
|
54
|
+
* except the official index, then the official index last as the backstop.
|
|
55
|
+
*
|
|
56
|
+
* `official` is pinned last on purpose. It is the most correct source and the
|
|
57
|
+
* slowest / most-blocked from the networks this is expected to run on, so it
|
|
58
|
+
* is the fallback rather than the default.
|
|
59
|
+
*/
|
|
60
|
+
export declare function mirrorChain(preferred?: string): Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
url: string;
|
|
63
|
+
}>;
|
|
64
|
+
interface ExecResult {
|
|
65
|
+
code: number;
|
|
66
|
+
stdout: string;
|
|
67
|
+
stderr: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run a child process, streaming its output to `onLine`.
|
|
71
|
+
*
|
|
72
|
+
* Exit codes follow the convention the installer relies on:
|
|
73
|
+
* 124 -- the step timed out and was killed
|
|
74
|
+
* 130 -- the run was aborted by the caller
|
|
75
|
+
* 127 -- the executable could not be spawned at all
|
|
76
|
+
*/
|
|
77
|
+
export declare function execStreaming(cmd: string, args: string[], opts?: {
|
|
78
|
+
cwd?: string;
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
signal?: AbortSignal;
|
|
81
|
+
onLine?: (line: string) => void;
|
|
82
|
+
}): Promise<ExecResult>;
|
|
83
|
+
/** Find a usable system Python, newest acceptable version first. */
|
|
84
|
+
export declare function discoverPython(): Promise<{
|
|
85
|
+
cmd: string;
|
|
86
|
+
version: string;
|
|
87
|
+
} | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* Bring the environment to a usable state.
|
|
90
|
+
*
|
|
91
|
+
* Returns rather than throws: the caller (`setup` CLI, or the MCP layer's
|
|
92
|
+
* lazy bootstrap) needs the step list to report *what* went wrong.
|
|
93
|
+
*/
|
|
94
|
+
export declare function bootstrapEnvironment(opts?: {
|
|
95
|
+
mirror?: string;
|
|
96
|
+
onProgress?: ProgressFn;
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
}): Promise<BootstrapResult>;
|
|
99
|
+
export {};
|