// bridgeStdio.ts – only the parts that change
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerBridgedTools } from "./bridgeUtils.js";
/* ───────── minimal WHITELISTED env ───────── */
function minimalEnv(extra: Record<string, string>) {
const baseVars = [
// ─ core runtime ─
"PATH", "HOME", "USERPROFILE", "TEMP", "TMP",
// ─ npm / npx on Windows ─
"APPDATA", "LOCALAPPDATA"
];
const env: Record<string, string> = {};
for (const k of baseVars) {
if (process.env[k]) env[k] = process.env[k] as string;
}
return { ...env, ...extra };
}
/* ───────── bridge function ───────── */
export async function bridgeStdio(
hub: McpServer,
command: string,
args: string[],
prefix: string, // e.g. "brave"
omit?: string[] // tools to omit from registration
) {
const envVar = `${prefix.toUpperCase()}_API_KEY`; // "BRAVE_API_KEY"
const apiKey = process.env[envVar] ?? "";
const transport = new StdioClientTransport({
command,
args,
env: minimalEnv({
[envVar]: apiKey, // **only** this secret
DEBUG: `${prefix}:*`, // remote logging (optional)
NODE_DEBUG: "undici" // (optional) raw HTTP traces
}),
// 👇 makes the child’s stdout/stderr go straight to the hub’s console
});
console.error(`[bridge] ${envVar} in child:`, apiKey ? "(set)" : "(empty)");
const client = new Client({
name: `bridge-${prefix}`,
version: "0.1",
requestTimeout: 30_000 // 30 s
});
await client.connect(transport);
console.error("transport", transport);
// ── hook up logs *once* ─────────────────────────────────────────────
const child = (transport as unknown as { _process?: import("child_process").ChildProcess })._process;
if (child) {
if (child.stdout) child.stdout.pipe(process.stdout);
if (child.stderr) child.stderr.pipe(process.stderr);
console.error(`[bridge] child PID ${child.pid}`);
} else {
console.warn("[bridge] no child process found – nothing to pipe");
}
const { tools } = await client.listTools().catch(() => ({ tools: [] }));
const { prompts } = await client.listPrompts().catch(() => ({ prompts: [] }));
registerBridgedTools(hub, client, tools, prefix, omit);
console.error(`🔗 bridged stdio "${command} ${args.join(" ")}" → "${prefix}/…" (omitted: ${omit?.join(', ') || 'none'})`);
}