@wrongstack/mcp 0.295.0 → 0.295.1
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/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +212 -158
- package/dist/index.js.map +4 -4
- package/dist/manifest-cache.d.ts.map +1 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/read-body.d.ts +21 -0
- package/dist/read-body.d.ts.map +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/tool-schema.d.ts.map +1 -1
- package/dist/transport-jsonrpc.d.ts +2 -2
- package/dist/transport-jsonrpc.d.ts.map +1 -1
- package/dist/transport-sse.d.ts.map +1 -1
- package/dist/transport-streamable.d.ts.map +1 -1
- package/dist/transport.d.ts +2 -2
- package/dist/transport.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
1
2
|
import type { MCPAuthorizationProvider } from './authorization.js';
|
|
2
3
|
import { type MCPGetPromptResult, type MCPListPromptsResult, type MCPListResourcesResult, type MCPListResourceTemplatesResult, type MCPReadResourceResult, type MCPServerMetadata } from './protocol.js';
|
|
3
4
|
export type Transport = 'stdio' | 'sse' | 'streamable-http';
|
|
@@ -58,6 +59,14 @@ type ExitListener = (name: string, code: number | null, signal: string | null) =
|
|
|
58
59
|
*/
|
|
59
60
|
type ToolsChangedListener = (name: string, tools: MCPTool[]) => void;
|
|
60
61
|
export type MCPListChangedListener = (name: string) => void;
|
|
62
|
+
/**
|
|
63
|
+
* Force-kill a child and its descendants. On Windows a stdio server is launched
|
|
64
|
+
* through a `.cmd` shim with `shell: true`, so `child` is the `cmd.exe` wrapper
|
|
65
|
+
* and the real server (npx→node / uvx) is its grandchild — `child.kill('SIGKILL')`
|
|
66
|
+
* signals only the wrapper and orphans the server, which then accumulates across
|
|
67
|
+
* every close / restart / idle-sleep. `taskkill /T /F` tears down the whole tree.
|
|
68
|
+
*/
|
|
69
|
+
export declare function forceKillTree(child: ChildProcess): void;
|
|
61
70
|
/**
|
|
62
71
|
* Lightweight MCP client supporting three transport types:
|
|
63
72
|
* - stdio: spawns a child process and communicates over pipes
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ function parseMcpBearerChallenge(header, resource) {
|
|
|
47
47
|
if (!header) return challenge;
|
|
48
48
|
const bearer = /(?:^|,)\s*Bearer(?:\s+|$)/i.exec(header);
|
|
49
49
|
if (!bearer) return challenge;
|
|
50
|
-
const parameters = header.slice(
|
|
50
|
+
const parameters = header.slice(bearer.index + bearer[0].length);
|
|
51
51
|
const resourceMetadata = challengeParameter(parameters, "resource_metadata");
|
|
52
52
|
if (resourceMetadata) {
|
|
53
53
|
const metadataUrl = validateMetadataUrl(resourceMetadata);
|
|
@@ -395,7 +395,7 @@ async function requestPinnedJson(rawUrl, options) {
|
|
|
395
395
|
};
|
|
396
396
|
const requestFn = url.protocol === "https:" ? https.request : http.request;
|
|
397
397
|
const request3 = requestFn(requestOptions, (response) => {
|
|
398
|
-
const status = response.statusCode
|
|
398
|
+
const status = response.statusCode;
|
|
399
399
|
if (status === 404 || status === 410) {
|
|
400
400
|
response.resume();
|
|
401
401
|
finish(void 0, void 0);
|
|
@@ -739,8 +739,7 @@ function boundedServerName(value) {
|
|
|
739
739
|
|
|
740
740
|
// src/client.ts
|
|
741
741
|
import { spawn } from "node:child_process";
|
|
742
|
-
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
743
|
-
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
742
|
+
import { buildChildEnv, toErrorMessage } from "@wrongstack/core/utils";
|
|
744
743
|
|
|
745
744
|
// src/constants.ts
|
|
746
745
|
var MCP_CONSTANTS = Object.freeze({
|
|
@@ -997,13 +996,15 @@ function normalizeMCPTools(value) {
|
|
|
997
996
|
if (typeof t.name !== "string" || t.name.trim().length === 0) continue;
|
|
998
997
|
const inputSchema = t.inputSchema && typeof t.inputSchema === "object" && !Array.isArray(t.inputSchema) ? t.inputSchema : { type: "object", properties: {} };
|
|
999
998
|
if (!t.inputSchema || typeof t.inputSchema !== "object" || Array.isArray(t.inputSchema)) {
|
|
1000
|
-
console.warn(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
999
|
+
console.warn(
|
|
1000
|
+
JSON.stringify({
|
|
1001
|
+
level: "warn",
|
|
1002
|
+
event: "mcp.tool_schema_invalid",
|
|
1003
|
+
tool: t.name,
|
|
1004
|
+
message: "no/invalid inputSchema \u2014 defaulting to empty object",
|
|
1005
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1006
|
+
})
|
|
1007
|
+
);
|
|
1007
1008
|
}
|
|
1008
1009
|
tools.push({
|
|
1009
1010
|
name: t.name,
|
|
@@ -1014,96 +1015,8 @@ function normalizeMCPTools(value) {
|
|
|
1014
1015
|
return tools;
|
|
1015
1016
|
}
|
|
1016
1017
|
|
|
1017
|
-
// src/transport-jsonrpc.ts
|
|
1018
|
-
import { ToolError } from "@wrongstack/core/types";
|
|
1019
|
-
function isJsonRpcResult(v) {
|
|
1020
|
-
if (typeof v !== "object" || v === null) return false;
|
|
1021
|
-
const r = v;
|
|
1022
|
-
if (r["jsonrpc"] !== "2.0" || typeof r["id"] !== "number") return false;
|
|
1023
|
-
if (Object.hasOwn(r, "method")) return false;
|
|
1024
|
-
const hasResult = Object.hasOwn(r, "result");
|
|
1025
|
-
const hasError = Object.hasOwn(r, "error");
|
|
1026
|
-
if (hasResult === hasError) return false;
|
|
1027
|
-
if (hasError) {
|
|
1028
|
-
const error = r["error"];
|
|
1029
|
-
return typeof error === "object" && error !== null && typeof error["code"] === "number" && typeof error["message"] === "string";
|
|
1030
|
-
}
|
|
1031
|
-
return true;
|
|
1032
|
-
}
|
|
1033
|
-
function isJsonRpcMethodEnvelope(v) {
|
|
1034
|
-
if (typeof v !== "object" || v === null) return false;
|
|
1035
|
-
const envelope = v;
|
|
1036
|
-
if (envelope["jsonrpc"] !== "2.0" || typeof envelope["method"] !== "string") return false;
|
|
1037
|
-
const id = envelope["id"];
|
|
1038
|
-
return id === void 0 || typeof id === "number" || typeof id === "string";
|
|
1039
|
-
}
|
|
1040
|
-
function extractJsonRpcEnvelopes(text) {
|
|
1041
|
-
const out = [];
|
|
1042
|
-
let dataBuf = [];
|
|
1043
|
-
const flush = () => {
|
|
1044
|
-
if (dataBuf.length === 0) return;
|
|
1045
|
-
const joined = dataBuf.join("\n").trim();
|
|
1046
|
-
dataBuf = [];
|
|
1047
|
-
if (!joined) return;
|
|
1048
|
-
try {
|
|
1049
|
-
const parsed = JSON.parse(joined);
|
|
1050
|
-
if (isJsonRpcResult(parsed) || isJsonRpcMethodEnvelope(parsed)) out.push(parsed);
|
|
1051
|
-
} catch {
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
for (const raw of text.split("\n")) {
|
|
1055
|
-
const line = raw.replace(/\r$/, "");
|
|
1056
|
-
if (line === "") {
|
|
1057
|
-
flush();
|
|
1058
|
-
continue;
|
|
1059
|
-
}
|
|
1060
|
-
if (line.startsWith(":")) continue;
|
|
1061
|
-
if (line.startsWith("data:")) {
|
|
1062
|
-
let v = line.slice(5);
|
|
1063
|
-
if (v.startsWith(" ")) v = v.slice(1);
|
|
1064
|
-
dataBuf.push(v);
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
if (line.startsWith("event:") || line.startsWith("id:") || line.startsWith("retry:")) {
|
|
1068
|
-
continue;
|
|
1069
|
-
}
|
|
1070
|
-
const trimmed = line.trim();
|
|
1071
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
1072
|
-
try {
|
|
1073
|
-
const parsed = JSON.parse(trimmed);
|
|
1074
|
-
if (isJsonRpcResult(parsed) || isJsonRpcMethodEnvelope(parsed)) out.push(parsed);
|
|
1075
|
-
} catch {
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
flush();
|
|
1080
|
-
return out;
|
|
1081
|
-
}
|
|
1082
|
-
function extractJsonRpcResults(text) {
|
|
1083
|
-
return extractJsonRpcEnvelopes(text).filter(isJsonRpcResult);
|
|
1084
|
-
}
|
|
1085
|
-
function assertMatchingJsonRpcResult(data, expectedId, method) {
|
|
1086
|
-
if (!isJsonRpcResult(data)) {
|
|
1087
|
-
throw new ToolError({
|
|
1088
|
-
message: "Invalid JSON-RPC response: not a JSON-RPC 2.0 envelope",
|
|
1089
|
-
code: "TOOL_EXECUTION_FAILED",
|
|
1090
|
-
toolName: "mcp_transport_jsonrpc",
|
|
1091
|
-
context: { method, expectedId, reason: "not-jsonrpc-envelope" }
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
if (data.id !== expectedId) {
|
|
1095
|
-
throw new ToolError({
|
|
1096
|
-
message: `Invalid JSON-RPC response: id mismatch for ${method} (expected ${expectedId}, got ${data.id})`,
|
|
1097
|
-
code: "TOOL_EXECUTION_FAILED",
|
|
1098
|
-
toolName: "mcp_transport_jsonrpc",
|
|
1099
|
-
context: { method, expectedId, actualId: data.id, reason: "id-mismatch" }
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
return data;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
1018
|
// src/sse-reader.ts
|
|
1106
|
-
import { ToolError
|
|
1019
|
+
import { ToolError } from "@wrongstack/core/types";
|
|
1107
1020
|
var SSE_READER_MAX_BUFFER = 256 * 1024;
|
|
1108
1021
|
var SSE_READER_MAX_DATA_LINES = 1024;
|
|
1109
1022
|
var SSEReader = class {
|
|
@@ -1119,7 +1032,7 @@ var SSEReader = class {
|
|
|
1119
1032
|
}
|
|
1120
1033
|
feed(chunk) {
|
|
1121
1034
|
if (chunk.length > SSE_READER_MAX_BUFFER) {
|
|
1122
|
-
throw new
|
|
1035
|
+
throw new ToolError({
|
|
1123
1036
|
message: `SSE: chunk size ${chunk.length} exceeds max buffer ${SSE_READER_MAX_BUFFER} \u2014 refusing to accumulate`,
|
|
1124
1037
|
code: "TOOL_EXECUTION_FAILED",
|
|
1125
1038
|
toolName: "mcp_transport_sse_reader",
|
|
@@ -1128,7 +1041,7 @@ var SSEReader = class {
|
|
|
1128
1041
|
}
|
|
1129
1042
|
this.buffer += chunk;
|
|
1130
1043
|
if (this.buffer.length > SSE_READER_MAX_BUFFER) {
|
|
1131
|
-
throw new
|
|
1044
|
+
throw new ToolError({
|
|
1132
1045
|
message: `SSE: pending line exceeds ${SSE_READER_MAX_BUFFER} bytes \u2014 upstream is not framing events`,
|
|
1133
1046
|
code: "TOOL_EXECUTION_FAILED",
|
|
1134
1047
|
toolName: "mcp_transport_sse_reader",
|
|
@@ -1163,7 +1076,7 @@ var SSEReader = class {
|
|
|
1163
1076
|
if (field === "event") {
|
|
1164
1077
|
} else if (field === "data") {
|
|
1165
1078
|
if (this.dataLines.length >= SSE_READER_MAX_DATA_LINES) {
|
|
1166
|
-
throw new
|
|
1079
|
+
throw new ToolError({
|
|
1167
1080
|
message: `SSE: exceeded ${SSE_READER_MAX_DATA_LINES} data lines per event \u2014 upstream is not sending blank-line delimiters`,
|
|
1168
1081
|
code: "TOOL_EXECUTION_FAILED",
|
|
1169
1082
|
toolName: "mcp_transport_sse_reader",
|
|
@@ -1453,9 +1366,133 @@ var BaseHTTPTransport = class {
|
|
|
1453
1366
|
}
|
|
1454
1367
|
};
|
|
1455
1368
|
|
|
1369
|
+
// src/transport-jsonrpc.ts
|
|
1370
|
+
import { ToolError as ToolError2 } from "@wrongstack/core/types";
|
|
1371
|
+
function isJsonRpcResult(v) {
|
|
1372
|
+
if (typeof v !== "object" || v === null) return false;
|
|
1373
|
+
const r = v;
|
|
1374
|
+
if (r["jsonrpc"] !== "2.0" || typeof r["id"] !== "number") return false;
|
|
1375
|
+
if (Object.hasOwn(r, "method")) return false;
|
|
1376
|
+
const hasResult = Object.hasOwn(r, "result");
|
|
1377
|
+
const hasError = Object.hasOwn(r, "error");
|
|
1378
|
+
if (hasResult === hasError) return false;
|
|
1379
|
+
if (hasError) {
|
|
1380
|
+
const error = r["error"];
|
|
1381
|
+
return typeof error === "object" && error !== null && typeof error["code"] === "number" && typeof error["message"] === "string";
|
|
1382
|
+
}
|
|
1383
|
+
return true;
|
|
1384
|
+
}
|
|
1385
|
+
function isJsonRpcMethodEnvelope(v) {
|
|
1386
|
+
if (typeof v !== "object" || v === null) return false;
|
|
1387
|
+
const envelope = v;
|
|
1388
|
+
if (envelope["jsonrpc"] !== "2.0" || typeof envelope["method"] !== "string") return false;
|
|
1389
|
+
const id = envelope["id"];
|
|
1390
|
+
return id === void 0 || typeof id === "number" || typeof id === "string";
|
|
1391
|
+
}
|
|
1392
|
+
function extractJsonRpcEnvelopes(text) {
|
|
1393
|
+
const out = [];
|
|
1394
|
+
let dataBuf = [];
|
|
1395
|
+
const flush = () => {
|
|
1396
|
+
if (dataBuf.length === 0) return;
|
|
1397
|
+
const joined = dataBuf.join("\n").trim();
|
|
1398
|
+
dataBuf = [];
|
|
1399
|
+
if (!joined) return;
|
|
1400
|
+
try {
|
|
1401
|
+
const parsed = JSON.parse(joined);
|
|
1402
|
+
if (isJsonRpcResult(parsed) || isJsonRpcMethodEnvelope(parsed)) out.push(parsed);
|
|
1403
|
+
} catch {
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
for (const raw of text.split("\n")) {
|
|
1407
|
+
const line = raw.replace(/\r$/, "");
|
|
1408
|
+
if (line === "") {
|
|
1409
|
+
flush();
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
if (line.startsWith(":")) continue;
|
|
1413
|
+
if (line.startsWith("data:")) {
|
|
1414
|
+
let v = line.slice(5);
|
|
1415
|
+
if (v.startsWith(" ")) v = v.slice(1);
|
|
1416
|
+
dataBuf.push(v);
|
|
1417
|
+
continue;
|
|
1418
|
+
}
|
|
1419
|
+
if (line.startsWith("event:") || line.startsWith("id:") || line.startsWith("retry:")) {
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
const trimmed = line.trim();
|
|
1423
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
1424
|
+
try {
|
|
1425
|
+
const parsed = JSON.parse(trimmed);
|
|
1426
|
+
if (isJsonRpcResult(parsed) || isJsonRpcMethodEnvelope(parsed)) out.push(parsed);
|
|
1427
|
+
} catch {
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
flush();
|
|
1432
|
+
return out;
|
|
1433
|
+
}
|
|
1434
|
+
function extractJsonRpcResults(text) {
|
|
1435
|
+
return extractJsonRpcEnvelopes(text).filter(isJsonRpcResult);
|
|
1436
|
+
}
|
|
1437
|
+
function assertMatchingJsonRpcResult(data, expectedId, method) {
|
|
1438
|
+
if (!isJsonRpcResult(data)) {
|
|
1439
|
+
throw new ToolError2({
|
|
1440
|
+
message: "Invalid JSON-RPC response: not a JSON-RPC 2.0 envelope",
|
|
1441
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
1442
|
+
toolName: "mcp_transport_jsonrpc",
|
|
1443
|
+
context: { method, expectedId, reason: "not-jsonrpc-envelope" }
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
if (data.id !== expectedId) {
|
|
1447
|
+
throw new ToolError2({
|
|
1448
|
+
message: `Invalid JSON-RPC response: id mismatch for ${method} (expected ${expectedId}, got ${data.id})`,
|
|
1449
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
1450
|
+
toolName: "mcp_transport_jsonrpc",
|
|
1451
|
+
context: { method, expectedId, actualId: data.id, reason: "id-mismatch" }
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
return data;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1456
1457
|
// src/transport-sse.ts
|
|
1457
1458
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1459
|
+
import { ToolError as ToolError4 } from "@wrongstack/core/types";
|
|
1460
|
+
|
|
1461
|
+
// src/read-body.ts
|
|
1458
1462
|
import { ToolError as ToolError3 } from "@wrongstack/core/types";
|
|
1463
|
+
var MAX_MCP_HTTP_BODY_BYTES = 16 * 1024 * 1024;
|
|
1464
|
+
async function readBodyCapped(res, maxBytes = MAX_MCP_HTTP_BODY_BYTES) {
|
|
1465
|
+
const body = res.body;
|
|
1466
|
+
if (!body || typeof body.getReader !== "function") {
|
|
1467
|
+
return await res.text();
|
|
1468
|
+
}
|
|
1469
|
+
const reader = body.getReader();
|
|
1470
|
+
const chunks = [];
|
|
1471
|
+
let total = 0;
|
|
1472
|
+
try {
|
|
1473
|
+
for (; ; ) {
|
|
1474
|
+
const { done, value } = await reader.read();
|
|
1475
|
+
if (done) break;
|
|
1476
|
+
if (!value) continue;
|
|
1477
|
+
total += value.byteLength;
|
|
1478
|
+
if (total > maxBytes) {
|
|
1479
|
+
await reader.cancel().catch(() => void 0);
|
|
1480
|
+
throw new ToolError3({
|
|
1481
|
+
message: `MCP response body exceeded ${maxBytes} bytes \u2014 refusing to buffer`,
|
|
1482
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
1483
|
+
toolName: "mcp_transport",
|
|
1484
|
+
context: { maxBytes, received: total }
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
chunks.push(value);
|
|
1488
|
+
}
|
|
1489
|
+
} finally {
|
|
1490
|
+
reader.releaseLock?.();
|
|
1491
|
+
}
|
|
1492
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/transport-sse.ts
|
|
1459
1496
|
var SSETransport = class extends BaseHTTPTransport {
|
|
1460
1497
|
_nextId = 1;
|
|
1461
1498
|
readerDone = false;
|
|
@@ -1502,7 +1539,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1502
1539
|
this.applyTlsAgent(fetchOpts);
|
|
1503
1540
|
const response = await this.fetchWithAuthorization(sseUrl, fetchOpts, signal);
|
|
1504
1541
|
if (!response.ok) {
|
|
1505
|
-
throw new
|
|
1542
|
+
throw new ToolError4({
|
|
1506
1543
|
message: `SSE connect HTTP ${response.status}: ${response.statusText}`,
|
|
1507
1544
|
code: "TOOL_EXECUTION_FAILED",
|
|
1508
1545
|
toolName: "mcp_transport_sse_connect",
|
|
@@ -1510,7 +1547,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1510
1547
|
});
|
|
1511
1548
|
}
|
|
1512
1549
|
if (!response.body) {
|
|
1513
|
-
throw new
|
|
1550
|
+
throw new ToolError4({
|
|
1514
1551
|
message: "SSE response has no body",
|
|
1515
1552
|
code: "TOOL_EXECUTION_FAILED",
|
|
1516
1553
|
toolName: "mcp_transport_sse_connect",
|
|
@@ -1543,7 +1580,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1543
1580
|
clientInfo: MCP_CONSTANTS.CLIENT_INFO
|
|
1544
1581
|
});
|
|
1545
1582
|
if (initRes.error) {
|
|
1546
|
-
throw new
|
|
1583
|
+
throw new ToolError4({
|
|
1547
1584
|
message: `initialize failed: ${initRes.error.message}`,
|
|
1548
1585
|
code: "TOOL_EXECUTION_FAILED",
|
|
1549
1586
|
toolName: "mcp_transport_initialize",
|
|
@@ -1618,7 +1655,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1618
1655
|
const body2 = await res.text();
|
|
1619
1656
|
const cap = MCP_CONSTANTS.REQUEST_LOG_CAP;
|
|
1620
1657
|
const snippet = body2.length > cap ? `${body2.slice(0, cap)}\u2026 [${body2.length} bytes total]` : body2;
|
|
1621
|
-
throw new
|
|
1658
|
+
throw new ToolError4({
|
|
1622
1659
|
message: `HTTP ${res.status}: ${snippet}`,
|
|
1623
1660
|
code: "TOOL_EXECUTION_FAILED",
|
|
1624
1661
|
toolName: method,
|
|
@@ -1627,9 +1664,9 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1627
1664
|
}
|
|
1628
1665
|
let data;
|
|
1629
1666
|
try {
|
|
1630
|
-
data = await res
|
|
1667
|
+
data = JSON.parse(await readBodyCapped(res));
|
|
1631
1668
|
} catch (err) {
|
|
1632
|
-
throw new
|
|
1669
|
+
throw new ToolError4({
|
|
1633
1670
|
message: `Invalid JSON-RPC response: ${err instanceof Error ? err.message : "parse failed"}`,
|
|
1634
1671
|
code: "TOOL_EXECUTION_FAILED",
|
|
1635
1672
|
toolName: method,
|
|
@@ -1654,7 +1691,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1654
1691
|
}
|
|
1655
1692
|
async callTool(name, input, opts) {
|
|
1656
1693
|
if (this.state !== "connected") {
|
|
1657
|
-
throw new
|
|
1694
|
+
throw new ToolError4({
|
|
1658
1695
|
message: `SSE transport not connected (state=${this.state})`,
|
|
1659
1696
|
code: "TOOL_EXECUTION_FAILED",
|
|
1660
1697
|
toolName: name,
|
|
@@ -1691,7 +1728,7 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1691
1728
|
try {
|
|
1692
1729
|
const res = await this.fetchWithAuthorization(this.url, fetchOpts, timeoutSignal.signal);
|
|
1693
1730
|
if (!res.ok) {
|
|
1694
|
-
throw new
|
|
1731
|
+
throw new ToolError4({
|
|
1695
1732
|
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
1696
1733
|
code: "TOOL_EXECUTION_FAILED",
|
|
1697
1734
|
toolName: method,
|
|
@@ -1705,9 +1742,9 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
1705
1742
|
}
|
|
1706
1743
|
let data;
|
|
1707
1744
|
try {
|
|
1708
|
-
data = await res
|
|
1745
|
+
data = JSON.parse(await readBodyCapped(res));
|
|
1709
1746
|
} catch (err) {
|
|
1710
|
-
throw new
|
|
1747
|
+
throw new ToolError4({
|
|
1711
1748
|
message: `Invalid JSON-RPC response: ${err instanceof Error ? err.message : "parse failed"}`,
|
|
1712
1749
|
code: "TOOL_EXECUTION_FAILED",
|
|
1713
1750
|
toolName: method,
|
|
@@ -1767,7 +1804,7 @@ var StreamableHTTPTransport = class extends BaseHTTPTransport {
|
|
|
1767
1804
|
}
|
|
1768
1805
|
}
|
|
1769
1806
|
const responses = envelopes.filter(isJsonRpcResult);
|
|
1770
|
-
return responses.find((envelope) => envelope.id === requestId) ?? responses
|
|
1807
|
+
return responses.find((envelope) => envelope.id === requestId) ?? responses[0];
|
|
1771
1808
|
}
|
|
1772
1809
|
handleNotification(method) {
|
|
1773
1810
|
if (method === "notifications/resources/list_changed") {
|
|
@@ -1886,9 +1923,9 @@ var StreamableHTTPTransport = class extends BaseHTTPTransport {
|
|
|
1886
1923
|
}
|
|
1887
1924
|
if (method.startsWith("notifications/")) {
|
|
1888
1925
|
await res.text().catch(() => void 0);
|
|
1889
|
-
return { jsonrpc: "2.0" };
|
|
1926
|
+
return { jsonrpc: "2.0", id };
|
|
1890
1927
|
}
|
|
1891
|
-
const match = this.consumeResponseText(await res
|
|
1928
|
+
const match = this.consumeResponseText(await readBodyCapped(res), id);
|
|
1892
1929
|
if (match) {
|
|
1893
1930
|
return assertMatchingJsonRpcResult(match, id, method);
|
|
1894
1931
|
}
|
|
@@ -1935,7 +1972,7 @@ var StreamableHTTPTransport = class extends BaseHTTPTransport {
|
|
|
1935
1972
|
await res.text().catch(() => void 0);
|
|
1936
1973
|
return { jsonrpc: "2.0", id };
|
|
1937
1974
|
}
|
|
1938
|
-
const parsed = this.consumeResponseText(await res
|
|
1975
|
+
const parsed = this.consumeResponseText(await readBodyCapped(res), id);
|
|
1939
1976
|
if (parsed) {
|
|
1940
1977
|
return {
|
|
1941
1978
|
jsonrpc: "2.0",
|
|
@@ -1982,17 +2019,32 @@ var StreamableHTTPTransport = class extends BaseHTTPTransport {
|
|
|
1982
2019
|
};
|
|
1983
2020
|
|
|
1984
2021
|
// src/client.ts
|
|
1985
|
-
function
|
|
1986
|
-
if (
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
if (
|
|
1994
|
-
|
|
1995
|
-
|
|
2022
|
+
function forceKillTree(child) {
|
|
2023
|
+
if (child.pid === void 0) {
|
|
2024
|
+
try {
|
|
2025
|
+
child.kill("SIGKILL");
|
|
2026
|
+
} catch {
|
|
2027
|
+
}
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (process.platform === "win32") {
|
|
2031
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
2032
|
+
stdio: "ignore",
|
|
2033
|
+
windowsHide: true
|
|
2034
|
+
});
|
|
2035
|
+
killer.once("error", () => {
|
|
2036
|
+
try {
|
|
2037
|
+
child.kill("SIGKILL");
|
|
2038
|
+
} catch {
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
killer.unref();
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
try {
|
|
2045
|
+
child.kill("SIGKILL");
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
1996
2048
|
}
|
|
1997
2049
|
var MCPClient = class _MCPClient {
|
|
1998
2050
|
constructor(opts) {
|
|
@@ -2386,10 +2438,7 @@ var MCPClient = class _MCPClient {
|
|
|
2386
2438
|
new Promise((resolve) => setTimeout(() => resolve("timeout"), GRACEFUL_MS))
|
|
2387
2439
|
]);
|
|
2388
2440
|
if (gracefulRace === "timeout") {
|
|
2389
|
-
|
|
2390
|
-
child.kill("SIGKILL");
|
|
2391
|
-
} catch {
|
|
2392
|
-
}
|
|
2441
|
+
forceKillTree(child);
|
|
2393
2442
|
await Promise.race([
|
|
2394
2443
|
exitPromise,
|
|
2395
2444
|
new Promise((resolve) => setTimeout(resolve, FORCE_TIMEOUT_MS))
|
|
@@ -2618,11 +2667,12 @@ var MCPClient = class _MCPClient {
|
|
|
2618
2667
|
}
|
|
2619
2668
|
return;
|
|
2620
2669
|
}
|
|
2621
|
-
if (!
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
this.pending.
|
|
2625
|
-
|
|
2670
|
+
if (!isJsonRpcResult(msg)) return;
|
|
2671
|
+
const response = msg;
|
|
2672
|
+
if (this.pending.has(response.id)) {
|
|
2673
|
+
const entry = this.pending.get(response.id);
|
|
2674
|
+
this.pending.delete(response.id);
|
|
2675
|
+
entry?.resolve(response);
|
|
2626
2676
|
}
|
|
2627
2677
|
}
|
|
2628
2678
|
handleServerRequest(request3) {
|
|
@@ -2856,7 +2906,7 @@ async function writeConfig(path2, cfg) {
|
|
|
2856
2906
|
try {
|
|
2857
2907
|
await fs.rename(tmp, path2);
|
|
2858
2908
|
} catch (err) {
|
|
2859
|
-
await fs.rm(tmp, { force: true })
|
|
2909
|
+
await fs.rm(tmp, { force: true });
|
|
2860
2910
|
throw err;
|
|
2861
2911
|
}
|
|
2862
2912
|
}
|
|
@@ -3096,7 +3146,7 @@ function forgetRegistryState(registry, name) {
|
|
|
3096
3146
|
}
|
|
3097
3147
|
|
|
3098
3148
|
// src/manifest-cache.ts
|
|
3099
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
3149
|
+
import { createHash as createHash2, randomBytes as randomBytes4 } from "node:crypto";
|
|
3100
3150
|
import * as fs2 from "node:fs/promises";
|
|
3101
3151
|
import * as path from "node:path";
|
|
3102
3152
|
function manifestConfigHash(cfg) {
|
|
@@ -3144,9 +3194,14 @@ async function writeCapabilityManifest(cacheDir, name, configHash, manifest) {
|
|
|
3144
3194
|
const file = manifestFile(cacheDir, name);
|
|
3145
3195
|
await fs2.mkdir(path.dirname(file), { recursive: true });
|
|
3146
3196
|
const body = { version: 2, configHash, ...manifest };
|
|
3147
|
-
const tmp = `${file}.tmp`;
|
|
3148
|
-
|
|
3149
|
-
|
|
3197
|
+
const tmp = `${file}.${process.pid}.${randomBytes4(6).toString("hex")}.tmp`;
|
|
3198
|
+
try {
|
|
3199
|
+
await fs2.writeFile(tmp, JSON.stringify(body, null, 2), "utf8");
|
|
3200
|
+
await fs2.rename(tmp, file);
|
|
3201
|
+
} catch (err) {
|
|
3202
|
+
await fs2.rm(tmp, { force: true });
|
|
3203
|
+
throw err;
|
|
3204
|
+
}
|
|
3150
3205
|
} catch {
|
|
3151
3206
|
}
|
|
3152
3207
|
}
|
|
@@ -3207,7 +3262,10 @@ function evaluateHealthThresholds(operations, thresholds) {
|
|
|
3207
3262
|
if (!thresholds) return [];
|
|
3208
3263
|
const checks = [];
|
|
3209
3264
|
if (thresholds.connectionLatencyP95Ms !== void 0 && operations.connectionSamples.length > 0) {
|
|
3210
|
-
const value = percentile(
|
|
3265
|
+
const value = percentile(
|
|
3266
|
+
[...operations.connectionSamples].sort((a, b) => a - b),
|
|
3267
|
+
0.95
|
|
3268
|
+
);
|
|
3211
3269
|
checks.push({
|
|
3212
3270
|
name: "connection-latency-p95",
|
|
3213
3271
|
passed: value <= thresholds.connectionLatencyP95Ms,
|
|
@@ -3216,7 +3274,10 @@ function evaluateHealthThresholds(operations, thresholds) {
|
|
|
3216
3274
|
});
|
|
3217
3275
|
}
|
|
3218
3276
|
if (thresholds.discoveryLatencyP95Ms !== void 0 && operations.discoverySamples.length > 0) {
|
|
3219
|
-
const value = percentile(
|
|
3277
|
+
const value = percentile(
|
|
3278
|
+
[...operations.discoverySamples].sort((a, b) => a - b),
|
|
3279
|
+
0.95
|
|
3280
|
+
);
|
|
3220
3281
|
checks.push({
|
|
3221
3282
|
name: "discovery-latency-p95",
|
|
3222
3283
|
passed: value <= thresholds.discoveryLatencyP95Ms,
|
|
@@ -3225,7 +3286,10 @@ function evaluateHealthThresholds(operations, thresholds) {
|
|
|
3225
3286
|
});
|
|
3226
3287
|
}
|
|
3227
3288
|
if (thresholds.callLatencyP95Ms !== void 0 && operations.callSamples.length > 0) {
|
|
3228
|
-
const value = percentile(
|
|
3289
|
+
const value = percentile(
|
|
3290
|
+
[...operations.callSamples].sort((a, b) => a - b),
|
|
3291
|
+
0.95
|
|
3292
|
+
);
|
|
3229
3293
|
checks.push({
|
|
3230
3294
|
name: "call-latency-p95",
|
|
3231
3295
|
passed: value <= thresholds.callLatencyP95Ms,
|
|
@@ -3461,11 +3525,7 @@ var MCPRegistry = class _MCPRegistry {
|
|
|
3461
3525
|
* cache yet, do a one-time cold discovery connect to learn + cache the tools.
|
|
3462
3526
|
*/
|
|
3463
3527
|
async startLazy(slot) {
|
|
3464
|
-
const cacheDir = this.cacheDir;
|
|
3465
|
-
if (!cacheDir) {
|
|
3466
|
-
await this.attemptConnect(slot);
|
|
3467
|
-
return;
|
|
3468
|
-
}
|
|
3528
|
+
const cacheDir = expectDefined(this.cacheDir);
|
|
3469
3529
|
const hash = manifestConfigHash(slot.cfg);
|
|
3470
3530
|
const cached = await readCapabilityManifest(cacheDir, slot.cfg.name, hash);
|
|
3471
3531
|
if (cached) {
|
|
@@ -4303,8 +4363,7 @@ function catalogSnapshot(slot) {
|
|
|
4303
4363
|
|
|
4304
4364
|
// src/server.ts
|
|
4305
4365
|
import { createServer } from "node:http";
|
|
4306
|
-
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
4307
|
-
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
4366
|
+
import { expectDefined as expectDefined2, toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
4308
4367
|
var PARSE_ERROR = -32700;
|
|
4309
4368
|
var INVALID_REQUEST = -32600;
|
|
4310
4369
|
var METHOD_NOT_FOUND = -32601;
|
|
@@ -4629,8 +4688,7 @@ function serveHttp(server, opts = {}) {
|
|
|
4629
4688
|
httpServer.once("error", reject);
|
|
4630
4689
|
httpServer.listen(port, host, () => {
|
|
4631
4690
|
httpServer.removeListener("error", reject);
|
|
4632
|
-
const
|
|
4633
|
-
const boundPort = typeof addr === "object" && addr ? addr.port : port;
|
|
4691
|
+
const boundPort = httpServer.address().port;
|
|
4634
4692
|
const displayHost = host === "::1" ? "[::1]" : host;
|
|
4635
4693
|
resolve({
|
|
4636
4694
|
port: boundPort,
|
|
@@ -4662,18 +4720,14 @@ async function handleHttpRequest(server, req, res, token, log) {
|
|
|
4662
4720
|
}
|
|
4663
4721
|
}
|
|
4664
4722
|
let body = "";
|
|
4665
|
-
let tooLarge = false;
|
|
4666
4723
|
req.on("data", (chunk) => {
|
|
4667
|
-
if (tooLarge) return;
|
|
4668
4724
|
body += chunk.toString("utf8");
|
|
4669
4725
|
if (body.length > HTTP_BODY_CAP) {
|
|
4670
|
-
tooLarge = true;
|
|
4671
4726
|
send(413, JSON.stringify({ error: "payload too large" }));
|
|
4672
4727
|
req.destroy();
|
|
4673
4728
|
}
|
|
4674
4729
|
});
|
|
4675
4730
|
req.on("end", () => {
|
|
4676
|
-
if (tooLarge) return;
|
|
4677
4731
|
void server.handleMessage(body).then((out) => {
|
|
4678
4732
|
if (out === null) return send(202, "");
|
|
4679
4733
|
return send(200, out);
|