@wrongstack/mcp 0.282.0 → 0.282.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/index.d.ts +10 -0
- package/dist/index.js +70 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,14 @@ interface MCPClientOptions {
|
|
|
12
12
|
headers?: Record<string, string> | undefined;
|
|
13
13
|
startupTimeoutMs?: number | undefined;
|
|
14
14
|
requestTimeoutMs?: number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Allowlist of env var names to forward from the parent process (process.env)
|
|
17
|
+
* to the child. Values are resolved at spawn time and merged into `env`
|
|
18
|
+
* via the `extra` path of `buildChildEnv` (unfiltered). This is how built-in
|
|
19
|
+
* MCP server presets (GitHub, Slack, Brave Search, …) get their API tokens
|
|
20
|
+
* without storing them in config.json or being scrubbed by the secret filter.
|
|
21
|
+
*/
|
|
22
|
+
passthroughEnv?: string[] | undefined;
|
|
15
23
|
}
|
|
16
24
|
type ConnectionState = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'reconnecting' | 'failed'
|
|
17
25
|
/** Lazy server: registered from a cached manifest, process not spawned. */
|
|
@@ -291,6 +299,8 @@ interface McpServerInput {
|
|
|
291
299
|
permission?: Permission | undefined;
|
|
292
300
|
/** Lazy connect — spawn the process only on first tool call (see config). */
|
|
293
301
|
lazy?: boolean | undefined;
|
|
302
|
+
/** Env var names to forward from parent process at spawn time. */
|
|
303
|
+
passthroughEnv?: string[] | undefined;
|
|
294
304
|
}
|
|
295
305
|
/** Projected view of one server, merging disk config with live registry state. */
|
|
296
306
|
interface McpServerInfo {
|
package/dist/index.js
CHANGED
|
@@ -624,30 +624,38 @@ var SSETransport = class extends BaseHTTPTransport {
|
|
|
624
624
|
signal: timeoutSignal.signal
|
|
625
625
|
};
|
|
626
626
|
this.applyTlsAgent(fetchOpts);
|
|
627
|
-
const res = await fetch(this.url, fetchOpts);
|
|
628
|
-
if (!res.ok) {
|
|
629
|
-
throw new ToolError({
|
|
630
|
-
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
631
|
-
code: "TOOL_EXECUTION_FAILED",
|
|
632
|
-
toolName: method,
|
|
633
|
-
context: { transport: "sse", url: this.url, status: res.status, statusText: res.statusText }
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
let data;
|
|
637
627
|
try {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
628
|
+
const res = await fetch(this.url, fetchOpts);
|
|
629
|
+
if (!res.ok) {
|
|
630
|
+
throw new ToolError({
|
|
631
|
+
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
632
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
633
|
+
toolName: method,
|
|
634
|
+
context: {
|
|
635
|
+
transport: "sse",
|
|
636
|
+
url: this.url,
|
|
637
|
+
status: res.status,
|
|
638
|
+
statusText: res.statusText
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
let data;
|
|
643
|
+
try {
|
|
644
|
+
data = await res.json();
|
|
645
|
+
} catch (err) {
|
|
646
|
+
throw new ToolError({
|
|
647
|
+
message: `Invalid JSON-RPC response: ${err instanceof Error ? err.message : "parse failed"}`,
|
|
648
|
+
code: "TOOL_EXECUTION_FAILED",
|
|
649
|
+
toolName: method,
|
|
650
|
+
context: { transport: "sse", url: this.url, phase: "parse-json" },
|
|
651
|
+
cause: err
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
const result = assertMatchingJsonRpcResult(data, id, method);
|
|
655
|
+
return { jsonrpc: "2.0", id, result: result.result, error: result.error };
|
|
656
|
+
} finally {
|
|
657
|
+
timeoutSignal.dispose();
|
|
647
658
|
}
|
|
648
|
-
const result = assertMatchingJsonRpcResult(data, id, method);
|
|
649
|
-
timeoutSignal.dispose();
|
|
650
|
-
return { jsonrpc: "2.0", id, result: result.result, error: result.error };
|
|
651
659
|
}
|
|
652
660
|
async close() {
|
|
653
661
|
if (this.state === "disconnected") return;
|
|
@@ -916,9 +924,18 @@ var MCPClient = class {
|
|
|
916
924
|
throw new Error('MCP stdio transport requires "command"');
|
|
917
925
|
}
|
|
918
926
|
this.rxBuffer = "";
|
|
927
|
+
const extraEnv = { ...this.opts.env };
|
|
928
|
+
if (this.opts.passthroughEnv) {
|
|
929
|
+
for (const name of this.opts.passthroughEnv) {
|
|
930
|
+
const val = process.env[name];
|
|
931
|
+
if (val !== void 0) {
|
|
932
|
+
extraEnv[name] = val;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
919
936
|
const isWin = process.platform === "win32";
|
|
920
937
|
const rawArgs = this.opts.args ?? [];
|
|
921
|
-
const spawnEnv = buildChildEnv({ extra:
|
|
938
|
+
const spawnEnv = buildChildEnv({ extra: extraEnv });
|
|
922
939
|
const stdio = ["pipe", "pipe", "pipe"];
|
|
923
940
|
const child = isWin ? spawn([this.opts.command, ...rawArgs].map(quoteWindowsArg).join(" "), {
|
|
924
941
|
env: spawnEnv,
|
|
@@ -944,8 +961,9 @@ var MCPClient = class {
|
|
|
944
961
|
}
|
|
945
962
|
}
|
|
946
963
|
});
|
|
947
|
-
child.on("error", () => {
|
|
964
|
+
child.on("error", (err) => {
|
|
948
965
|
this.state = "failed";
|
|
966
|
+
this.failPending(`MCP "${this.opts.name}" child error: ${toErrorMessage(err)}`);
|
|
949
967
|
});
|
|
950
968
|
const initialize = await this.request(
|
|
951
969
|
"initialize",
|
|
@@ -1145,8 +1163,16 @@ var MCPClient = class {
|
|
|
1145
1163
|
},
|
|
1146
1164
|
timer
|
|
1147
1165
|
});
|
|
1166
|
+
const stdin = this.child?.stdin;
|
|
1167
|
+
if (!stdin || stdin.destroyed) {
|
|
1168
|
+
const pending = this.pending.get(id);
|
|
1169
|
+
this.pending.delete(id);
|
|
1170
|
+
if (pending) clearTimeout(pending.timer);
|
|
1171
|
+
reject(new Error(`MCP "${this.opts.name}" request "${method}": stdin not writable`));
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1148
1174
|
try {
|
|
1149
|
-
|
|
1175
|
+
stdin.write(JSON.stringify(req) + "\n");
|
|
1150
1176
|
} catch (err) {
|
|
1151
1177
|
const pending = this.pending.get(id);
|
|
1152
1178
|
this.pending.delete(id);
|
|
@@ -1181,7 +1207,14 @@ var MCPClient = class {
|
|
|
1181
1207
|
if (this._drainPending) {
|
|
1182
1208
|
this._lastNotifySkipped = true;
|
|
1183
1209
|
console.warn(
|
|
1184
|
-
|
|
1210
|
+
JSON.stringify({
|
|
1211
|
+
level: "warn",
|
|
1212
|
+
event: "mcp.notify_skipped_backpressure",
|
|
1213
|
+
server: this.opts.name,
|
|
1214
|
+
method,
|
|
1215
|
+
message: "stdin buffer backpressure (already waiting for drain)",
|
|
1216
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1217
|
+
})
|
|
1185
1218
|
);
|
|
1186
1219
|
return;
|
|
1187
1220
|
}
|
|
@@ -1813,7 +1846,8 @@ var MCPRegistry = class _MCPRegistry {
|
|
|
1813
1846
|
url: slot.cfg.url,
|
|
1814
1847
|
headers: slot.cfg.headers,
|
|
1815
1848
|
startupTimeoutMs: slot.cfg.startupTimeoutMs,
|
|
1816
|
-
requestTimeoutMs: slot.cfg.requestTimeoutMs
|
|
1849
|
+
requestTimeoutMs: slot.cfg.requestTimeoutMs,
|
|
1850
|
+
passthroughEnv: slot.cfg.passthroughEnv
|
|
1817
1851
|
});
|
|
1818
1852
|
if (slot.cfg.transport === "stdio") {
|
|
1819
1853
|
client.addExitListener(this.onChildExit);
|
|
@@ -1897,9 +1931,14 @@ async function readConfig(path2) {
|
|
|
1897
1931
|
}
|
|
1898
1932
|
async function writeConfig(path2, cfg) {
|
|
1899
1933
|
const raw = JSON.stringify(cfg, null, 2);
|
|
1900
|
-
const tmp = `${path2}.tmp`;
|
|
1934
|
+
const tmp = `${path2}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
1901
1935
|
await fs.writeFile(tmp, raw, "utf8");
|
|
1902
|
-
|
|
1936
|
+
try {
|
|
1937
|
+
await fs.rename(tmp, path2);
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
await fs.rm(tmp, { force: true }).catch(() => void 0);
|
|
1940
|
+
throw err;
|
|
1941
|
+
}
|
|
1903
1942
|
}
|
|
1904
1943
|
function isMcpServerRecord(value) {
|
|
1905
1944
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -1943,6 +1982,8 @@ function buildConfig(input, base) {
|
|
|
1943
1982
|
if (enabled !== void 0) cfg.enabled = enabled;
|
|
1944
1983
|
const lazy = input.lazy ?? base?.lazy;
|
|
1945
1984
|
if (lazy !== void 0) cfg.lazy = lazy;
|
|
1985
|
+
const passthroughEnv = input.passthroughEnv ?? base?.passthroughEnv;
|
|
1986
|
+
if (passthroughEnv !== void 0) cfg.passthroughEnv = passthroughEnv;
|
|
1946
1987
|
return cfg;
|
|
1947
1988
|
}
|
|
1948
1989
|
function projectServer(name, cfg, registry) {
|