diff --git a/src/agents/mcp-sse.ts b/src/agents/mcp-sse.ts index 86c50759105..ec8936dcffa 100644 --- a/src/agents/mcp-sse.ts +++ b/src/agents/mcp-sse.ts @@ -35,7 +35,10 @@ function toStringRecord( export function resolveSseMcpServerLaunchConfig( raw: unknown, - options?: { onDroppedHeader?: (key: string, value: unknown) => void }, + options?: { + onDroppedHeader?: (key: string, value: unknown) => void; + onMalformedHeaders?: (value: unknown) => void; + }, ): SseMcpServerLaunchResult { if (!isRecord(raw)) { return { ok: false, reason: "server config must be an object" }; @@ -56,17 +59,49 @@ export function resolveSseMcpServerLaunchConfig( reason: `only http and https URLs are supported, got ${parsed.protocol}`, }; } + // Warn if headers is present but not an object (e.g. a string or array). + let headers: Record | undefined; + if (raw.headers !== undefined && raw.headers !== null) { + if (!isRecord(raw.headers)) { + options?.onMalformedHeaders?.(raw.headers); + } else { + headers = toStringRecord(raw.headers, options?.onDroppedHeader); + } + } return { ok: true, config: { url, - headers: toStringRecord(raw.headers, options?.onDroppedHeader), + headers, }, }; } export function describeSseMcpServerLaunchConfig(config: SseMcpServerLaunchConfig): string { - return config.url; + try { + const parsed = new URL(config.url); + // Redact embedded credentials and query-token auth from log/description output. + if (parsed.username || parsed.password) { + parsed.username = parsed.username ? "***" : ""; + parsed.password = parsed.password ? "***" : ""; + } + for (const key of parsed.searchParams.keys()) { + const lower = key.toLowerCase(); + if ( + lower === "token" || + lower === "key" || + lower === "api_key" || + lower === "apikey" || + lower === "secret" || + lower === "access_token" + ) { + parsed.searchParams.set(key, "***"); + } + } + return parsed.toString(); + } catch { + return config.url; + } } export type { SseMcpServerLaunchConfig, SseMcpServerLaunchResult }; diff --git a/src/agents/pi-bundle-mcp-tools.ts b/src/agents/pi-bundle-mcp-tools.ts index b7cae0ea2c5..bae9160fcbc 100644 --- a/src/agents/pi-bundle-mcp-tools.ts +++ b/src/agents/pi-bundle-mcp-tools.ts @@ -155,13 +155,30 @@ function resolveTransport( `bundle-mcp: server "${serverName}": header "${key}" has an unsupported value type and was ignored.`, ); }, + onMalformedHeaders: () => { + logWarn( + `bundle-mcp: server "${serverName}": "headers" must be a JSON object; the value was ignored.`, + ); + }, }); if (sseLaunch.ok) { const headers: Record = { ...sseLaunch.config.headers, }; + const hasHeaders = Object.keys(headers).length > 0; const transport = new SSEClientTransport(new URL(sseLaunch.config.url), { - requestInit: Object.keys(headers).length > 0 ? { headers } : undefined, + // Apply headers to POST requests (tool calls, listTools, etc.). + requestInit: hasHeaders ? { headers } : undefined, + // Apply headers to the initial SSE GET handshake (required for auth). + eventSourceInit: hasHeaders + ? { + fetch: (url, init) => + fetch(url, { + ...init, + headers: { ...headers, ...(init?.headers as Record) }, + }), + } + : undefined, }); return { transport,