@wrongstack/mcp 1.0.26 → 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/client.d.ts CHANGED
@@ -26,7 +26,15 @@ export declare class MCPClient {
26
26
  * (stdio child exit, `close()`) so callers don't hang forever.
27
27
  */
28
28
  private readonly pending;
29
- private rxBuffer;
29
+ /**
30
+ * The unterminated tail of stdout, as the chunks it arrived in; joined only
31
+ * when its newline arrives. Kept as one string (`+=` then `indexOf`), every
32
+ * chunk flattened and re-scanned everything buffered so far — O(n²) for a
33
+ * multi-MiB tools/call result.
34
+ */
35
+ private rxParts;
36
+ private get rxBuffer();
37
+ private set rxBuffer(value);
30
38
  private rxBufferBytes;
31
39
  /**
32
40
  * Incremental UTF-8 decoder for the stdio rx path. A pipe read boundary can
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) challenge.resourceMetadataUrl = 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`);
@@ -1543,27 +1552,8 @@ var SSEReader = class {
1543
1552
  };
1544
1553
  }
1545
1554
  feed(chunk) {
1546
- if (chunk.length > SSE_READER_MAX_BUFFER) {
1547
- throw new ToolError({
1548
- message: `SSE: chunk size ${chunk.length} exceeds max buffer ${SSE_READER_MAX_BUFFER} \u2014 refusing to accumulate`,
1549
- code: "TOOL_EXECUTION_FAILED",
1550
- toolName: "mcp_transport_sse_reader",
1551
- context: { phase: "feed", chunkLength: chunk.length, maxBuffer: SSE_READER_MAX_BUFFER }
1552
- });
1553
- }
1555
+ const carried = this.buffer.length;
1554
1556
  this.buffer += chunk;
1555
- if (this.buffer.length > SSE_READER_MAX_BUFFER) {
1556
- throw new ToolError({
1557
- message: `SSE: pending line exceeds ${SSE_READER_MAX_BUFFER} bytes \u2014 upstream is not framing events`,
1558
- code: "TOOL_EXECUTION_FAILED",
1559
- toolName: "mcp_transport_sse_reader",
1560
- context: {
1561
- phase: "feed",
1562
- bufferLength: this.buffer.length,
1563
- maxBuffer: SSE_READER_MAX_BUFFER
1564
- }
1565
- });
1566
- }
1567
1557
  let start = 0;
1568
1558
  let idx = this.buffer.indexOf("\n", start);
1569
1559
  while (idx !== -1) {
@@ -1574,6 +1564,25 @@ var SSEReader = class {
1574
1564
  idx = this.buffer.indexOf("\n", start);
1575
1565
  }
1576
1566
  if (start > 0) this.buffer = this.buffer.slice(start);
1567
+ if (this.buffer.length <= SSE_READER_MAX_BUFFER) return;
1568
+ if (start >= carried) {
1569
+ throw new ToolError({
1570
+ message: `SSE: chunk size ${chunk.length} exceeds max buffer ${SSE_READER_MAX_BUFFER} \u2014 refusing to accumulate`,
1571
+ code: "TOOL_EXECUTION_FAILED",
1572
+ toolName: "mcp_transport_sse_reader",
1573
+ context: { phase: "feed", chunkLength: chunk.length, maxBuffer: SSE_READER_MAX_BUFFER }
1574
+ });
1575
+ }
1576
+ throw new ToolError({
1577
+ message: `SSE: pending line exceeds ${SSE_READER_MAX_BUFFER} bytes \u2014 upstream is not framing events`,
1578
+ code: "TOOL_EXECUTION_FAILED",
1579
+ toolName: "mcp_transport_sse_reader",
1580
+ context: {
1581
+ phase: "feed",
1582
+ bufferLength: this.buffer.length,
1583
+ maxBuffer: SSE_READER_MAX_BUFFER
1584
+ }
1585
+ });
1577
1586
  }
1578
1587
  processLine(line) {
1579
1588
  if (line === "") {
@@ -1649,6 +1658,7 @@ import * as https2 from "node:https";
1649
1658
  import { ConfigError as ConfigError2 } from "@wrongstack/core/types";
1650
1659
 
1651
1660
  // src/elicitation.ts
1661
+ import { sanitizeTerminalText } from "@wrongstack/core/utils";
1652
1662
  var ELICITATION_MAX_WAIT_MS = 10 * 6e4;
1653
1663
  var MAX_FIELDS = 32;
1654
1664
  var MAX_MESSAGE_CHARS = 4e3;
@@ -1659,6 +1669,13 @@ function isRecord(v) {
1659
1669
  function optionalString3(v) {
1660
1670
  return typeof v === "string" && v.trim() ? v : void 0;
1661
1671
  }
1672
+ function displayText(v) {
1673
+ return sanitizeTerminalText(v);
1674
+ }
1675
+ function optionalDisplayString(v) {
1676
+ const text = optionalString3(v);
1677
+ return text === void 0 ? void 0 : optionalString3(displayText(text));
1678
+ }
1662
1679
  function optionalNumber(v) {
1663
1680
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
1664
1681
  }
@@ -1672,7 +1689,7 @@ function parseOptions(schema) {
1672
1689
  const names = Array.isArray(schema["enumNames"]) ? schema["enumNames"] : [];
1673
1690
  return values.map((value, i) => ({
1674
1691
  value,
1675
- label: typeof names[i] === "string" && names[i] ? names[i] : value
1692
+ label: displayText(typeof names[i] === "string" && names[i] ? names[i] : value)
1676
1693
  }));
1677
1694
  }
1678
1695
  const titled = Array.isArray(schema["oneOf"]) ? schema["oneOf"] : Array.isArray(schema["anyOf"]) ? schema["anyOf"] : void 0;
@@ -1682,7 +1699,7 @@ function parseOptions(schema) {
1682
1699
  if (!isRecord(entry) || typeof entry["const"] !== "string") return void 0;
1683
1700
  options.push({
1684
1701
  value: entry["const"],
1685
- label: optionalString3(entry["title"]) ?? entry["const"]
1702
+ label: optionalDisplayString(entry["title"]) ?? displayText(entry["const"])
1686
1703
  });
1687
1704
  }
1688
1705
  return options;
@@ -1691,8 +1708,8 @@ function parseField(name, schema, required) {
1691
1708
  if (!isRecord(schema)) return { ok: false, error: `property "${name}" is not a schema object` };
1692
1709
  const base = {
1693
1710
  name,
1694
- title: optionalString3(schema["title"]),
1695
- description: optionalString3(schema["description"]),
1711
+ title: optionalDisplayString(schema["title"]),
1712
+ description: optionalDisplayString(schema["description"]),
1696
1713
  required
1697
1714
  };
1698
1715
  const type = schema["type"];
@@ -1789,7 +1806,7 @@ function parseUrlElicitation(params) {
1789
1806
  ok: true,
1790
1807
  value: {
1791
1808
  mode: "url",
1792
- message: message.slice(0, MAX_MESSAGE_CHARS),
1809
+ message: displayText(message).slice(0, MAX_MESSAGE_CHARS),
1793
1810
  url: url.href,
1794
1811
  elicitationId
1795
1812
  }
@@ -1822,10 +1839,10 @@ function parseElicitationParams(params) {
1822
1839
  if (!parsed.ok) return parsed;
1823
1840
  fields.push(parsed.value);
1824
1841
  }
1825
- return { ok: true, value: { message: message.slice(0, MAX_MESSAGE_CHARS), fields } };
1842
+ return { ok: true, value: { message: displayText(message).slice(0, MAX_MESSAGE_CHARS), fields } };
1826
1843
  }
1827
1844
  function label(field) {
1828
- return field.title ?? field.name;
1845
+ return field.title ?? displayText(field.name);
1829
1846
  }
1830
1847
  function checkValue(field, raw) {
1831
1848
  const name = label(field);
@@ -1884,7 +1901,7 @@ function validateElicitationContent(fields, content) {
1884
1901
  out[field.name] = [];
1885
1902
  continue;
1886
1903
  }
1887
- if (raw === void 0 || raw === null || raw === "" || Array.isArray(raw) && raw.length === 0) {
1904
+ if (raw === void 0 || raw === null || Array.isArray(raw) && raw.length === 0) {
1888
1905
  if (field.required) return { ok: false, error: `${label(field)} is required` };
1889
1906
  continue;
1890
1907
  }
@@ -2191,6 +2208,14 @@ if (!pinnedAgentsCleanupRegistered) {
2191
2208
  pinnedAgents.clear();
2192
2209
  });
2193
2210
  }
2211
+ function urlOriginForLog(raw) {
2212
+ try {
2213
+ const url = new URL(raw);
2214
+ return `${url.protocol}//${url.host}`;
2215
+ } catch {
2216
+ return "<invalid url>";
2217
+ }
2218
+ }
2194
2219
  function makeAbortError(method) {
2195
2220
  const err = new Error(`MCP request "${method}" aborted by client`);
2196
2221
  err.name = "AbortError";
@@ -2262,15 +2287,16 @@ var BaseHTTPTransport = class {
2262
2287
  this.requestTimeout = opts.requestTimeoutMs ?? 6e4;
2263
2288
  if (opts.tls) {
2264
2289
  if (opts.tls.rejectUnauthorized === false) {
2290
+ const where = urlOriginForLog(this.url);
2265
2291
  if (!isTlsUnsafeAllowed()) {
2266
2292
  throw new ConfigError2({
2267
- message: `[mcp:${transportName}] TLS verification disabled \u2014 set WRONGSTACK_UNSAFE_MCP_TLS=1 to allow. Rejecting insecure configuration for ${this.url}.`,
2293
+ message: `[mcp:${transportName}] TLS verification disabled \u2014 set WRONGSTACK_UNSAFE_MCP_TLS=1 to allow. Rejecting insecure configuration for ${where}.`,
2268
2294
  code: "CONFIG_INVALID",
2269
- context: { field: "tls.rejectUnauthorized", transportName, url: this.url }
2295
+ context: { field: "tls.rejectUnauthorized", transportName, url: where }
2270
2296
  });
2271
2297
  }
2272
2298
  console.error(
2273
- `[mcp:${transportName}] \u26A0\uFE0F TLS verification DISABLED for ${this.url}. Network attacks are possible \u2014 only use on localhost.`
2299
+ `[mcp:${transportName}] \u26A0\uFE0F TLS verification DISABLED for ${where}. Network attacks are possible \u2014 only use on localhost.`
2274
2300
  );
2275
2301
  }
2276
2302
  this.tlsAgent = new https2.Agent({
@@ -3581,7 +3607,19 @@ var MCPClient = class _MCPClient {
3581
3607
  * (stdio child exit, `close()`) so callers don't hang forever.
3582
3608
  */
3583
3609
  pending = /* @__PURE__ */ new Map();
3584
- rxBuffer = "";
3610
+ /**
3611
+ * The unterminated tail of stdout, as the chunks it arrived in; joined only
3612
+ * when its newline arrives. Kept as one string (`+=` then `indexOf`), every
3613
+ * chunk flattened and re-scanned everything buffered so far — O(n²) for a
3614
+ * multi-MiB tools/call result.
3615
+ */
3616
+ rxParts = [];
3617
+ get rxBuffer() {
3618
+ return this.rxParts.join("");
3619
+ }
3620
+ set rxBuffer(value) {
3621
+ this.rxParts = value ? [value] : [];
3622
+ }
3585
3623
  rxBufferBytes = 0;
3586
3624
  /**
3587
3625
  * Incremental UTF-8 decoder for the stdio rx path. A pipe read boundary can
@@ -4063,11 +4101,10 @@ var MCPClient = class _MCPClient {
4063
4101
  }
4064
4102
  }
4065
4103
  onData(s) {
4066
- this.rxBuffer += s;
4067
4104
  this.rxBufferBytes += Buffer.byteLength(s, "utf8");
4068
4105
  if (this.rxBufferBytes > _MCPClient.MAX_RX_BUFFER_BYTES) {
4069
4106
  const truncated = this.rxBufferBytes;
4070
- this.rxBuffer = "";
4107
+ this.rxParts = [];
4071
4108
  this.rxBufferBytes = 0;
4072
4109
  this.failPending(
4073
4110
  `MCP "${this.opts.name}" rx buffer overflow (${truncated} bytes without a newline) \u2014 closing connection`
@@ -4076,17 +4113,22 @@ var MCPClient = class _MCPClient {
4076
4113
  return;
4077
4114
  }
4078
4115
  let start = 0;
4079
- let idx = this.rxBuffer.indexOf("\n");
4116
+ let idx = s.indexOf("\n");
4117
+ if (idx === -1) {
4118
+ this.rxParts.push(s);
4119
+ return;
4120
+ }
4080
4121
  while (idx !== -1) {
4081
- const line = this.rxBuffer.slice(start, idx).trim();
4122
+ const head = this.rxParts.length > 0 ? this.rxParts.join("") : "";
4123
+ this.rxParts = [];
4124
+ const line = (head + s.slice(start, idx)).trim();
4082
4125
  start = idx + 1;
4083
4126
  if (line) this.onLine(line);
4084
- idx = this.rxBuffer.indexOf("\n", start);
4085
- }
4086
- if (start > 0) {
4087
- this.rxBufferBytes -= Buffer.byteLength(this.rxBuffer.slice(0, start), "utf8");
4088
- this.rxBuffer = this.rxBuffer.slice(start);
4127
+ idx = s.indexOf("\n", start);
4089
4128
  }
4129
+ const tail = s.slice(start);
4130
+ this.rxBufferBytes = Buffer.byteLength(tail, "utf8");
4131
+ if (tail) this.rxParts.push(tail);
4090
4132
  }
4091
4133
  onLine(line) {
4092
4134
  let msg;
@@ -4424,6 +4466,7 @@ function requireIdentity(value, label2) {
4424
4466
  // src/elicitation-form.ts
4425
4467
  import { randomUUID } from "node:crypto";
4426
4468
  import { hostname } from "node:os";
4469
+ import { sanitizeTerminalText as sanitizeTerminalText2 } from "@wrongstack/core/utils";
4427
4470
  var MAX_ATTEMPTS = 3;
4428
4471
  var CONFIRM_QUESTION = "confirm";
4429
4472
  var URL_QUESTION = "url";
@@ -4456,7 +4499,8 @@ function describe(field) {
4456
4499
  function toQuestion(field, index) {
4457
4500
  const base = {
4458
4501
  id: questionId(index),
4459
- prompt: field.title ?? field.name,
4502
+ // Titles arrive sanitized from the parser; a bare field name does not.
4503
+ prompt: field.title ?? sanitizeTerminalText2(field.name),
4460
4504
  description: describe(field),
4461
4505
  required: field.required
4462
4506
  };
@@ -4572,8 +4616,12 @@ function toUserInputRequest(request3, problem) {
4572
4616
  function answerValue(field, answer) {
4573
4617
  if (!answer || answer.delegated) return void 0;
4574
4618
  switch (field.kind) {
4575
- case "string":
4576
- return answer.text?.trim() ? answer.text : void 0;
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
+ }
4577
4625
  case "number":
4578
4626
  case "integer":
4579
4627
  return answer.text?.trim() || void 0;
@@ -4660,8 +4708,14 @@ async function readConfig(path2) {
4660
4708
  async function writeConfig(path2, cfg) {
4661
4709
  const raw = JSON.stringify(cfg, null, 2);
4662
4710
  const tmp = `${path2}.${process.pid}.${randomBytes3(6).toString("hex")}.tmp`;
4663
- await fs.writeFile(tmp, raw, "utf8");
4711
+ let mode = 384;
4712
+ try {
4713
+ mode = (await fs.stat(path2)).mode & 511;
4714
+ } catch {
4715
+ }
4664
4716
  try {
4717
+ await fs.writeFile(tmp, raw, { encoding: "utf8", mode });
4718
+ await fs.chmod(tmp, mode);
4665
4719
  await fs.rename(tmp, path2);
4666
4720
  } catch (err) {
4667
4721
  await fs.rm(tmp, { force: true });
@@ -4733,6 +4787,10 @@ function buildConfig(input, base) {
4733
4787
  if (allowedTools !== void 0) cfg.allowedTools = allowedTools;
4734
4788
  const permission = input.permission ?? base?.permission;
4735
4789
  if (permission !== void 0) cfg.permission = permission;
4790
+ const startupTimeoutMs = input.startupTimeoutMs ?? base?.startupTimeoutMs;
4791
+ if (startupTimeoutMs !== void 0) cfg.startupTimeoutMs = startupTimeoutMs;
4792
+ const requestTimeoutMs = input.requestTimeoutMs ?? base?.requestTimeoutMs;
4793
+ if (requestTimeoutMs !== void 0) cfg.requestTimeoutMs = requestTimeoutMs;
4736
4794
  const allowPrivateNetworks = input.allowPrivateNetworks ?? base?.allowPrivateNetworks;
4737
4795
  if (allowPrivateNetworks !== void 0) cfg.allowPrivateNetworks = allowPrivateNetworks;
4738
4796
  const enabled = input.enabled ?? base?.enabled;
@@ -5091,52 +5149,59 @@ function healthStateFor(connectionState, operations, enabled = true) {
5091
5149
  function evaluateHealthThresholds(operations, thresholds) {
5092
5150
  if (!thresholds) return [];
5093
5151
  const checks = [];
5094
- if (thresholds.connectionLatencyP95Ms !== void 0 && operations.connectionSamples.length > 0) {
5152
+ const connectionP95 = thresholds.connectionLatencyP95Ms;
5153
+ if (isUsableThreshold(connectionP95) && operations.connectionSamples.length > 0) {
5095
5154
  const value = percentile(
5096
5155
  [...operations.connectionSamples].sort((a, b) => a - b),
5097
5156
  0.95
5098
5157
  );
5099
5158
  checks.push({
5100
5159
  name: "connection-latency-p95",
5101
- passed: value <= thresholds.connectionLatencyP95Ms,
5160
+ passed: value <= connectionP95,
5102
5161
  value,
5103
- threshold: thresholds.connectionLatencyP95Ms
5162
+ threshold: connectionP95
5104
5163
  });
5105
5164
  }
5106
- if (thresholds.discoveryLatencyP95Ms !== void 0 && operations.discoverySamples.length > 0) {
5165
+ const discoveryP95 = thresholds.discoveryLatencyP95Ms;
5166
+ if (isUsableThreshold(discoveryP95) && operations.discoverySamples.length > 0) {
5107
5167
  const value = percentile(
5108
5168
  [...operations.discoverySamples].sort((a, b) => a - b),
5109
5169
  0.95
5110
5170
  );
5111
5171
  checks.push({
5112
5172
  name: "discovery-latency-p95",
5113
- passed: value <= thresholds.discoveryLatencyP95Ms,
5173
+ passed: value <= discoveryP95,
5114
5174
  value,
5115
- threshold: thresholds.discoveryLatencyP95Ms
5175
+ threshold: discoveryP95
5116
5176
  });
5117
5177
  }
5118
- if (thresholds.callLatencyP95Ms !== void 0 && operations.callSamples.length > 0) {
5178
+ const callP95 = thresholds.callLatencyP95Ms;
5179
+ if (isUsableThreshold(callP95) && operations.callSamples.length > 0) {
5119
5180
  const value = percentile(
5120
5181
  [...operations.callSamples].sort((a, b) => a - b),
5121
5182
  0.95
5122
5183
  );
5123
5184
  checks.push({
5124
5185
  name: "call-latency-p95",
5125
- passed: value <= thresholds.callLatencyP95Ms,
5186
+ passed: value <= callP95,
5126
5187
  value,
5127
- threshold: thresholds.callLatencyP95Ms
5188
+ threshold: callP95
5128
5189
  });
5129
5190
  }
5130
- if (thresholds.inFlightCalls !== void 0) {
5191
+ const inFlight = thresholds.inFlightCalls;
5192
+ if (isUsableThreshold(inFlight)) {
5131
5193
  checks.push({
5132
5194
  name: "in-flight-calls",
5133
- passed: operations.peakInFlightCalls <= thresholds.inFlightCalls,
5195
+ passed: operations.peakInFlightCalls <= inFlight,
5134
5196
  value: operations.peakInFlightCalls,
5135
- threshold: thresholds.inFlightCalls
5197
+ threshold: inFlight
5136
5198
  });
5137
5199
  }
5138
5200
  return checks;
5139
5201
  }
5202
+ function isUsableThreshold(value) {
5203
+ return typeof value === "number" && Number.isFinite(value);
5204
+ }
5140
5205
  function applyHealthThresholds(state, checks) {
5141
5206
  if (state !== "healthy") return state;
5142
5207
  return checks.some((c) => !c.passed) ? "degraded" : "healthy";
@@ -5648,7 +5713,8 @@ async function attemptConnectSlot(ctx, slot) {
5648
5713
  slot.reconnectPending = false;
5649
5714
  ctx.events.emit("mcp.server.disconnected", {
5650
5715
  name: slot.cfg.name,
5651
- reason: err instanceof Error ? err.message : "unknown"
5716
+ reason: err instanceof Error ? err.message : "unknown",
5717
+ terminal: true
5652
5718
  });
5653
5719
  return;
5654
5720
  }
@@ -5869,7 +5935,8 @@ function scheduleRegistryReconnect({
5869
5935
  );
5870
5936
  events.emit("mcp.server.disconnected", {
5871
5937
  name: slot.cfg.name,
5872
- reason: `reconnect-exhausted:${slot.reconnectCycles}`
5938
+ reason: `reconnect-exhausted:${slot.reconnectCycles}`,
5939
+ terminal: true
5873
5940
  });
5874
5941
  return;
5875
5942
  }
@@ -6861,6 +6928,7 @@ function serveStdio(server, opts = {}) {
6861
6928
  let bufferBytes = 0;
6862
6929
  let closed = false;
6863
6930
  let bufferTooLarge = false;
6931
+ const decoder = new TextDecoder();
6864
6932
  let writeChain = Promise.resolve();
6865
6933
  const inFlightHandlers = /* @__PURE__ */ new Set();
6866
6934
  const writeLine = (s) => {
@@ -6883,7 +6951,7 @@ function serveStdio(server, opts = {}) {
6883
6951
  };
6884
6952
  const onData = (chunk) => {
6885
6953
  if (bufferTooLarge) return;
6886
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
6954
+ const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
6887
6955
  buffer += text;
6888
6956
  bufferBytes += Buffer.byteLength(text, "utf8");
6889
6957
  if (bufferBytes > HTTP_BODY_CAP) {
@@ -7174,8 +7242,8 @@ var MCPVaultTokenStore = class {
7174
7242
  async readFile() {
7175
7243
  let raw;
7176
7244
  try {
7177
- const stat2 = await fs3.stat(this.filePath);
7178
- if (stat2.size > MAX_STORE_BYTES) throw new Error("MCP token store exceeds size limit");
7245
+ const stat3 = await fs3.stat(this.filePath);
7246
+ if (stat3.size > MAX_STORE_BYTES) throw new Error("MCP token store exceeds size limit");
7179
7247
  raw = await fs3.readFile(this.filePath, "utf8");
7180
7248
  } catch (error) {
7181
7249
  if (error.code === "ENOENT") return emptyFile();
package/dist/manage.d.ts CHANGED
@@ -17,6 +17,10 @@ export interface McpServerInput {
17
17
  bearerTokenEnv?: string | undefined;
18
18
  allowedTools?: string[] | undefined;
19
19
  permission?: Permission | undefined;
20
+ /** Connect/startup timeout in ms. Defaults to 10s when omitted. */
21
+ startupTimeoutMs?: number | undefined;
22
+ /** Per-request JSON-RPC timeout in ms. Defaults to 60s when omitted. */
23
+ requestTimeoutMs?: number | undefined;
20
24
  /** Lazy connect — spawn the process only on first tool call (see config). */
21
25
  lazy?: boolean | undefined;
22
26
  /** Env var names to forward from parent process at spawn time. */
@@ -51,6 +51,11 @@ export interface HttpTransportOptions {
51
51
  * Abort error whose `name` is `'AbortError'` so the core executor's
52
52
  * classifyToolError maps it to FATAL / not-retryable (user cancellation).
53
53
  */
54
+ /**
55
+ * `scheme://host:port` of a server URL, for log lines. The path and query can
56
+ * hold an expanded `${SECRET}` (config-env), and userinfo is a credential.
57
+ */
58
+ export declare function urlOriginForLog(raw: string): string;
54
59
  export declare function makeAbortError(method: string): Error;
55
60
  /** Advance a JSON-RPC number id without leaving JavaScript's safe-integer range. */
56
61
  export declare function nextJsonRpcId(id: number): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/mcp",
3
- "version": "1.0.26",
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.26",
29
+ "@wrongstack/core": "1.0.28",
30
30
  "undici": "^8.10.2"
31
31
  },
32
32
  "devDependencies": {