export { O as OAuthOutboundUrlBlockedError, a as assertOutboundOAuthUrlAllowed, i as isDisallowedIpAddress, b as isLoopbackOAuthUrl, c as isPrivateHost } from '../ssrf-guard-BPxOTXTF.js'; export { O as OAuthProxyError, a as OAuthProxyRequest, b as OAuthProxyResponse, e as executeDebugOAuthProxy, c as executeOAuthProxy, f as fetchOAuthMetadata, d as fetchPinnedPublicDocument, v as validateUrl } from '../oauth-proxy-DP5EGDJ4.js'; /** * A DNS-pinned `fetch` that can STREAM — the transport the real MCP connection * rides on. * * WHY A SECOND ONE. `executeOAuthProxy` already resolves once, refuses private * answers, and pins the address into the socket, and the inspector's * `createPinnedFetch` wraps it in a `fetch` shape. But that path BUFFERS: it * reads the whole body into memory before answering, so it can never carry * `text/event-stream`. That is why the guarded fetch threaded into the * conformance suites reached only the raw probes, and why the one real MCP * connection each suite opens still followed redirects unchecked — the gap the * suite's own comment documented and could not close. * * This module closes it. Same two-step guarantee as the buffering path, taken * from the same {@link resolvePinnedAddresses}/{@link createPinnedLookup} * implementation, but the response body is handed back as a live * `ReadableStream`, so an SSE stream stays open and an MCP client cannot tell * it is not talking to `globalThis.fetch`. * * WHAT IT ENFORCES, all of it per hop: * * - **Scheme.** https, unless the CHAIN started at loopback and this hop is * loopback too. A public target may not steer a hop at the user's own * machine. * - **Address.** Resolve once, classify under RFC 6890, pin. Every redirect * hop repeats this in full — a `302` to `169.254.169.254` is refused at the * hop that names it, not after it has been dialled. * - **Redirect ceiling.** Bounded; the chain fails rather than looping. * - **Credentials.** Dropped when the origin changes, exactly as Fetch does, * so a redirect off-origin cannot carry an access token with it. * - **Time.** A chain deadline covers DNS, connect and headers across every * hop. It deliberately does NOT cover an established body stream — an SSE * stream is long-lived by design — which is what {@link * PinnedStreamingFetchOptions.bodyIdleTimeoutMs} is for: a stalled stream * dies, a healthy one does not. * - **Size.** A cumulative cap on body bytes AFTER decompression, so a * compressed bomb is measured at its real size. * * Node-only. Do not import from browser or worker entry points. */ interface PinnedStreamingFetchOptions { /** * Local-dev opt-in for a loopback TARGET. It never relaxes anything else, * and it belongs to the chain: a chain that started public can never arrive * at loopback, however many redirects it takes to try. */ allowLoopback?: boolean; /** * Budget for DNS + connect + response headers, summed across every hop of * the redirect chain. An established body stream is outside it — see the * module docblock. Default 30s. */ chainTimeoutMs?: number; /** * Kill a body stream that has produced no bytes for this long. This is the * bound that applies to a long-lived SSE stream; a total deadline would * close healthy ones. Default 0 (no idle bound). */ bodyIdleTimeoutMs?: number; /** * Cumulative cap on decompressed body bytes for one fetch. Exceeding it * destroys the socket and errors the stream. Default 32 MiB; 0 disables. */ maxResponseBytes?: number; /** Redirect hops allowed before the chain is refused. Default 5. */ maxRedirects?: number; /** Used in refusal messages, e.g. `"MCP server"`. */ targetLabel?: string; } /** * Build a streaming, DNS-pinned `fetch`. * * The result is drop-in for the subset of `fetch` an MCP transport uses: a URL, * a method, headers, a byte/string body, an `AbortSignal`, and a streaming * response. It does not implement `credentials`, `cache`, or a streaming * request body, and a caller who needs those should not be using it. */ declare function createPinnedStreamingFetch(options?: PinnedStreamingFetchOptions): typeof fetch; export { type PinnedStreamingFetchOptions, createPinnedStreamingFetch };