@wrongstack/mcp 1.0.27 → 1.0.28
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/index.js +47 -20
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45,7 +45,9 @@ function parseMcpBearerChallenge(header, resource) {
|
|
|
45
45
|
const resourceMetadata = challengeParameter(parameters, "resource_metadata");
|
|
46
46
|
if (resourceMetadata) {
|
|
47
47
|
const metadataUrl = validateMetadataUrl(resourceMetadata);
|
|
48
|
-
if (metadataUrl
|
|
48
|
+
if (metadataUrl && isSameHost(metadataUrl, resource)) {
|
|
49
|
+
challenge.resourceMetadataUrl = metadataUrl;
|
|
50
|
+
}
|
|
49
51
|
}
|
|
50
52
|
const scope = challengeParameter(parameters, "scope");
|
|
51
53
|
if (scope) {
|
|
@@ -194,6 +196,13 @@ function validateMetadataUrl(value) {
|
|
|
194
196
|
return void 0;
|
|
195
197
|
}
|
|
196
198
|
}
|
|
199
|
+
function isSameHost(candidate, resource) {
|
|
200
|
+
try {
|
|
201
|
+
return unbracket(new URL(candidate).hostname).toLowerCase() === unbracket(new URL(resource).hostname).toLowerCase();
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
197
206
|
function record(value, label2) {
|
|
198
207
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
199
208
|
throw new Error(`MCP ${label2} must be an object`);
|
|
@@ -1892,7 +1901,7 @@ function validateElicitationContent(fields, content) {
|
|
|
1892
1901
|
out[field.name] = [];
|
|
1893
1902
|
continue;
|
|
1894
1903
|
}
|
|
1895
|
-
if (raw === void 0 || raw === null ||
|
|
1904
|
+
if (raw === void 0 || raw === null || Array.isArray(raw) && raw.length === 0) {
|
|
1896
1905
|
if (field.required) return { ok: false, error: `${label(field)} is required` };
|
|
1897
1906
|
continue;
|
|
1898
1907
|
}
|
|
@@ -4607,8 +4616,12 @@ function toUserInputRequest(request3, problem) {
|
|
|
4607
4616
|
function answerValue(field, answer) {
|
|
4608
4617
|
if (!answer || answer.delegated) return void 0;
|
|
4609
4618
|
switch (field.kind) {
|
|
4610
|
-
case "string":
|
|
4611
|
-
|
|
4619
|
+
case "string": {
|
|
4620
|
+
const text = answer.text;
|
|
4621
|
+
if (text === void 0) return void 0;
|
|
4622
|
+
if (!text.trim() && !field.required) return void 0;
|
|
4623
|
+
return text;
|
|
4624
|
+
}
|
|
4612
4625
|
case "number":
|
|
4613
4626
|
case "integer":
|
|
4614
4627
|
return answer.text?.trim() || void 0;
|
|
@@ -4695,8 +4708,14 @@ async function readConfig(path2) {
|
|
|
4695
4708
|
async function writeConfig(path2, cfg) {
|
|
4696
4709
|
const raw = JSON.stringify(cfg, null, 2);
|
|
4697
4710
|
const tmp = `${path2}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
|
|
4698
|
-
|
|
4711
|
+
let mode = 384;
|
|
4712
|
+
try {
|
|
4713
|
+
mode = (await fs.stat(path2)).mode & 511;
|
|
4714
|
+
} catch {
|
|
4715
|
+
}
|
|
4699
4716
|
try {
|
|
4717
|
+
await fs.writeFile(tmp, raw, { encoding: "utf8", mode });
|
|
4718
|
+
await fs.chmod(tmp, mode);
|
|
4700
4719
|
await fs.rename(tmp, path2);
|
|
4701
4720
|
} catch (err) {
|
|
4702
4721
|
await fs.rm(tmp, { force: true });
|
|
@@ -5130,52 +5149,59 @@ function healthStateFor(connectionState, operations, enabled = true) {
|
|
|
5130
5149
|
function evaluateHealthThresholds(operations, thresholds) {
|
|
5131
5150
|
if (!thresholds) return [];
|
|
5132
5151
|
const checks = [];
|
|
5133
|
-
|
|
5152
|
+
const connectionP95 = thresholds.connectionLatencyP95Ms;
|
|
5153
|
+
if (isUsableThreshold(connectionP95) && operations.connectionSamples.length > 0) {
|
|
5134
5154
|
const value = percentile(
|
|
5135
5155
|
[...operations.connectionSamples].sort((a, b) => a - b),
|
|
5136
5156
|
0.95
|
|
5137
5157
|
);
|
|
5138
5158
|
checks.push({
|
|
5139
5159
|
name: "connection-latency-p95",
|
|
5140
|
-
passed: value <=
|
|
5160
|
+
passed: value <= connectionP95,
|
|
5141
5161
|
value,
|
|
5142
|
-
threshold:
|
|
5162
|
+
threshold: connectionP95
|
|
5143
5163
|
});
|
|
5144
5164
|
}
|
|
5145
|
-
|
|
5165
|
+
const discoveryP95 = thresholds.discoveryLatencyP95Ms;
|
|
5166
|
+
if (isUsableThreshold(discoveryP95) && operations.discoverySamples.length > 0) {
|
|
5146
5167
|
const value = percentile(
|
|
5147
5168
|
[...operations.discoverySamples].sort((a, b) => a - b),
|
|
5148
5169
|
0.95
|
|
5149
5170
|
);
|
|
5150
5171
|
checks.push({
|
|
5151
5172
|
name: "discovery-latency-p95",
|
|
5152
|
-
passed: value <=
|
|
5173
|
+
passed: value <= discoveryP95,
|
|
5153
5174
|
value,
|
|
5154
|
-
threshold:
|
|
5175
|
+
threshold: discoveryP95
|
|
5155
5176
|
});
|
|
5156
5177
|
}
|
|
5157
|
-
|
|
5178
|
+
const callP95 = thresholds.callLatencyP95Ms;
|
|
5179
|
+
if (isUsableThreshold(callP95) && operations.callSamples.length > 0) {
|
|
5158
5180
|
const value = percentile(
|
|
5159
5181
|
[...operations.callSamples].sort((a, b) => a - b),
|
|
5160
5182
|
0.95
|
|
5161
5183
|
);
|
|
5162
5184
|
checks.push({
|
|
5163
5185
|
name: "call-latency-p95",
|
|
5164
|
-
passed: value <=
|
|
5186
|
+
passed: value <= callP95,
|
|
5165
5187
|
value,
|
|
5166
|
-
threshold:
|
|
5188
|
+
threshold: callP95
|
|
5167
5189
|
});
|
|
5168
5190
|
}
|
|
5169
|
-
|
|
5191
|
+
const inFlight = thresholds.inFlightCalls;
|
|
5192
|
+
if (isUsableThreshold(inFlight)) {
|
|
5170
5193
|
checks.push({
|
|
5171
5194
|
name: "in-flight-calls",
|
|
5172
|
-
passed: operations.peakInFlightCalls <=
|
|
5195
|
+
passed: operations.peakInFlightCalls <= inFlight,
|
|
5173
5196
|
value: operations.peakInFlightCalls,
|
|
5174
|
-
threshold:
|
|
5197
|
+
threshold: inFlight
|
|
5175
5198
|
});
|
|
5176
5199
|
}
|
|
5177
5200
|
return checks;
|
|
5178
5201
|
}
|
|
5202
|
+
function isUsableThreshold(value) {
|
|
5203
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
5204
|
+
}
|
|
5179
5205
|
function applyHealthThresholds(state, checks) {
|
|
5180
5206
|
if (state !== "healthy") return state;
|
|
5181
5207
|
return checks.some((c) => !c.passed) ? "degraded" : "healthy";
|
|
@@ -6902,6 +6928,7 @@ function serveStdio(server, opts = {}) {
|
|
|
6902
6928
|
let bufferBytes = 0;
|
|
6903
6929
|
let closed = false;
|
|
6904
6930
|
let bufferTooLarge = false;
|
|
6931
|
+
const decoder = new TextDecoder();
|
|
6905
6932
|
let writeChain = Promise.resolve();
|
|
6906
6933
|
const inFlightHandlers = /* @__PURE__ */ new Set();
|
|
6907
6934
|
const writeLine = (s) => {
|
|
@@ -6924,7 +6951,7 @@ function serveStdio(server, opts = {}) {
|
|
|
6924
6951
|
};
|
|
6925
6952
|
const onData = (chunk) => {
|
|
6926
6953
|
if (bufferTooLarge) return;
|
|
6927
|
-
const text = typeof chunk === "string" ? chunk :
|
|
6954
|
+
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
6928
6955
|
buffer += text;
|
|
6929
6956
|
bufferBytes += Buffer.byteLength(text, "utf8");
|
|
6930
6957
|
if (bufferBytes > HTTP_BODY_CAP) {
|
|
@@ -7215,8 +7242,8 @@ var MCPVaultTokenStore = class {
|
|
|
7215
7242
|
async readFile() {
|
|
7216
7243
|
let raw;
|
|
7217
7244
|
try {
|
|
7218
|
-
const
|
|
7219
|
-
if (
|
|
7245
|
+
const stat3 = await fs3.stat(this.filePath);
|
|
7246
|
+
if (stat3.size > MAX_STORE_BYTES) throw new Error("MCP token store exceeds size limit");
|
|
7220
7247
|
raw = await fs3.readFile(this.filePath, "utf8");
|
|
7221
7248
|
} catch (error) {
|
|
7222
7249
|
if (error.code === "ENOENT") return emptyFile();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack Model Context Protocol client and registry: stdio, SSE, and streamable HTTP transports.",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"!dist/**/*.map"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@wrongstack/core": "1.0.
|
|
29
|
+
"@wrongstack/core": "1.0.28",
|
|
30
30
|
"undici": "^8.10.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|