import { l as errorMessage } from "./errors-DkR6GPJw.js";
import { a as toolResultToText, i as toolOutputByteLength } from "./types-BiobHM1D.js";
import { t as reconcileImageMediaType } from "./image-sniff-B7uFSNO1.js";
import { Buffer } from "node:buffer";
//#region src/mcp/oauth-provider.ts
/**
* In-memory store — primarily for tests, but valid as a no-persistence option
* (tokens evaporate on process exit, the user re-auths every cold start).
*/
function createMemoryMcpCredentialStore(seed) {
	const state = new Map(Object.entries(seed ?? {}));
	return {
		load: (name) => state.get(name),
		save: (name, entry) => {
			state.set(name, entry);
		},
		delete: (name) => {
			state.delete(name);
		}
	};
}
const DEFAULT_CLIENT_NAME = "zidane";
var McpOAuthProvider = class {
	name;
	store;
	_redirectUri;
	onAuthorizationUrl;
	clientName;
	_scope;
	codeVerifierValue;
	constructor(opts) {
		this.name = opts.name;
		this.store = opts.store;
		this._redirectUri = opts.redirectUri;
		this.onAuthorizationUrl = opts.onAuthorizationUrl;
		this.clientName = opts.clientName ?? DEFAULT_CLIENT_NAME;
		this._scope = opts.scope;
	}
	get redirectUrl() {
		return this._redirectUri;
	}
	get clientMetadata() {
		return {
			redirect_uris: [this._redirectUri ?? "http://127.0.0.1:0/callback"],
			client_name: this.clientName,
			token_endpoint_auth_method: "none",
			grant_types: ["authorization_code", "refresh_token"],
			response_types: ["code"],
			...this._scope ? { scope: this._scope } : {}
		};
	}
	tokens() {
		return this.store.load(this.name)?.tokens;
	}
	saveTokens(tokens) {
		this.patch({ tokens });
	}
	clientInformation() {
		return this.store.load(this.name)?.clientInformation;
	}
	saveClientInformation(info) {
		this.patch({ clientInformation: info });
	}
	discoveryState() {
		return this.store.load(this.name)?.discoveryState;
	}
	saveDiscoveryState(state) {
		this.patch({ discoveryState: state });
	}
	saveCodeVerifier(verifier) {
		this.codeVerifierValue = verifier;
	}
	codeVerifier() {
		if (!this.codeVerifierValue) throw new Error(`MCP OAuth: code verifier missing for "${this.name}" — the flow must call saveCodeVerifier() before the redirect`);
		return this.codeVerifierValue;
	}
	async redirectToAuthorization(url) {
		await this.onAuthorizationUrl?.(url);
	}
	/**
	* Wipe stored credentials when the server reports the cached state is no
	* longer valid. The SDK calls this with a scope hint:
	*   - `'tokens'`  → access/refresh revoked, keep client registration
	*   - `'client'`  → client registration invalidated, reset everything
	*   - `'verifier'`→ PKCE state stale (e.g. mismatched state param)
	*   - `'discovery'` → discovery metadata stale (servers re-keyed)
	*   - `'all'`     → full reset
	*/
	async invalidateCredentials(scope) {
		if (scope === "verifier") {
			this.codeVerifierValue = void 0;
			return;
		}
		const current = this.store.load(this.name);
		if (!current) return;
		if (scope === "all") {
			this.codeVerifierValue = void 0;
			this.store.delete(this.name);
			return;
		}
		const next = { ...current };
		if (scope === "tokens") delete next.tokens;
		if (scope === "client") {
			delete next.clientInformation;
			delete next.tokens;
			delete next.discoveryState;
		}
		if (scope === "discovery") delete next.discoveryState;
		this.store.save(this.name, next);
	}
	patch(updates) {
		const existing = this.store.load(this.name) ?? {};
		this.store.save(this.name, {
			...existing,
			...updates
		});
	}
};
/**
* True when an HTTP transport's auth headers already include an explicit
* Authorization. Used by the bootstrap escape-hatch: a user who provided
* their own bearer token shouldn't be auto-promoted to OAuth on a 401.
*
* Case-insensitive — Node normalizes outgoing headers to lowercase but
* users hand-write `Authorization` in configs.
*/
function hasAuthorizationHeader(headers) {
	if (!headers) return false;
	for (const key of Object.keys(headers)) if (key.toLowerCase() === "authorization") return true;
	return false;
}
//#endregion
//#region src/mcp/sse-to-json-fetch.ts
/**
* `fetch` shim that converts streamable-http POST responses with
* `Content-Type: text/event-stream` into synthetic `application/json`
* responses, preserving every original header (notably `mcp-session-id`).
*
* Why this exists
* ----------------
* The MCP SDK's streamable-http transport handles POST responses two ways
* depending on `content-type`:
*
*   - `application/json` → `await response.json()` (works on every runtime).
*   - `text/event-stream` → `body.pipeThrough(TextDecoderStream)
*                              .pipeThrough(EventSourceParserStream)`
*                          and forward each parsed event to `onmessage`.
*
* On Bun + MCP SDK 1.29.x the second pipeline silently drops the parsed
* event: the full `event: message\ndata: {...}\n\n` arrives intact, the
* stream closes, but `onmessage` is never called. Bootstrap then waits the
* full `bootstrapTimeout` (10s default) and the agent loses every tool the
* server would have exposed.
*
* The MCP spec lets clients accept either content type
* (`Accept: application/json, text/event-stream`), so flipping the stream
* to JSON at the boundary is a transparent, server-agnostic workaround.
*
* Scope of the shim
* -----------------
*   - POST + `text/event-stream` → drain, parse SSE message events, return
*     a synthetic `application/json` response. Single event becomes a JSON
*     object (matching the shape the SDK expects from non-streaming
*     servers); multiple events become a JSON array (the SDK already
*     iterates `Array.isArray(data)` from a JSON response).
*   - GET (long-lived `_startOrAuthSse` listener) → untouched. Per-event
*     latency matters there and the SSE pipeline isn't always broken on
*     GET in the same way (different code path inside Bun's stream impl).
*   - 202 / non-SSE / malformed SSE / no body → passthrough.
*
* Runtime gating
* --------------
* `sseToJsonFetchIfNeeded()` only returns a wrapper on Bun. Node + browser
* runtimes get `undefined` and the SDK uses global `fetch` directly — no
* extra buffer-and-redrain on the happy path, and no risk of collapsing a
* future progress-streaming response into a single batched array. If you
* need to apply the shim unconditionally (testing, custom hosts), call
* `sseToJsonFetch()` directly.
*
* Cleanup
* -------
* Remove this file and its `fetch:` injection in `createTransport` once
* either Bun fixes the `pipeThrough` chain or the SDK switches off the
* streaming pipeline by default.
*/
/**
* Detect whether we're running on Bun. The `Bun` global is set by the
* runtime itself and isn't faked by Bun's Node-compat layer.
*/
function isBunRuntime() {
	return typeof globalThis.Bun !== "undefined";
}
/**
* Returns the `sseToJsonFetch` wrapper only on runtimes that need it
* (currently Bun). Returns `undefined` everywhere else, which makes the
* SDK fall back to the global `fetch` and keeps the streaming pipeline
* intact on Node where it works correctly.
*
* Designed as the value to pass directly to `StreamableHTTPClientTransport`'s
* `fetch` option:
*
*     new StreamableHTTPClientTransport(url, {
*       fetch: sseToJsonFetchIfNeeded(),
*     })
*/
function sseToJsonFetchIfNeeded() {
	return isBunRuntime() ? sseToJsonFetch() : void 0;
}
/**
* Wrap a `fetch` implementation so streamable-http POST responses that come
* back as `text/event-stream` are converted to JSON. Pass the result as
* `opts.fetch` to `StreamableHTTPClientTransport`.
*
* Always-on (i.e. unconditional) — for the runtime-gated entry point,
* use {@link sseToJsonFetchIfNeeded} instead.
*/
function sseToJsonFetch(baseFetch = fetch) {
	return async function sseToJsonWrappedFetch(input, init) {
		const response = await baseFetch(input, init);
		if ((init?.method ?? "GET").toString().toUpperCase() !== "POST") return response;
		const contentType = response.headers.get("content-type");
		if (!contentType || !contentType.includes("text/event-stream")) return response;
		if (!response.body) return response;
		let raw;
		try {
			raw = await response.text();
		} catch {
			return response;
		}
		const events = parseSseDataEvents(raw);
		return synthesizeJsonResponse(response, events.length === 1 ? events[0] : events);
	};
}
/**
* Parse a buffered SSE body into the JSON payloads of its `message` events.
*
* Skips:
* - SSE comments (lines starting with `:`).
* - Non-default event types (`event: foo` ≠ `message`). The MCP server
*   only ever emits `message` events for JSON-RPC; anything else is out of
*   band and the SDK wouldn't have surfaced it to `onmessage` either.
* - Malformed `data:` payloads (anything that fails `JSON.parse`).
*/
function parseSseDataEvents(raw) {
	const events = [];
	for (const block of raw.split(/\r?\n\r?\n/)) {
		if (!block.trim()) continue;
		const dataLines = [];
		let isMessageEvent = true;
		for (const line of block.split(/\r?\n/)) {
			if (line.startsWith(":")) continue;
			if (line.startsWith("event:")) {
				const eventType = line.slice(6).trim();
				if (eventType && eventType !== "message") isMessageEvent = false;
			} else if (line.startsWith("data:")) {
				const value = line.slice(5);
				dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
			}
		}
		if (!isMessageEvent || dataLines.length === 0) continue;
		try {
			events.push(JSON.parse(dataLines.join("\n")));
		} catch {}
	}
	return events;
}
/**
* Build a `Response` mirroring the original's status / statusText / headers
* but with a JSON body and `content-type: application/json`. Header
* preservation is the whole point — `mcp-session-id` is set by the server
* on the initialize POST and must round-trip into the SDK's
* `_sessionId` capture (`response.headers.get('mcp-session-id')`).
*/
function synthesizeJsonResponse(original, payload) {
	const headers = new Headers(original.headers);
	headers.set("content-type", "application/json");
	return new Response(JSON.stringify(payload), {
		status: original.status,
		statusText: original.statusText,
		headers
	});
}
//#endregion
//#region src/mcp/tolerant-client.ts
/**
* Async factory — builds an instance of the tolerant MCP client. The
* actual `Client` subclass definition lives inside the factory so the
* `@modelcontextprotocol/sdk` import only resolves when MCP is in use.
*
* Second `options` arg forwards directly into the upstream `Client`
* constructor so tests / advanced consumers can supply capabilities,
* `enforceStrictCapabilities`, etc.
*/
async function createTolerantClient(info, options) {
	const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
	const { Protocol } = await import("@modelcontextprotocol/sdk/shared/protocol.js");
	const { InitializeResultSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } = await import("@modelcontextprotocol/sdk/types.js");
	const onWarning = options?.onWarning;
	class TolerantMcpClient extends Client {
		async connect(transport, options) {
			await Protocol.prototype.connect.call(this, transport);
			if (transport.sessionId !== void 0) return;
			const self = this;
			try {
				const result = await this.request({
					method: "initialize",
					params: {
						protocolVersion: LATEST_PROTOCOL_VERSION,
						capabilities: self._capabilities,
						clientInfo: self._clientInfo
					}
				}, InitializeResultSchema, options);
				if (result === void 0) throw new Error(`Server sent invalid initialize result: ${result}`);
				if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
				self._serverCapabilities = result.capabilities;
				self._serverVersion = result.serverInfo;
				const setProtocolVersion = transport.setProtocolVersion;
				if (setProtocolVersion) setProtocolVersion.call(transport, result.protocolVersion);
				self._instructions = result.instructions;
				try {
					await this.notification({ method: "notifications/initialized" });
				} catch (notifyError) {
					onWarning?.(`server rejected notifications/initialized (continuing): ${errorMessage(notifyError)}`);
				}
				if (self._pendingListChangedConfig) {
					self._setupListChangedHandlers(self._pendingListChangedConfig);
					self._pendingListChangedConfig = void 0;
				}
			} catch (error) {
				this.close();
				throw error;
			}
		}
	}
	return new TolerantMcpClient(info, options);
}
//#endregion
//#region src/mcp/index.ts
const DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS = 1e4;
const DEFAULT_MCP_CLOSE_TIMEOUT_MS = 5e3;
/**
* Fire a hook without letting a listener's rejection escape. A throwing
* listener used to bring down the whole `Promise.all` in
* `connectMcpServers`, defeating the batch's partial-failure tolerance.
*
* Failures are swallowed; under `ZIDANE_DEBUG` they're traced to stderr
* so the misbehaving listener is still discoverable when a host opts in.
*
* `args` is variadic to match `Hookable.callHook`'s spread shape; in
* practice every `AgentHooks` event takes a single context object.
*/
async function safeCallHook(hooks, event, ...args) {
	if (!hooks) return;
	try {
		await hooks.callHook(event, ...args);
	} catch (err) {
		if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] hook "${event}" listener rejected: ${errorMessage(err)}\n`);
	}
}
/**
* Close a transport best-effort. The catch swallows everything — by the
* time we're closing, the bootstrap has already failed and the only thing
* worse than a leaked transport is a leaked transport AND a new error
* masking the original cause.
*/
async function closeTransportQuietly(transport) {
	if (!transport) return;
	try {
		await transport.close();
	} catch {}
}
/**
* Read the child PID off a stdio transport. Structural — only
* `StdioClientTransport` exposes `pid`; HTTP/SSE transports return `null`.
* Returns `null` once the child has exited (the SDK clears its process
* reference on the child's `close` event), so a kill based on this value
* never targets a recycled PID.
*/
function stdioPidOf(transport) {
	const pid = transport?.pid;
	return typeof pid === "number" ? pid : null;
}
/**
* Last-resort reaper for a stdio child whose transport close wedged. By the
* time this runs, the graceful path (stdin EOF → SIGTERM escalation inside
* the SDK, where the installed version implements it) has been abandoned —
* SIGKILL is the only signal a hung child can't ignore. ESRCH (already
* exited) is swallowed.
*/
function killStdioChild(pid) {
	if (pid == null) return;
	try {
		process.kill(pid, "SIGKILL");
	} catch {}
}
/**
* Detect failure signatures that point at a transport mismatch and return
* an actionable hint string. Conservative — only matches the signals we've
* actually observed in the wild against MCP servers, so a real outage on
* the right transport doesn't get misdiagnosed.
*
* Streamable-HTTP against an SSE-only endpoint:
*   - The transport opens an `initialize` POST. SSE-only servers respond
*     with `405 Method Not Allowed` or `404 Not Found` (their `/sse` GET
*     endpoint is the only handler). The SDK wraps these as
*     `Error POSTing to endpoint (HTTP 40X): ...`.
*   - Some proxies return `400` with a `text/event-stream` mismatch on
*     POST; same root cause, same fix.
*/
function buildTransportMismatchHint(transport, err) {
	const msg = err.message;
	if (transport === "streamable-http") {
		if (/\bHTTP\s*40[45]\b/i.test(msg) || /\(40[45]\)/.test(msg)) return "This server may not implement the streamable-HTTP transport. Try `transport: 'sse'` (or `sseUrl` instead of `url`) on this server's config.";
	}
	return null;
}
/**
* Wrap a bootstrap failure so the surfaced `Error` carries an actionable
* hint while preserving the original message + stack + cause. Returns the
* input error verbatim when no hint applies.
*
* `cause` (ES2022) gives hosts that introspect a way back to the raw SDK
* error; the human-readable hint is the new message body that lands in
* logs / hook payloads.
*/
function withTransportMismatchHint(transport, err) {
	const hint = buildTransportMismatchHint(transport, err);
	if (!hint) return err;
	const augmented = new Error(`${err.message}\nHint: ${hint}`, { cause: err });
	if (err.stack) augmented.stack = err.stack;
	return augmented;
}
function inferTransport(raw) {
	if (raw.transport === "stdio" || raw.transport === "sse" || raw.transport === "streamable-http") return raw.transport;
	if (raw.type === "stdio" || raw.type === "sse" || raw.type === "streamable-http" || raw.type === "http") return raw.type === "http" ? "streamable-http" : raw.type;
	if (raw.command) return "stdio";
	if (raw.httpUrl) return "streamable-http";
	if (raw.sseUrl) return "sse";
	if (raw.url) return "streamable-http";
	throw new Error(`Cannot infer MCP transport from config: ${JSON.stringify(raw)}`);
}
function normalizeOne(name, raw) {
	const transport = inferTransport(raw);
	const url = raw.url ?? raw.httpUrl ?? raw.sseUrl;
	const config = {
		name,
		transport
	};
	if (raw.command) config.command = raw.command;
	if (raw.args) config.args = raw.args;
	if (raw.env) config.env = raw.env;
	if (raw.strictEnv === true) config.strictEnv = true;
	if (typeof raw.cwd === "string" && raw.cwd.length > 0) config.cwd = raw.cwd;
	if (url) config.url = url;
	if (raw.headers) config.headers = raw.headers;
	if (typeof raw.bootstrapTimeout === "number") config.bootstrapTimeout = raw.bootstrapTimeout;
	if (typeof raw.toolTimeout === "number") config.toolTimeout = raw.toolTimeout;
	if (typeof raw.closeTimeout === "number") config.closeTimeout = raw.closeTimeout;
	if (Array.isArray(raw.enabledTools)) config.enabledTools = raw.enabledTools;
	if (Array.isArray(raw.disabledTools)) config.disabledTools = raw.disabledTools;
	if (typeof raw.toolFilter === "function") config.toolFilter = raw.toolFilter;
	if (raw.disclosure === "eager" || raw.disclosure === "lazy") config.disclosure = raw.disclosure;
	if (Array.isArray(raw.cachedTools)) config.cachedTools = raw.cachedTools;
	if (raw.lazyConnect === true) config.lazyConnect = true;
	if (raw.auth === "oauth" || raw.authMethod === "mcpOAuth") config.auth = "oauth";
	return config;
}
/**
* True when the input looks like a single config object (flat fields like `command`,
* `transport`, `url`) rather than a record whose values are configs.
*/
function looksLikeSingleConfig(obj) {
	return [
		"transport",
		"type",
		"command",
		"url",
		"httpUrl",
		"sseUrl"
	].some((key) => typeof obj[key] === "string");
}
/**
* Normalize MCP server configs from any common shape to `McpServerConfig[]`.
*
* Accepts:
* - `McpServerConfig[]` — zidane native (pass-through).
* - `McpServerConfig` — a single config object (wrapped to a 1-element array).
* - `Record<string, RawShape>` — name-keyed map (common in host-SDK configs), where the key is the server name.
* - Mixed shapes with `type` vs `transport`, `httpUrl`/`sseUrl` vs `url`.
*
* Returns `[]` when `input` is nullish. Throws a descriptive error when the transport
* cannot be inferred from a given entry, or when the input shape is unsupported.
*/
function normalizeMcpServers(input) {
	if (input == null) return [];
	if (Array.isArray(input)) return input.map((raw, idx) => {
		const obj = raw;
		return normalizeOne(obj.name ?? `mcp_${idx}`, obj);
	});
	if (typeof input === "object") {
		const obj = input;
		if (looksLikeSingleConfig(obj)) {
			const raw = obj;
			return [normalizeOne(raw.name ?? "mcp_0", raw)];
		}
		return Object.entries(obj).map(([name, raw]) => normalizeOne(name, raw ?? {}));
	}
	throw new Error(`Unsupported MCP server config shape: ${typeof input}`);
}
/**
* Lossy flattener — converts MCP `CallToolResult.content` blocks to a single
* string. Text blocks are extracted; non-text blocks are JSON-stringified.
*
* Use this only at UI / log boundaries that require a string. The agent
* loop itself routes through {@link normalizeMcpBlocks} so image blocks
* survive into provider-native tool_result content (Anthropic blocks,
* OpenAI companion-user-message).
*/
function resultToString(content) {
	if (!content || !Array.isArray(content)) return "";
	return content.map((block) => {
		if (block && typeof block === "object" && block.type === "text") {
			const text = block.text;
			if (typeof text === "string") return text;
		}
		return JSON.stringify(block);
	}).join("\n");
}
/**
* Normalize MCP `CallToolResult.content` to zidane's {@link ToolResultContent[]} shape.
*
* Handles the four MCP content block types:
* - `text` → preserved as `{type:'text', text}`
* - `image` → preserved as `{type:'image', mediaType, data}` (MCP uses `mimeType`)
* - `resource` with embedded text → flattened to a text block
* - `resource` with embedded blob whose `mimeType` is `image/*` → flattened to an image block
* - Any unrecognized block → JSON-stringified fallback text block (lossy but safe)
*
* Returns `null` when the input is not an array — callers should fall back to an empty
* result in that case.
*/
function normalizeMcpBlocks(content) {
	if (!Array.isArray(content)) return null;
	const out = [];
	for (const raw of content) {
		if (!raw || typeof raw !== "object") continue;
		const block = raw;
		if (block.type === "text" && typeof block.text === "string") {
			out.push({
				type: "text",
				text: block.text
			});
			continue;
		}
		if (block.type === "image" && typeof block.data === "string") {
			const mediaType = reconcileImageMediaType(typeof block.mimeType === "string" ? block.mimeType : typeof block.mediaType === "string" ? block.mediaType : "image/png", block.data);
			out.push({
				type: "image",
				mediaType,
				data: block.data
			});
			continue;
		}
		if (block.type === "resource" && block.resource && typeof block.resource === "object") {
			const res = block.resource;
			if (typeof res.text === "string") {
				out.push({
					type: "text",
					text: res.text
				});
				continue;
			}
			if (typeof res.blob === "string" && typeof res.mimeType === "string" && res.mimeType.startsWith("image/")) {
				const mediaType = reconcileImageMediaType(res.mimeType, res.blob);
				out.push({
					type: "image",
					mediaType,
					data: res.blob
				});
				continue;
			}
		}
		out.push({
			type: "text",
			text: JSON.stringify(block)
		});
	}
	return out;
}
/**
* Route the MCP result content through the narrowest appropriate zidane shape:
*
* - All blocks are `text` → return a joined string (smaller wire payload,
*   string-friendly for hook consumers that don't need structured access).
* - Any block is non-text → return a structured `ToolResultContent[]`.
* - Empty / non-array input → return `''`.
*/
function packMcpResult(content) {
	const normalized = normalizeMcpBlocks(content);
	if (!normalized || normalized.length === 0) return "";
	const parts = [];
	for (const block of normalized) {
		if (block.type !== "text") return normalized;
		parts.push(block.text);
	}
	return parts.join("\n");
}
/**
* Create the appropriate MCP transport for a server config.
*
* For stdio: when `config.env` is provided, it is merged on top of the MCP SDK's
* `getDefaultEnvironment()` whitelist (`PATH`, `HOME`, `LANG`, `SHELL`, `USER` on
* POSIX; `APPDATA`, `PATH`, ... on Win32). Without this defensive merge, older
* MCP SDK versions strip `PATH` the moment a consumer sets any env, breaking
* `spawn('node', ...)` with ENOENT. Pass `strictEnv: true` to opt out and send
* `env` verbatim.
*
* `config.cwd` is forwarded to `StdioClientTransport` (→ `spawn`'s `cwd`).
* Without this, the child inherits the agent's cwd, breaking servers that
* resolve paths relative to their own working directory (e.g. project-root
* MCPs reading `<root>/supabase/functions/...`).
*/
async function createTransport(config, authProvider, hooks) {
	switch (config.transport) {
		case "stdio": {
			const { StdioClientTransport, getDefaultEnvironment } = await import("./stdio-loader-Ce68wUmM.js");
			const mergedEnv = config.env && !config.strictEnv ? {
				...getDefaultEnvironment(),
				...config.env
			} : config.env;
			const transport = new StdioClientTransport({
				command: config.command,
				args: config.args,
				env: mergedEnv,
				stderr: "pipe",
				...config.cwd ? { cwd: config.cwd } : {}
			});
			attachStderrWarnPump(transport, config.name, hooks);
			return transport;
		}
		case "sse": {
			const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
			return new SSEClientTransport(new URL(config.url), {
				requestInit: config.headers ? { headers: config.headers } : void 0,
				authProvider
			});
		}
		case "streamable-http": {
			const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
			return new StreamableHTTPClientTransport(new URL(config.url), {
				requestInit: config.headers ? { headers: config.headers } : void 0,
				fetch: sseToJsonFetchIfNeeded(),
				authProvider
			});
		}
		default: throw new Error(`Unknown MCP transport: ${config.transport}`);
	}
}
/**
* Split a stdio-transport child's stderr into lines and surface them via `mcp:warn`.
*/
const MAX_MCP_STDERR_LINE_CHARS = 64 * 1024;
function attachStderrWarnPump(transport, serverName, hooks) {
	const stream = transport.stderr;
	if (!stream) return;
	let buffer = "";
	const emitLine = (line) => {
		const trimmed = line.replace(/\r$/, "").trim();
		if (trimmed.length === 0) return;
		(hooks?.callHook("mcp:warn", {
			name: serverName,
			message: trimmed
		}))?.catch(() => {});
	};
	const flush = () => {
		if (buffer.length === 0) return;
		emitLine(buffer);
		buffer = "";
	};
	stream.on("data", (chunk) => {
		buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
		let nl = buffer.indexOf("\n");
		while (nl !== -1) {
			const line = buffer.slice(0, nl);
			buffer = buffer.slice(nl + 1);
			emitLine(line);
			nl = buffer.indexOf("\n");
		}
		while (buffer.length >= MAX_MCP_STDERR_LINE_CHARS) {
			emitLine(buffer.slice(0, MAX_MCP_STDERR_LINE_CHARS));
			buffer = buffer.slice(MAX_MCP_STDERR_LINE_CHARS);
		}
	});
	stream.on("end", flush);
	stream.on("close", flush);
	stream.on("error", flush);
}
/**
* True when an error from `client.connect()` indicates the user needs to
* (re-)authenticate against the MCP server. Two signal families:
*
*   1. **SDK-formal** — the MCP SDK confirmed the resource is OAuth-protected
*      (RFC 9728 metadata advertised) and threw `UnauthorizedError`. Detected
*      by class name + "Unauthorized" message prefix, since the SDK's
*      `UnauthorizedError` class isn't a stable export across versions.
*
*   2. **Raw HTTP 401 with token-rejection signal** — some real-world servers
*      (Linear is one) skip the RFC 9728 dance and just return `HTTP 401`
*      with an OAuth-style JSON body (`{"error":"invalid_token"}` /
*      `"invalid_grant"` / `"insufficient_scope"`). The MCP SDK wraps that
*      into `"Error POSTing to endpoint (HTTP 401): { ... }"`. Without
*      promotion, a stale Linear token reads as a generic `mcp:error`
*      bootstrap failure instead of "needs login" — the user gets a noisy
*      red banner instead of a re-login affordance.
*
* Promotion to the skip+auth-required path is still gated on
* `explicitAuth || eligibleAutoPromote` in the caller, so a server that
* legitimately requires a static `Authorization: Bearer …` header (no
* `auth: 'oauth'` flag, host-provided header) won't be mis-promoted into
* a doomed OAuth flow.
*/
function isUnauthorizedError(err) {
	if (!err || typeof err !== "object") return false;
	const e = err;
	if (e.name === "UnauthorizedError" || e.constructor?.name === "UnauthorizedError") return true;
	const message = typeof e.message === "string" ? e.message : "";
	if (message.toLowerCase().startsWith("unauthorized")) return true;
	if (/\bHTTP\s*401\b/i.test(message) || /\b\(401\)/.test(message)) {
		if (/"error"\s*:\s*"(?:invalid_token|invalid_grant|insufficient_scope|invalid_client)"/i.test(message)) return true;
	}
	return false;
}
/**
* Connect to MCP servers and discover their tools.
*
* Each tool is namespaced as `mcp_{serverName}_{toolName}` to avoid
* collisions with agent tools or tools from other servers.
*
* @param configs - Array of MCP server configurations
* @param _clientFactory - Internal: override client construction for testing
* @param hooks - Optional agent hooks for firing mcp:connect, mcp:error, mcp:close events
* @param options - Optional bootstrap knobs (e.g. OAuth provider injection)
*/
async function connectMcpServers(configs, _clientFactory, hooks, options) {
	const connections = [];
	const tools = {};
	const instructions = /* @__PURE__ */ new Map();
	const errors = [];
	let closed = false;
	const bootstrapResults = (await Promise.allSettled(configs.map((config) => bootstrapServer(config, _clientFactory, hooks, options)))).map((result, idx) => {
		if (result.status === "fulfilled") return result.value;
		const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
		return {
			ok: false,
			name: configs[idx].name,
			error,
			durationMs: 0
		};
	});
	if (configs.length > 0) await safeCallHook(hooks, "mcp:bootstrap:settled", { results: bootstrapResults.map((result, idx) => {
		const transport = configs[idx].transport;
		if (result.ok) return {
			name: result.name,
			transport,
			durationMs: result.durationMs,
			ok: true,
			toolCount: result.tools.length,
			...result.lazy ? { lazy: true } : {}
		};
		return {
			name: result.name,
			transport,
			durationMs: result.durationMs,
			ok: false,
			...result.skipped ? { skipped: true } : {},
			..."error" in result && result.error ? { error: result.error } : {}
		};
	}).sort((a, b) => a.name.localeCompare(b.name)) });
	for (const result of bootstrapResults) {
		if (result.skipped) continue;
		if (!result.ok) {
			errors.push({
				name: result.name,
				error: result.error
			});
			await safeCallHook(hooks, "mcp:error", {
				name: result.name,
				error: result.error
			});
			continue;
		}
		const toolNames = result.tools.map((t) => `mcp_${result.config.name}_${t.name}`);
		const getClient = result.lazy ? wrapConnectWithHookFire(() => result.handle.connect(), onceFireConnect(result.name, result.config.transport, toolNames, hooks)) : (() => {
			const client = result.client;
			return () => Promise.resolve(client);
		})();
		for (let i = 0; i < result.tools.length; i++) tools[toolNames[i]] = buildMcpToolDef(result.config, getClient, result.tools[i], toolNames[i], hooks);
		const closeTimeout = result.config.closeTimeout ?? DEFAULT_MCP_CLOSE_TIMEOUT_MS;
		if (result.lazy) {
			const handle = result.handle;
			const name = result.name;
			connections.push({
				name,
				closeTimeout,
				closeIfConnected: async () => {
					await safeCallHook(hooks, "mcp:close", { name });
					await handle.closeIfConnected();
				},
				...result.config.transport === "stdio" ? { stdioPid: handle.stdioPid } : {}
			});
		} else {
			const eagerClient = result.client;
			const eagerTransport = result.transport;
			connections.push({
				name: result.name,
				closeTimeout,
				closeIfConnected: async () => {
					await safeCallHook(hooks, "mcp:close", { name: result.name });
					await eagerClient.close();
				},
				...result.config.transport === "stdio" ? { stdioPid: () => stdioPidOf(eagerTransport) } : {}
			});
			if (result.instructions !== void 0) instructions.set(result.name, result.instructions);
			await safeCallHook(hooks, "mcp:connect", {
				name: result.name,
				transport: result.config.transport,
				tools: toolNames
			});
		}
	}
	return {
		tools,
		instructions,
		close: async () => {
			if (closed) return;
			closed = true;
			await Promise.allSettled(connections.map(async (c) => {
				const pid = c.stdioPid?.() ?? null;
				try {
					await raceWithTimeout(() => c.closeIfConnected(), c.closeTimeout, `MCP server "${c.name}" close timed out after ${c.closeTimeout}ms (configurable via \`closeTimeout\`).`);
				} catch (err) {
					killStdioChild(pid);
					if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] close "${c.name}" failed: ${errorMessage(err)}\n`);
				}
			}));
		}
	};
}
/**
* Build a one-shot `mcp:connect` emitter for a lazy server. The wrapper
* around the connector calls this on the first successful connect;
* concurrent first calls converge on the connector's in-flight promise
* so the hook still fires exactly once.
*/
function onceFireConnect(name, transport, toolNames, hooks) {
	let fired = false;
	return async () => {
		if (fired) return;
		fired = true;
		await safeCallHook(hooks, "mcp:connect", {
			name,
			transport,
			tools: toolNames,
			lazy: true
		});
	};
}
/**
* Wrap a lazy connector so the first successful resolution fires
* `mcp:connect` exactly once. Failures don't fire the hook — the
* connection isn't live yet and a retry will eventually run through the
* wrapper again. Both the eager and lazy paths reach the tool builder
* through this resolver shape so `buildMcpToolDef` stays mode-agnostic.
*/
function wrapConnectWithHookFire(connect, fire) {
	return async () => {
		const client = await connect();
		await fire();
		return client;
	};
}
/**
* Connect one MCP server and list its tools, wrapped in a single race against
* `config.bootstrapTimeout` (default 10s). Always emits `mcp:bootstrap:start`
* before network I/O and `mcp:bootstrap:end` with `durationMs` + outcome, so a
* host can build a timing view even when every server succeeds.
*
* Errors are captured into the `{ ok: false }` result rather than thrown — the
* parent uses `Promise.all` across every server and we can't let one rejection
* short-circuit the batch.
*/
async function bootstrapServer(config, _clientFactory, hooks, options) {
	const start = Date.now();
	if (config.enabledTools && config.disabledTools) return emitConfigFailure(config, hooks, `MCP server "${config.name}": enabledTools and disabledTools are mutually exclusive — set one or the other, not both.`);
	if (config.lazyConnect && (!config.cachedTools || config.cachedTools.length === 0)) return emitConfigFailure(config, hooks, `MCP server "${config.name}": lazyConnect requires cachedTools (the host must pre-populate the tool schemas so they can be advertised without a connection).`);
	if (config.lazyConnect && config.auth === "oauth") return emitConfigFailure(config, hooks, `MCP server "${config.name}": lazyConnect is not compatible with auth: 'oauth' — OAuth servers must complete the handshake at bootstrap so a login affordance can surface before the model commits to a tool call.`);
	await safeCallHook(hooks, "mcp:bootstrap:start", {
		name: config.name,
		transport: config.transport
	});
	const explicitAuth = config.auth === "oauth";
	const authProvider = explicitAuth ? options?.buildAuthProvider?.(config) : void 0;
	const warnings = [];
	const onWarning = (message) => {
		warnings.push(message);
	};
	if (config.lazyConnect) {
		const cachedSchemas = config.cachedTools;
		const filteredTools = await applyMcpToolFilters(config, cachedSchemas, hooks);
		const handle = makeLazyConnector(config, _clientFactory, authProvider, hooks);
		const durationMs = Date.now() - start;
		await safeCallHook(hooks, "mcp:bootstrap:end", {
			name: config.name,
			transport: config.transport,
			durationMs,
			ok: true,
			toolCount: filteredTools.length,
			lazy: true,
			cached: true
		});
		return {
			ok: true,
			name: config.name,
			config,
			lazy: true,
			handle,
			tools: filteredTools,
			durationMs
		};
	}
	let client = null;
	let transport = null;
	try {
		client = _clientFactory ? _clientFactory({ onWarning }) : await createTolerantClient({
			name: "zidane",
			version: "1.0.0"
		}, { onWarning });
		const currentClient = client;
		transport = await createTransport(config, authProvider, hooks);
		const bootstrapTimeout = config.bootstrapTimeout ?? DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS;
		const cachedSchemas = config.cachedTools;
		const mcpTools = await raceWithTimeout(async () => {
			await currentClient.connect(transport);
			return cachedSchemas ?? (await currentClient.listTools()).tools;
		}, bootstrapTimeout, `MCP server "${config.name}" bootstrap timed out after ${bootstrapTimeout}ms (configurable via \`bootstrapTimeout\`).`);
		await safeCallHook(hooks, "mcp:tools:list", {
			name: config.name,
			transport: config.transport,
			tools: mcpTools
		});
		const filteredTools = await applyMcpToolFilters(config, mcpTools, hooks);
		let rawInstructions;
		if (typeof currentClient.getInstructions === "function") try {
			rawInstructions = currentClient.getInstructions();
		} catch {
			rawInstructions = void 0;
		}
		const instructions = typeof rawInstructions === "string" && rawInstructions.trim().length > 0 ? rawInstructions : void 0;
		const durationMs = Date.now() - start;
		await safeCallHook(hooks, "mcp:bootstrap:end", {
			name: config.name,
			transport: config.transport,
			durationMs,
			ok: true,
			toolCount: filteredTools.length,
			...cachedSchemas ? { cached: true } : {},
			...warnings.length > 0 ? { warnings } : {}
		});
		return {
			ok: true,
			name: config.name,
			config,
			lazy: false,
			client: currentClient,
			transport,
			tools: filteredTools,
			...instructions !== void 0 ? { instructions } : {},
			durationMs
		};
	} catch (err) {
		await closeTransportQuietly(transport);
		await closeClientQuietly(client);
		const rawError = err instanceof Error ? err : new Error(String(err));
		const error = withTransportMismatchHint(config.transport, rawError);
		const durationMs = Date.now() - start;
		if (isUnauthorizedError(err)) {
			const eligibleAutoPromote = !explicitAuth && !hasAuthorizationHeader(config.headers) && !!options?.buildAuthProvider;
			if (explicitAuth || eligibleAutoPromote) {
				await safeCallHook(hooks, "mcp:bootstrap:end", {
					name: config.name,
					transport: config.transport,
					durationMs,
					ok: false,
					error
				});
				await safeCallHook(hooks, "mcp:auth:required", {
					name: config.name,
					transport: config.transport,
					reason: explicitAuth ? "no-tokens" : "auto-promoted"
				});
				return {
					ok: false,
					skipped: true,
					name: config.name,
					durationMs
				};
			}
		}
		await safeCallHook(hooks, "mcp:bootstrap:end", {
			name: config.name,
			transport: config.transport,
			durationMs,
			ok: false,
			error
		});
		return {
			ok: false,
			name: config.name,
			error,
			durationMs
		};
	}
}
/**
* Emit the start/end hooks for a config-level failure and return the error
* result. Centralised so the various validation branches (enabled/disabled
* mutex, lazyConnect-without-cachedTools, lazyConnect+OAuth) stay readable.
*/
async function emitConfigFailure(config, hooks, message) {
	const error = new Error(message);
	await safeCallHook(hooks, "mcp:bootstrap:start", {
		name: config.name,
		transport: config.transport
	});
	await safeCallHook(hooks, "mcp:bootstrap:end", {
		name: config.name,
		transport: config.transport,
		durationMs: 0,
		ok: false,
		error
	});
	return {
		ok: false,
		name: config.name,
		error,
		durationMs: 0
	};
}
/**
* Build a memoized lazy connector + matching close handle for a
* `lazyConnect: true` server. State lives in the closure: the connector
* tracks its own in-flight and resolved-client state so the close path
* can reason about "never connected" vs "in flight" vs "live" without
* forcing a connect from inside `close()`.
*
* Concurrency contract:
*   - First `connect()` builds + connects a fresh client. Concurrent
*     callers converge on the same in-flight promise.
*   - On rejection, the cached promise is dropped so the next caller
*     retries with a fresh client instance.
*   - `closeIfConnected()` awaits any pending connect, closes the
*     resulting client, and marks the handle destroyed. Any subsequent
*     `connect()` rejects synchronously so a tool call racing the
*     shutdown can't leak a freshly-built client past the close.
*/
function makeLazyConnector(config, _clientFactory, authProvider, hooks) {
	let inFlight = null;
	let live = null;
	let liveTransport = null;
	let destroyed = false;
	const connect = () => {
		if (destroyed) return Promise.reject(/* @__PURE__ */ new Error(`MCP server "${config.name}": cannot connect after close()`));
		if (live) return Promise.resolve(live);
		if (inFlight) return inFlight;
		const pending = (async () => {
			const bootstrapTimeout = config.bootstrapTimeout ?? DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS;
			const client = _clientFactory ? _clientFactory() : await createTolerantClient({
				name: "zidane",
				version: "1.0.0"
			});
			let transport = null;
			try {
				transport = await createTransport(config, authProvider, hooks);
				await raceWithTimeout(() => client.connect(transport), bootstrapTimeout, `MCP server "${config.name}" lazy connect timed out after ${bootstrapTimeout}ms (configurable via \`bootstrapTimeout\`).`);
				if (destroyed) {
					await closeTransportQuietly(transport);
					await closeClientQuietly(client);
					throw new Error(`MCP server "${config.name}": closed during connect`);
				}
				live = client;
				liveTransport = transport;
				return client;
			} catch (err) {
				await closeTransportQuietly(transport);
				await closeClientQuietly(client);
				throw err instanceof Error ? withTransportMismatchHint(config.transport, err) : err;
			}
		})();
		inFlight = pending;
		pending.then(() => {
			inFlight = null;
		}, () => {
			inFlight = null;
		});
		return pending;
	};
	const closeIfConnected = async () => {
		destroyed = true;
		if (inFlight) try {
			await inFlight;
		} catch {}
		if (live) {
			const client = live;
			live = null;
			await client.close();
		}
	};
	return {
		connect,
		closeIfConnected,
		stdioPid: () => stdioPidOf(liveTransport)
	};
}
/**
* Apply config-side filters (`enabledTools` / `disabledTools` / `toolFilter`)
* and then the `mcp:tools:filter` hook to the upstream tool list.
*
* Composition order — narrowest-to-widest:
* 1. Allow-list (`enabledTools`) — keep only listed tools.
* 2. Deny-list (`disabledTools`) — drop listed tools.
* 3. Predicate (`toolFilter`) — fine-grained metadata filtering.
* 4. Hook (`mcp:tools:filter`) — runtime / per-host decisions.
*
* The mutual exclusion of `enabledTools` and `disabledTools` is checked in
* `bootstrapServer` so this stays a pure data transform.
*/
async function applyMcpToolFilters(config, tools, hooks) {
	let filtered = tools;
	if (config.enabledTools && config.enabledTools.length > 0) {
		const allow = new Set(config.enabledTools);
		filtered = filtered.filter((t) => allow.has(t.name));
	}
	if (config.disabledTools && config.disabledTools.length > 0) {
		const deny = new Set(config.disabledTools);
		filtered = filtered.filter((t) => !deny.has(t.name));
	}
	if (config.toolFilter) {
		const predicate = config.toolFilter;
		filtered = filtered.filter((t) => predicate(t));
	}
	if (hooks) {
		const ctx = {
			server: config.name,
			transport: config.transport,
			tools: [...filtered]
		};
		await hooks.callHook("mcp:tools:filter", ctx);
		filtered = ctx.tools;
	}
	return filtered;
}
/**
* Build the zidane `ToolDef` that wraps a single MCP tool. Extracted so the
* parallel bootstrap path can assemble tools from a results array without
* inlining a 70-line closure inside the collector loop.
*
* `getClient` resolves to a live `Client` ready for `tools/call`. For
* eagerly-bootstrapped servers it's a constant resolved promise; for
* `lazyConnect: true` servers it's the memoized connector built by
* {@link makeLazyConnector} — the first invocation pays the connect cost,
* concurrent / subsequent calls observe the cached client. Reconnects /
* swap-outs go through a fresh `connectMcpServers` call rather than
* rewiring the tool in place.
*/
function buildMcpToolDef(config, getClient, tool, namespacedName, hooks) {
	return {
		spec: {
			name: namespacedName,
			description: tool.description || "",
			inputSchema: tool.inputSchema ?? {
				type: "object",
				properties: {}
			}
		},
		execute: async (input, ctx) => {
			const { turnId, callId, signal } = ctx;
			const displayName = ctx.toolAliases?.[namespacedName] ?? namespacedName;
			const identity = {
				...ctx.runId !== void 0 ? { runId: ctx.runId } : {},
				...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
				...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
			};
			const gateCtx = {
				turnId,
				callId,
				server: config.name,
				tool: tool.name,
				displayName,
				input,
				...identity,
				block: false,
				reason: "MCP tool execution was blocked"
			};
			await hooks?.callHook("mcp:tool:gate", gateCtx);
			if (gateCtx.block) return `Blocked: ${gateCtx.reason}`;
			const effectiveInput = gateCtx.input;
			if (gateCtx.result !== void 0) {
				let substitute = gateCtx.result;
				const transformCtx = {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					result: substitute,
					outputBytes: toolOutputByteLength(substitute)
				};
				await hooks?.callHook("mcp:tool:transform", transformCtx);
				substitute = transformCtx.result;
				await hooks?.callHook("mcp:tool:after", {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					result: substitute,
					outputBytes: toolOutputByteLength(substitute)
				});
				return substitute;
			}
			await hooks?.callHook("mcp:tool:before", {
				turnId,
				callId,
				server: config.name,
				tool: tool.name,
				displayName,
				input: effectiveInput,
				...identity
			});
			const timeout = config.toolTimeouts?.[tool.name] ?? config.toolTimeout ?? 3e4;
			try {
				const client = await getClient();
				const result = await raceWithTimeoutAndSignal((rpcSignal) => client.callTool({
					name: tool.name,
					arguments: effectiveInput
				}, void 0, { signal: rpcSignal }), timeout, `MCP tool "${tool.name}" on server "${config.name}" timed out after ${timeout}ms. The server may still be processing the request — verify its effect before retrying, and do not immediately retry the identical call.`, signal);
				let output = packMcpResult(result.content);
				if (result.isError === true) {
					const detail = (typeof output === "string" ? output : toolResultToText(output)).trim();
					throw new Error(detail || `MCP tool "${tool.name}" on server "${config.name}" reported an error with no diagnostic content`);
				}
				const transformCtx = {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					result: output,
					outputBytes: toolOutputByteLength(output)
				};
				await hooks?.callHook("mcp:tool:transform", transformCtx);
				output = transformCtx.result;
				await hooks?.callHook("mcp:tool:after", {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					result: output,
					outputBytes: toolOutputByteLength(output)
				});
				return output;
			} catch (err) {
				const error = err instanceof Error ? err : new Error(String(err));
				await hooks?.callHook("mcp:tool:error", {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					error
				});
				await hooks?.callHook("mcp:tool:after", {
					turnId,
					callId,
					server: config.name,
					tool: tool.name,
					displayName,
					input: effectiveInput,
					...identity,
					result: error.message,
					outputBytes: Buffer.byteLength(error.message)
				});
				throw error;
			}
		}
	};
}
async function closeClientQuietly(client) {
	if (!client) return;
	try {
		await client.close();
	} catch {}
}
async function raceWithTimeout(task, timeoutMs, timeoutMessage) {
	let timer;
	try {
		return await new Promise((resolvePromise, rejectPromise) => {
			timer = setTimeout(() => rejectPromise(new Error(timeoutMessage)), timeoutMs);
			task().then(resolvePromise, rejectPromise);
		});
	} finally {
		if (timer) clearTimeout(timer);
	}
}
/**
* Race a promise-returning task against (a) a timeout and (b) an optional
* abort signal. Cleans up its timer and abort listener on every exit path.
*
* The task receives a dedicated `AbortSignal` that fires on either trigger.
* Callers thread it into the MCP SDK's `RequestOptions.signal` so a timeout
* or run-abort cancels the underlying RPC (the SDK sends
* `notifications/cancelled`) instead of leaving it running server-side —
* which matters for streamable-http transports, where `connection.close()`
* has no subprocess to kill.
*/
async function raceWithTimeoutAndSignal(task, timeoutMs, timeoutMessage, signal) {
	if (signal?.aborted) throw new Error("MCP tool call aborted");
	const rpcAbort = new AbortController();
	let timer;
	let onAbort;
	try {
		return await new Promise((resolvePromise, rejectPromise) => {
			timer = setTimeout(() => {
				rejectPromise(new Error(timeoutMessage));
				rpcAbort.abort();
			}, timeoutMs);
			if (signal) {
				onAbort = () => {
					rejectPromise(/* @__PURE__ */ new Error("MCP tool call aborted"));
					rpcAbort.abort();
				};
				signal.addEventListener("abort", onAbort, { once: true });
			}
			task(rpcAbort.signal).then(resolvePromise, rejectPromise);
		});
	} finally {
		if (timer) clearTimeout(timer);
		if (signal && onAbort) signal.removeEventListener("abort", onAbort);
	}
}
//#endregion
export { resultToString as a, McpOAuthProvider as c, normalizeMcpServers as i, createMemoryMcpCredentialStore as l, connectMcpServers as n, createTolerantClient as o, normalizeMcpBlocks as r, sseToJsonFetchIfNeeded as s, attachStderrWarnPump as t, hasAuthorizationHeader as u };

//# sourceMappingURL=mcp-tevNihk_.js.map